DEV Community

meta-closure
meta-closure

Posted on

Decorator Contract Programming: Making AI-Generated Code Bulletproof

Part 2 of 5: Building a Zero-Trust Architecture that AI tools can't break

Welcome back to our AI security series! In Part 1, we saw how AI-generated code can create critical security vulnerabilities. Today, we're going to fix that with a revolutionary approach: Decorator Contract Programming.

๐ŸŽฏ The Core Problem

AI tools generate code that works, but they don't understand:

  • Layer responsibilities (where should auth checks go?)
  • Cross-cutting concerns (logging, validation, rate limiting)
  • Business rules (who can access what?)
  • Security boundaries (trust nothing, verify everything)

Traditional solutions fail because they expect AI to be smart about security. Spoiler alert: It's not.

๐Ÿ’ก The Breakthrough: Contracts as Code

Instead of hoping AI gets security right, we make security violations impossible to deploy.

The secret? Declarative contracts that tell the system exactly what must be true before, during, and after each function call.

@contract({
  requires: [auth('user'), validates(schema), owns('userId')],
  ensures: [auditLog('action'), returns(outputSchema)]
})
async function dangerousFunction(input, context) {
  // Even if AI writes terrible code here,
  // the contract catches violations
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ—๏ธ Zero-Trust Layer Architecture

Here's the architecture that changes everything:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Presentation    โ”‚ โ† Basic auth (login status, session)
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Action          โ”‚ โ† Permissions (roles, rate limits, input validation)
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Business        โ”‚ โ† Ownership (resource access, business rules)
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Data            โ”‚ โ† Final defense (query-level security, audit)
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
Enter fullscreen mode Exit fullscreen mode

Key insight: Even if AI misses security in one layer, the other layers catch it.

๐Ÿ”ง Building Your First Contract

Let's start simple. Here's how to create a basic contract system:

Step 1: Define Contract Types

type ContractCondition = (input: any, context: any) => boolean | Promise<boolean>;
type ContractValidator = (input: unknown) => any;

interface ContractOptions {
  requires?: Array<ContractCondition | ContractValidator>;
  ensures?: Array<(output: any, input: any, context: any) => boolean>;
  layer?: 'presentation' | 'action' | 'business' | 'data';
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the Contract Decorator

function contract(options: ContractOptions) {
  return function(target: any, propertyName: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = async function(...args: any[]) {
      const [input, context] = args;

      try {
        // Pre-conditions (requires)
        let validatedInput = input;
        for (const condition of options.requires || []) {
          if (typeof condition === 'function' && condition.length === 1) {
            // Validator - transforms input
            validatedInput = condition(validatedInput);
          } else {
            // Condition check
            await condition(validatedInput, context);
          }
        }

        // Execute original method
        const result = await originalMethod.call(this, validatedInput, context);

        // Post-conditions (ensures)
        for (const condition of options.ensures || []) {
          await condition(result, validatedInput, context);
        }

        return result;

      } catch (error) {
        throw new ContractViolationError(
          `${target.constructor.name}.${propertyName}`,
          options.layer || 'unknown',
          error
        );
      }
    };
  };
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Build Contract Conditions

// Authentication check
function auth(requiredRole?: string) {
  return async (input: any, context: AuthContext): Promise<boolean> => {
    if (!context.user) {
      throw new ContractError('AUTHENTICATION_REQUIRED', 'User must be logged in');
    }

    if (!context.session || new Date(context.session.expiresAt) < new Date()) {
      throw new ContractError('SESSION_EXPIRED', 'Session has expired');
    }

    if (requiredRole && !context.user.roles.includes(requiredRole)) {
      throw new ContractError('INSUFFICIENT_ROLE', 
        `Required role: ${requiredRole}`);
    }

    return true;
  };
}

// Ownership validation
function owns(resourceIdField: string) {
  return async (input: any, context: AuthContext): Promise<boolean> => {
    const resourceId = input[resourceIdField];
    if (!resourceId) {
      throw new ContractError('MISSING_RESOURCE_ID', 
        `Field ${resourceIdField} is required`);
    }

    // Admin users can access all resources
    if (context.user.roles.includes('admin')) {
      return true;
    }

    // Check resource ownership
    const resource = await getResourceById(resourceId);
    if (!resource || resource.userId !== context.user.id) {
      throw new ContractError('OWNERSHIP_DENIED', 
        `User ${context.user.id} does not own resource ${resourceId}`);
    }

    return true;
  };
}

// Input validation
function validates(schema: z.ZodSchema) {
  return (input: unknown): any => {
    try {
      return schema.parse(input);
    } catch (error) {
      if (error instanceof z.ZodError) {
        const messages = error.issues.map(issue => 
          `${issue.path.join('.')}: ${issue.message}`
        ).join(', ');
        throw new ContractError('VALIDATION_FAILED', 
          `Input validation failed: ${messages}`);
      }
      throw error;
    }
  };
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Handle Contract Violations

class ContractError extends Error {
  constructor(public type: string, message: string) {
    super(message);
    this.name = 'ContractError';
  }
}

class ContractViolationError extends Error {
  constructor(
    public contractName: string,
    public layer: string,
    public originalError: any
  ) {
    super(`Contract violation in ${layer}.${contractName}: ${originalError.message}`);
    this.name = 'ContractViolationError';
  }

  getAppropriateResponse() {
    switch (this.layer) {
      case 'presentation':
        return { redirect: '/login', error: 'Authentication required' };
      case 'action':
        return { success: false, error: this.originalError.message };
      case 'business':
        return { success: false, error: 'Permission denied' };
      case 'data':
        return { success: false, error: 'Operation failed' };
      default:
        return { success: false, error: 'An error occurred' };
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŽช See It In Action

Now let's transform that dangerous AI-generated function:

Before (AI-generated, vulnerable):

export async function updateUserProfile(formData: FormData) {
  const userId = formData.get('userId') as string;
  const email = formData.get('email') as string;
  const name = formData.get('name') as string;

  const updatedUser = await db.user.update({
    where: { id: userId },
    data: { email, name }
  });

  return { success: true, user: updatedUser };
}
Enter fullscreen mode Exit fullscreen mode

After (contract-protected):

const userUpdateSchema = z.object({
  userId: z.string().uuid(),
  email: z.string().email().optional(),
  name: z.string().min(1).max(100).optional()
});

@contract({
  requires: [
    auth('user'),                    // Must be authenticated
    validates(userUpdateSchema),     // Input validation
    owns('userId')                   // Must own the profile
  ],
  ensures: [
    auditLog('profile_update')       // Log the action
  ],
  layer: 'action'
})
export async function updateUserProfile(
  input: UserUpdateInput, 
  context: AuthContext
): Promise<User> {
  return userService.updateUser(input, context);
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿš€ The Magic

What happens when someone tries to hack this?

  1. No auth token? โ†’ Contract throws AUTHENTICATION_REQUIRED
  2. Invalid email format? โ†’ Contract throws VALIDATION_FAILED
  3. Trying to update someone else's profile? โ†’ Contract throws OWNERSHIP_DENIED
  4. Everything succeeds? โ†’ Action is automatically logged for audit

The AI doesn't need to know about security. The contract handles everything.

๐ŸŽฏ Why This Works With AI

AI tools love this approach because:

  1. Declarative: Clear requirements, no ambiguity
  2. Copy-pasteable: Perfect patterns to replicate
  3. Composable: Mix and match contract pieces
  4. Fail-safe: Violations are caught automatically

Instead of teaching AI complex security concepts, we give it simple, repeatable patterns.

๐Ÿ”ฎ Coming Up

In Part 3, we'll build a complete Next.js user management system using these contracts. You'll see how to:

  • Structure your layers properly
  • Handle different types of operations
  • Integrate with Next.js Server Actions
  • Make it production-ready

Questions or ideas? Drop them in the comments! I love seeing how you adapt these patterns.


Don't miss Part 3: We're building a real-world system that AI tools can contribute to safely.

Top comments (0)