# Security Auditor — Worked Examples

Externalized from the agent definition per the few-shot-examples rule (#1587).

### Example 1: SQL Injection Vulnerability (Simple)

**Input:**
Review the user authentication endpoint for security vulnerabilities.

**Output:**
```markdown
# Security Finding: SQL Injection Vulnerability

**Severity**: CRITICAL
**OWASP Category**: A03:2021 - Injection
**Location**: `src/auth/login.ts:42`

## Current Code
```typescript
async function authenticateUser(email: string, password: string) {
  const query = `SELECT * FROM users WHERE email = '${email}' AND password = '${password}'`;
  const result = await db.query(query);
  return result.rows[0];
}
```

## Vulnerability
Direct string interpolation of user input into SQL query allows SQL injection attacks. An attacker can bypass authentication using inputs like:
- Email: `admin@example.com' OR '1'='1`
- Password: `anything`

## Remediation
```typescript
async function authenticateUser(email: string, password: string) {
  // Use parameterized queries
  const query = 'SELECT * FROM users WHERE email = $1';
  const result = await db.query(query, [email]);

  if (!result.rows[0]) {
    return null;
  }

  // Verify password using bcrypt (never store plaintext passwords)
  const isValid = await bcrypt.compare(password, result.rows[0].password_hash);
  return isValid ? result.rows[0] : null;
}
```

## Additional Recommendations
- Implement password hashing with bcrypt (cost factor 12+)
- Add rate limiting to prevent brute force attacks
- Log failed authentication attempts
- Use prepared statements for all database queries
```

**Why This Is Good:**
- Specific file and line number provided
- OWASP category mapped for compliance tracking
- Severity rating helps prioritization
- Complete remediation code provided, not just description
- Includes additional security hardening recommendations beyond the immediate fix

---

### Example 2: STRIDE Threat Model for Authentication API (Moderate)

**Input:**
Create a STRIDE threat model for our new authentication API that handles user registration, login, password reset, and token refresh.

**Output:**
```markdown
# Threat Model: Authentication API

## Assets

| Asset | Sensitivity | Description |
|-------|-------------|-------------|
| User Credentials | Critical | Email addresses and password hashes |
| Session Tokens | Critical | JWT access and refresh tokens |
| Password Reset Tokens | High | Time-limited single-use tokens |
| User Profile Data | Medium | Names, preferences, non-sensitive metadata |

## Trust Boundaries

1. **Client → API Gateway**: Public internet, untrusted
2. **API Gateway → Auth Service**: Internal network, authenticated
3. **Auth Service → Database**: Internal network, service account

## STRIDE Analysis

### Spoofing Identity

| Threat | Risk | Impact | Mitigation |
|--------|------|--------|------------|
| Credential stuffing attacks | High | Account takeover | - Rate limiting (5 attempts/15min)<br>- CAPTCHA after 3 failures<br>- Check against breach databases<br>- Multi-factor authentication |
| Session token theft | High | Unauthorized access | - HTTPOnly cookies<br>- Secure flag (HTTPS only)<br>- Short token expiry (15min)<br>- Token rotation on refresh |
| Email enumeration | Medium | Account discovery | - Generic error messages<br>- Same response time for valid/invalid emails |

### Tampering with Data

| Threat | Risk | Impact | Mitigation |
|--------|------|--------|------------|
| JWT token manipulation | High | Privilege escalation | - Asymmetric signing (RS256)<br>- Verify signature on every request<br>- Include user role in token claims<br>- Short expiry time |
| Password reset token manipulation | High | Account takeover | - Cryptographically secure random tokens<br>- Single-use tokens<br>- Time-limited (1 hour expiry)<br>- Invalidate on use |
| Parameter tampering | Medium | Authorization bypass | - Server-side validation of all inputs<br>- Validate user ID against session<br>- Enforce authorization at API layer |

### Repudiation

| Threat | Risk | Impact | Mitigation |
|--------|------|--------|------------|
| Deny authentication activity | Low | Audit gaps | - Comprehensive audit logging<br>- Log successful/failed logins<br>- Include IP, timestamp, user agent<br>- Tamper-proof log storage |

### Information Disclosure

| Threat | Risk | Impact | Mitigation |
|--------|------|--------|------------|
| Credential leakage in logs | Critical | Mass compromise | - Never log passwords or tokens<br>- Redact sensitive fields<br>- Encrypt logs at rest |
| Timing attacks | Medium | Email enumeration | - Constant-time string comparison<br>- Same processing time for valid/invalid |
| Error message leakage | Medium | System information | - Generic error messages<br>- Detailed errors only in secure logs |

### Denial of Service

| Threat | Risk | Impact | Mitigation |
|--------|------|--------|------------|
| Login endpoint flooding | High | Service unavailable | - Rate limiting per IP and per account<br>- CAPTCHA on repeated failures<br>- Auto-ban on excessive requests |
| Token refresh abuse | Medium | Resource exhaustion | - Limit refresh token lifetime (7 days)<br>- Revoke refresh tokens on logout<br>- Track and limit concurrent sessions |

### Elevation of Privilege

| Threat | Risk | Impact | Mitigation |
|--------|------|--------|------------|
| Role manipulation via token | Critical | Admin access | - Server-side role verification<br>- Don't trust token claims alone<br>- Re-validate role on privileged operations |
| Password reset to any account | Critical | Account takeover | - Email verification required<br>- Send reset link only to registered email<br>- Invalidate after password change |

## Security Controls Implementation

### 1. Authentication
- Bcrypt password hashing (cost factor 12)
- JWT with RS256 algorithm
- Multi-factor authentication (TOTP)
- Rate limiting: 5 attempts per 15 minutes

### 2. Authorization
- Role-based access control (RBAC)
- Token claims: {sub, role, iat, exp, iss, aud}
- Access token expiry: 15 minutes
- Refresh token expiry: 7 days

### 3. Monitoring
- Real-time alerts on:
  - Failed login attempts >5 in 5 minutes
  - Token verification failures
  - Password reset requests >3 per hour
  - Geographic anomalies (VPN detection)
```

**Why This Is Good:**
- Uses systematic STRIDE framework for comprehensive coverage
- Risk levels quantified (Critical/High/Medium/Low) for prioritization
- Specific, actionable mitigations for each threat
- Maps assets to sensitivity levels for compliance
- Documents trust boundaries clearly
- Includes implementation checklist with concrete parameters

---

### Example 3: JWT Implementation Security Review (Complex)

**Input:**
Perform a comprehensive security review of our JWT authentication implementation across the entire auth flow.

**Output:**
```markdown
# Security Review: JWT Authentication Implementation

**Review Scope**: End-to-end authentication flow
**Files Reviewed**: `auth/jwt.ts`, `middleware/auth.ts`, `routes/login.ts`, `routes/refresh.ts`
**OWASP Categories**: A02, A07, A08

## Executive Summary

Reviewed JWT implementation reveals 3 CRITICAL and 2 HIGH severity security issues requiring immediate remediation. The authentication flow lacks algorithm validation, uses excessive token expiry times, and stores secrets insecurely. Estimated remediation time: 8-12 hours.

---

## CRITICAL Findings

### 1. Algorithm Confusion Vulnerability (CVE-2015-9235)

**Severity**: CRITICAL
**OWASP**: A02:2021 - Cryptographic Failures
**Location**: `auth/jwt.ts:67`

**Current Code:**
```typescript
function verifyToken(token: string) {
  return jwt.verify(token, JWT_SECRET); // No algorithm specified!
}
```

**Vulnerability:**
The `jwt.verify()` call doesn't specify allowed algorithms. An attacker can change the JWT header from `HS256` to `none` and remove the signature, bypassing authentication entirely.

**Exploit Example:**
```json
// Attacker crafts token with "alg": "none"
{
  "alg": "none",
  "typ": "JWT"
}
{
  "sub": "admin",
  "role": "admin",
  "iat": 1640000000
}
```

**Remediation:**
```typescript
function verifyToken(token: string) {
  return jwt.verify(token, JWT_SECRET, {
    algorithms: ['HS256'], // REQUIRED: Whitelist allowed algorithms
    issuer: 'your-app',
    audience: 'your-app-users'
  });
}
```

**Priority**: Fix immediately before next deployment

---

### 2. Hardcoded JWT Secret

**Severity**: CRITICAL
**OWASP**: A05:2021 - Security Misconfiguration
**Location**: `auth/jwt.ts:12`

**Current Code:**
```typescript
const JWT_SECRET = 'super-secret-key-12345'; // NEVER DO THIS
```

**Vulnerability:**
Hardcoded secret in source code means:
- Secret is in version control history (even if later removed)
- All environments share same secret (dev/staging/prod)
- Secret exposed in code reviews, CI/CD logs
- Cannot rotate secret without code deployment

**Remediation:**
```typescript
// 1. Load from environment variable
const JWT_SECRET = process.env.JWT_SECRET;

// 2. Validate at startup
if (!JWT_SECRET || JWT_SECRET.length < 32) {
  throw new Error('JWT_SECRET must be set and at least 32 characters');
}

// 3. Use different secrets per environment
// .env.production:
// JWT_SECRET=<generated-with-openssl-rand-hex-64>

// 4. Rotate secret using key versioning
const JWT_SECRETS = {
  current: process.env.JWT_SECRET_V2,
  previous: process.env.JWT_SECRET_V1 // Accept old tokens during rotation
};
```

**Immediate Actions:**
1. Generate new secret: `openssl rand -hex 64`
2. Store in environment variable management system (AWS Secrets Manager, Azure Key Vault)
3. Deploy updated code
4. Invalidate all existing tokens (force re-login)
5. Remove secret from git history: `git filter-branch` or BFG Repo-Cleaner

---

### 3. Excessive Token Expiry

**Severity**: CRITICAL
**OWASP**: A07:2021 - Identification and Authentication Failures
**Location**: `auth/jwt.ts:45`

**Current Code:**
```typescript
const token = jwt.sign(payload, JWT_SECRET, {
  expiresIn: '30d' // 30 DAYS - far too long!
});
```

**Vulnerability:**
- If token is stolen, attacker has 30 days of access
- No mechanism to invalidate compromised tokens
- Violates principle of least privilege (time)

**Recommended Token Lifetimes:**
```typescript
// Access token: Short-lived, sent with every request
const ACCESS_TOKEN_EXPIRY = '15m'; // 15 minutes

// Refresh token: Longer-lived, used only to get new access token
const REFRESH_TOKEN_EXPIRY = '7d'; // 7 days

function generateAccessToken(userId: string, role: string) {
  return jwt.sign(
    { sub: userId, role: role, type: 'access' },
    JWT_SECRET,
    {
      algorithm: 'HS256',
      expiresIn: ACCESS_TOKEN_EXPIRY,
      issuer: 'your-app',
      audience: 'your-app-users'
    }
  );
}

function generateRefreshToken(userId: string) {
  const refreshToken = jwt.sign(
    { sub: userId, type: 'refresh' },
    JWT_REFRESH_SECRET, // Different secret!
    {
      algorithm: 'HS256',
      expiresIn: REFRESH_TOKEN_EXPIRY,
      issuer: 'your-app',
      audience: 'your-app-users'
    }
  );

  // Store refresh token hash in database for revocation
  await storeRefreshToken(userId, hashToken(refreshToken));

  return refreshToken;
}
```

**Token Refresh Endpoint:**
```typescript
async function refreshAccessToken(refreshToken: string) {
  // 1. Verify refresh token
  const decoded = jwt.verify(refreshToken, JWT_REFRESH_SECRET, {
    algorithms: ['HS256']
  });

  // 2. Check not revoked (database lookup)
  const isValid = await isRefreshTokenValid(decoded.sub, hashToken(refreshToken));
  if (!isValid) {
    throw new Error('Refresh token revoked');
  }

  // 3. Issue new access token
  return generateAccessToken(decoded.sub, decoded.role);
}
```

---

## HIGH Severity Findings

### 4. Missing Token Type Validation

**Severity**: HIGH
**Location**: `middleware/auth.ts:28`

**Current Code:**
```typescript
function authenticateToken(req, res, next) {
  const token = req.headers['authorization']?.split(' ')[1];
  const decoded = verifyToken(token); // Accepts ANY valid JWT
  req.user = decoded;
  next();
}
```

**Vulnerability:**
The middleware doesn't check token type. An attacker could use a refresh token (which should only be used on `/auth/refresh` endpoint) to access protected resources.

**Remediation:**
```typescript
function authenticateToken(req, res, next) {
  const token = req.headers['authorization']?.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'Authentication required' });
  }

  try {
    const decoded = verifyToken(token);

    // Validate token type
    if (decoded.type !== 'access') {
      return res.status(403).json({ error: 'Invalid token type' });
    }

    req.user = decoded;
    next();
  } catch (error) {
    return res.status(403).json({ error: 'Invalid or expired token' });
  }
}
```

---

### 5. Timing Attack on Token Comparison

**Severity**: HIGH
**OWASP**: A02:2021 - Cryptographic Failures
**Location**: `routes/refresh.ts:42`

**Current Code:**
```typescript
if (storedToken === providedToken) { // Vulnerable to timing attack
  return true;
}
```

**Vulnerability:**
String comparison using `===` is not constant-time. An attacker can measure response times to determine correct token bytes one at a time.

**Remediation:**
```typescript
const crypto = require('crypto');

function constantTimeCompare(a: string, b: string): boolean {
  if (a.length !== b.length) {
    return false;
  }

  return crypto.timingSafeEqual(
    Buffer.from(a),
    Buffer.from(b)
  );
}

// Usage
if (constantTimeCompare(storedToken, providedToken)) {
  return true;
}
```

---

## Prioritized Remediation Plan

| Priority | Finding | Estimated Time | Blocking? |
|----------|---------|----------------|-----------|
| 1 | Hardcoded JWT Secret | 2 hours | YES - Blocks deployment |
| 2 | Algorithm Confusion | 1 hour | YES - Blocks deployment |
| 3 | Excessive Token Expiry | 4 hours | YES - Blocks deployment |
| 4 | Missing Token Type Validation | 1 hour | NO - Can deploy with monitoring |
| 5 | Timing Attack | 30 minutes | NO - Low exploitability |

**Total Remediation Time**: 8.5 hours

**Deployment Checklist:**
- [ ] Generate new JWT secrets for all environments
- [ ] Update code with all CRITICAL fixes
- [ ] Force logout all existing sessions
- [ ] Deploy to staging and run security tests
- [ ] Deploy to production during maintenance window
- [ ] Monitor for authentication errors (24-48 hours)

---

## Testing Requirements

Add these security tests before deployment:

```typescript
describe('JWT Security', () => {
  test('rejects tokens with "none" algorithm', () => {
    const maliciousToken = createTokenWithAlgorithm('none');
    expect(() => verifyToken(maliciousToken)).toThrow('invalid algorithm');
  });

  test('rejects refresh token on protected endpoints', async () => {
    const refreshToken = generateRefreshToken('user123');
    const response = await request(app)
      .get('/api/protected')
      .set('Authorization', `Bearer ${refreshToken}`);
    expect(response.status).toBe(403);
  });

  test('access token expires after 15 minutes', async () => {
    const token = generateAccessToken('user123', 'user');
    await sleep(16 * 60 * 1000); // 16 minutes
    expect(() => verifyToken(token)).toThrow('jwt expired');
  });

  test('revoked refresh token cannot be used', async () => {
    const refreshToken = generateRefreshToken('user123');
    await revokeRefreshToken('user123', refreshToken);
    await expect(refreshAccessToken(refreshToken))
      .rejects.toThrow('Refresh token revoked');
  });
});
```

---

## Additional Recommendations

### Implement Token Revocation
- Add `jti` (JWT ID) claim to all tokens
- Store active token IDs in Redis with TTL matching token expiry
- Check token ID on every request
- Revoke tokens on logout, password change, or suspicious activity

### Add Security Monitoring
- Alert on:
  - Failed token verification >10/minute
  - Token reuse attempts
  - Tokens with invalid signatures
  - Geographic anomalies (IP suddenly changes country)

### Consider Upgrading to Asymmetric Signing
- Use RS256 instead of HS256
- Private key for signing (API server only)
- Public key for verification (can distribute to microservices)
- Easier key rotation and better security properties
```

**Why This Is Good:**
- Covers entire authentication flow, not just isolated issues
- Multiple OWASP categories addressed systematically
- Severity ratings with clear blocking vs. non-blocking distinctions
- Complete remediation code for every finding, not just descriptions
- Prioritized remediation plan with time estimates for project planning
- Includes test cases to prevent regression
- Executive summary for management visibility
- Exploit examples demonstrate real-world impact
- Deployment checklist ensures safe rollout

---

## Secure Implementation Reference Patterns

Illustrative "what correct secure code looks like" reference implementations the Security Auditor uses when proposing remediations. These are sample patterns, not gated checklist items — the OWASP Top 10 checklist and all gates live in the agent definition.

### Secure Authentication Patterns

#### JWT Implementation

```javascript
// Secure JWT configuration
const jwt = require('jsonwebtoken');
const crypto = require('crypto');

// Use strong secret (256 bits minimum)
const JWT_SECRET = process.env.JWT_SECRET; // Never hardcode!
const JWT_EXPIRY = '1h'; // Short-lived tokens

// Generate token
function generateToken(userId, role) {
  return jwt.sign(
    {
      sub: userId,
      role: role,
      iat: Math.floor(Date.now() / 1000)
    },
    JWT_SECRET,
    {
      algorithm: 'HS256',
      expiresIn: JWT_EXPIRY,
      issuer: 'your-app',
      audience: 'your-app-users'
    }
  );
}

// Verify token
function verifyToken(token) {
  try {
    return jwt.verify(token, JWT_SECRET, {
      algorithms: ['HS256'],
      issuer: 'your-app',
      audience: 'your-app-users'
    });
  } catch (error) {
    if (error.name === 'TokenExpiredError') {
      throw new Error('Token expired');
    }
    throw new Error('Invalid token');
  }
}

// Middleware for protected routes
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

  if (!token) {
    return res.status(401).json({ error: 'Authentication required' });
  }

  try {
    const decoded = verifyToken(token);
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(403).json({ error: error.message });
  }
}
```

#### OAuth2 Implementation

```javascript
// OAuth2 authorization code flow
const oauth2 = require('simple-oauth2');

const oauth2Config = {
  client: {
    id: process.env.OAUTH_CLIENT_ID,
    secret: process.env.OAUTH_CLIENT_SECRET
  },
  auth: {
    tokenHost: 'https://auth.provider.com',
    authorizePath: '/oauth/authorize',
    tokenPath: '/oauth/token'
  }
};

const oauth2Client = oauth2.AuthorizationCode(oauth2Config);

// Authorization URL
function getAuthorizationUrl() {
  return oauth2Client.authorizeURL({
    redirect_uri: 'https://your-app.com/callback',
    scope: 'read:user read:email',
    state: crypto.randomBytes(16).toString('hex') // CSRF protection
  });
}

// Handle callback
async function handleCallback(code, state) {
  // Verify state to prevent CSRF
  if (!verifyState(state)) {
    throw new Error('Invalid state parameter');
  }

  const tokenParams = {
    code: code,
    redirect_uri: 'https://your-app.com/callback'
  };

  try {
    const result = await oauth2Client.getToken(tokenParams);
    return result.token;
  } catch (error) {
    throw new Error('Failed to obtain access token');
  }
}
```

### Input Validation and Sanitization

```javascript
// Input validation using validator library
const validator = require('validator');

function validateUserInput(input) {
  const errors = {};

  // Email validation
  if (!validator.isEmail(input.email)) {
    errors.email = 'Invalid email format';
  }

  // URL validation
  if (input.website && !validator.isURL(input.website, {
    protocols: ['http', 'https'],
    require_protocol: true
  })) {
    errors.website = 'Invalid URL format';
  }

  // Strong password validation
  const passwordOptions = {
    minLength: 12,
    minLowercase: 1,
    minUppercase: 1,
    minNumbers: 1,
    minSymbols: 1
  };
  if (!validator.isStrongPassword(input.password, passwordOptions)) {
    errors.password = 'Password does not meet strength requirements';
  }

  // SQL injection prevention (use parameterized queries)
  // Never concatenate user input into SQL
  // WRONG: `SELECT * FROM users WHERE id = ${userId}`
  // RIGHT: Use parameterized query (see below)

  // XSS prevention (sanitize HTML)
  if (input.bio) {
    input.bio = validator.escape(input.bio);
  }

  return {
    isValid: Object.keys(errors).length === 0,
    errors: errors,
    sanitized: input
  };
}

// SQL injection prevention with parameterized queries
async function getUserById(userId) {
  // PostgreSQL parameterized query
  const result = await db.query(
    'SELECT * FROM users WHERE id = $1',
    [userId] // Parameters passed separately
  );
  return result.rows[0];
}

// ORM example (Sequelize)
async function getUserByEmail(email) {
  return await User.findOne({
    where: { email: email } // ORM handles parameterization
  });
}
```

### Security Headers Configuration

```javascript
// Express.js security headers middleware
const helmet = require('helmet');

app.use(helmet({
  // Content Security Policy
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'", "trusted-cdn.com"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'", "https://api.example.com"],
      fontSrc: ["'self'", "https://fonts.gstatic.com"],
      objectSrc: ["'none'"],
      mediaSrc: ["'self'"],
      frameSrc: ["'none'"]
    }
  },
  // HTTP Strict Transport Security
  hsts: {
    maxAge: 31536000, // 1 year
    includeSubDomains: true,
    preload: true
  },
  // X-Frame-Options
  frameguard: {
    action: 'deny'
  },
  // X-Content-Type-Options
  noSniff: true,
  // Referrer-Policy
  referrerPolicy: {
    policy: 'strict-origin-when-cross-origin'
  }
}));

// CORS configuration
const cors = require('cors');

app.use(cors({
  origin: ['https://your-app.com', 'https://admin.your-app.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400 // 24 hours
}));
```

### Encryption Implementation

```javascript
const crypto = require('crypto');

// Encrypt data at rest (AES-256-GCM)
function encrypt(plaintext, key) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);

  let encrypted = cipher.update(plaintext, 'utf8', 'hex');
  encrypted += cipher.final('hex');

  const authTag = cipher.getAuthTag();

  return {
    iv: iv.toString('hex'),
    encrypted: encrypted,
    authTag: authTag.toString('hex')
  };
}

function decrypt(encrypted, key, iv, authTag) {
  const decipher = crypto.createDecipheriv(
    'aes-256-gcm',
    key,
    Buffer.from(iv, 'hex')
  );

  decipher.setAuthTag(Buffer.from(authTag, 'hex'));

  let decrypted = decipher.update(encrypted, 'hex', 'utf8');
  decrypted += decipher.final('utf8');

  return decrypted;
}

// Password hashing (bcrypt)
const bcrypt = require('bcrypt');

async function hashPassword(password) {
  const saltRounds = 12; // Cost factor
  return await bcrypt.hash(password, saltRounds);
}

async function verifyPassword(password, hash) {
  return await bcrypt.compare(password, hash);
}

// Key derivation (PBKDF2)
function deriveKey(password, salt) {
  return crypto.pbkdf2Sync(
    password,
    salt,
    100000, // iterations
    32, // key length
    'sha256'
  );
}
```

### Security Testing

```javascript
// Security test examples (Jest)
describe('Authentication Security', () => {
  test('prevents SQL injection in login', async () => {
    const maliciousInput = "admin' OR '1'='1";
    const result = await login(maliciousInput, 'password');
    expect(result).toBeNull();
  });

  test('prevents XSS in user input', async () => {
    const maliciousInput = '<script>alert("XSS")</script>';
    const sanitized = sanitizeInput(maliciousInput);
    expect(sanitized).not.toContain('<script>');
  });

  test('enforces rate limiting on login', async () => {
    const attempts = [];
    for (let i = 0; i < 10; i++) {
      attempts.push(login('user@example.com', 'wrong'));
    }
    await Promise.all(attempts);

    // 11th attempt should be rate limited
    await expect(login('user@example.com', 'wrong'))
      .rejects.toThrow('Too many login attempts');
  });

  test('JWT tokens expire correctly', async () => {
    const token = generateToken('user123', 'user', '1s');
    await new Promise(resolve => setTimeout(resolve, 2000));
    expect(() => verifyToken(token)).toThrow('Token expired');
  });
});
```

### Token and Secret Management Security

**CRITICAL**: All API tokens, secrets, and credentials MUST be handled securely.

#### Token Storage Best Practices

```javascript
// NEVER hardcode tokens
// BAD:
const GITEA_TOKEN = "abc123def456...";

// GOOD: Load from environment
const GITEA_TOKEN = process.env.GITEA_TOKEN;
if (!GITEA_TOKEN) {
  throw new Error('GITEA_TOKEN environment variable not set');
}
```

#### File-Based Token Loading (Development)

```bash
# Secure token loading pattern for scripts
bash <<'EOF'
TOKEN=$(cat ~/.config/gitea/token)
curl -s -H "Authorization: token ${TOKEN}" \
  "https://git.integrolabs.net/api/v1/user"
EOF
```

#### Token Security Checklist

- [ ] **Never hardcode tokens** in any tracked file
- [ ] **Load from environment variables** (CI/CD) or secure files (development)
- [ ] **Use heredoc pattern** for multi-line shell operations with tokens
- [ ] **Enforce file permissions** mode 600 for token files
- [ ] **Never log token values** in application logs or console output
- [ ] **Rotate tokens regularly** and after any potential exposure
- [ ] **Use different tokens** for different privilege levels (admin vs standard)

#### Example: Secure API Authentication

```bash
# Single API call - inline token load
curl -s -H "Authorization: token $(cat ~/.config/gitea/token)" \
  "https://git.integrolabs.net/api/v1/repos/owner/repo/issues"

# Multiple API calls - heredoc pattern
bash <<'EOF'
TOKEN=$(cat ~/.config/gitea/token)

REPOS=$(curl -s -H "Authorization: token ${TOKEN}" \
  "https://git.integrolabs.net/api/v1/users/roctinam/repos")

ISSUES=$(curl -s -H "Authorization: token ${TOKEN}" \
  "https://git.integrolabs.net/api/v1/repos/roctinam/sysops/issues")

echo "Repositories found: $(echo "${REPOS}" | jq length)"
echo "Issues found: $(echo "${ISSUES}" | jq length)"
EOF
```

**Security Notes**:
- Token loaded within heredoc scope only
- Not visible in shell history
- Not exposed in process list
- Automatically cleaned up after execution
