Security Auditor Examples

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

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:

# Security Finding: SQL Injection Vulnerability

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

## Current Code

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: `[email protected]' OR '1'='1`
- Password: `anything`

## Remediation

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:

# 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:

# 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:**

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:**

// Attacker crafts token with "alg": "none"

{

"alg": "none",

"typ": "JWT"

}

{

"sub": "admin",

"role": "admin",

"iat": 1640000000

}


**Remediation:**

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:**

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:**

// 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:**

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:**

// 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:**

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:**

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:**

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:**

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:**

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:

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

// 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

// 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

// 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

// 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

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

// 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('[email protected]', 'wrong'));
    }
    await Promise.all(attempts);

    // 11th attempt should be rate limited
    await expect(login('[email protected]', '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

// 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)

# 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

# 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