Skip to main content

Scenario

A developer used GitHub Copilot to generate a Node.js authentication module. The AI-generated code hardcoded credentials that were committed and pushed before anyone noticed.
// auth/config.js โ€” generated by AI assistant
const config = {
  jwtSecret: 'supersecret',           // CRITICAL: predictable JWT secret
  awsKey: 'AKIAIOSFODNN7EXAMPLE',     // CRITICAL: AWS access key
  awsSecret: 'wJalrXUtnFEMI/K7...',  // HIGH: hardcoded AWS secret
  dbPassword: 'admin123',             // HIGH: hardcoded password
  sessionDuration: '365d',            // HIGH: year-long sessions
};

// Store JWT in localStorage after login
localStorage.setItem('auth_jwt', generateToken(user));  // HIGH: XSS exposure
Six vulnerabilities in nine lines. All introduced by an AI assistant following common patterns from its training data.

Detection

zenveil scan repo .
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚  ZenVeil Security Scan                                               โ”‚
โ”‚  Target: /home/user/node-auth  ยท  Scanners: secrets, supply_chain   โ”‚
โ”‚  Duration: 1.2s                                                      โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ID       โ”‚ Severity โ”‚ Scanner  โ”‚ Title                                  โ”‚ Location                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ZG-A1B2  โ”‚ CRITICAL โ”‚ secrets  โ”‚ AWS access key                         โ”‚ auth/config.js:4         โ”‚
โ”‚ ZG-C3D4  โ”‚ CRITICAL โ”‚ secrets  โ”‚ Predictable JWT signing secret         โ”‚ auth/config.js:3         โ”‚
โ”‚ ZG-E5F6  โ”‚ HIGH     โ”‚ secrets  โ”‚ Hardcoded API key assignment           โ”‚ auth/config.js:5         โ”‚
โ”‚ ZG-G7H8  โ”‚ HIGH     โ”‚ secrets  โ”‚ Hardcoded password assignment          โ”‚ auth/config.js:6         โ”‚
โ”‚ ZG-I9J0  โ”‚ HIGH     โ”‚ secrets  โ”‚ Token stored in browser storage        โ”‚ auth/login.js:47         โ”‚
โ”‚ ZG-K1L2  โ”‚ HIGH     โ”‚ secrets  โ”‚ Long-lived session token               โ”‚ auth/config.js:7         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

6 finding(s) ยท CRITICAL: 2 ยท HIGH: 4

Exiting with code 1 (CRITICAL/HIGH findings present)

Triage

zenveil triage
Triaging 6 finding(s)โ€ฆ

PRIORITY ORDER (highest risk first)
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

1. ZG-A1B2 โ€” AWS access key [CRITICAL]
   โš  IMMEDIATE ACTION REQUIRED
   An active AWS access key in source code is a live credential exposure.
   Automated bots scan GitHub continuously for this exact pattern (AKIA prefix).
   If this was ever pushed to a remote repository, assume it's compromised.

   Effort: 15 minutes
   Action:
   1. Revoke at https://console.aws.amazon.com/iam โ†’ Users โ†’ Security credentials
   2. git rm --cached auth/config.js
   3. git filter-branch or BFG to purge from history if committed
   4. Replace with: process.env.AWS_ACCESS_KEY_ID

2. ZG-C3D4 โ€” Predictable JWT signing secret [CRITICAL]
   Any JWT signed with 'supersecret' can be forged by an attacker.
   Run: node -e "require('jsonwebtoken').sign({admin:true}, 'supersecret')"
   โ†’ Any attacker can mint admin tokens.

   Effort: 30 minutes (must invalidate all existing sessions)
   Action: Replace with crypto.randomBytes(64).toString('hex'), store in secrets manager

[... continues for all 6 findings ...]

AI explanation

zenveil explain ZG-A1B2
Explaining ZG-A1B2: AWS access key

โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”

What this is:
An AWS access key starting with 'AKIA' was found committed at
auth/config.js:4. This is a live AWS credential โ€” the AKIA prefix
is a signature that AWS uses for long-lived access keys.

Why it's critical:
AWS access keys have no expiry by default and grant API-level access
to every AWS service the associated IAM user can access. Within minutes
of being committed to a public or semi-public repository, automated
scanners (GitGuardian, TruffleHog, and numerous threat actor tools)
will find and test this key.

Real-world consequence:
In 2023, a startup's AWS bill jumped from $10/month to $49,000 in 48
hours after an access key was committed to GitHub. The attacker spun up
GPU instances for crypto mining. AWS may not waive these charges.

OWASP: A02:2021 โ€” Cryptographic Failures
Confidence: 95%

Auto-fix: secret redaction PR

zenveil fix ZG-A1B2 --auto-pr --repo owner/node-auth
Applying redaction directly (no API key needed).

Opened pull request: https://github.com/owner/node-auth/pull/23

PR title: [ZenVeil] Redact AWS access key (ZG-A1B2)
PR description:
  ZenVeil detected an AWS access key at auth/config.js:4.

  This PR redacts the value with <REDACTED_AWS_ACCESS_KEY>.

  IMPORTANT: The key must also be revoked in the AWS IAM console:
  https://console.aws.amazon.com/iam

  Finding ID: ZG-A1B2 ยท Severity: CRITICAL ยท OWASP: A02:2021
The PR applies this diff:
- awsKey: 'AKIAIOSFODNN7EXAMPLE',
+ awsKey: '<REDACTED_AWS_ACCESS_KEY>',

Fix: JWT secret (with AI)

zenveil fix ZG-C3D4
Generating fix for ZG-C3D4: Predictable JWT signing secret

The JWT signing secret 'supersecret' at auth/config.js:3 is trivially
guessable. Any attacker who knows you're using JWT can forge tokens.

Here's the fix:

**Step 1: Generate a strong secret**

```bash
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
# โ†’ a3f2b8c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1
Step 2: Store in environment
# .env (never commit this)
JWT_SECRET=a3f2b8c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1
Step 3: Load at runtime
const config = {
  jwtSecret: process.env.JWT_SECRET,  // Load from environment
  // ... other config
};

// Validate at startup
if (!config.jwtSecret || config.jwtSecret.length < 32) {
  throw new Error('JWT_SECRET must be at least 32 characters');
}
Step 4: Invalidate existing tokens Since the old secret is compromised, you must invalidate all tokens signed with it. The simplest approach is to change the secret and require all users to log in again.

## Final state after remediation

After applying all fixes:

```javascript
// auth/config.js โ€” after remediation
const config = {
  jwtSecret: process.env.JWT_SECRET,           // Strong, from env
  awsAccessKeyId: process.env.AWS_ACCESS_KEY_ID, // From env/secrets manager
  awsSecretKey: process.env.AWS_SECRET_ACCESS_KEY,
  dbPassword: process.env.DB_PASSWORD,         // From env
  sessionDuration: '1h',                        // Reasonable lifetime
};
// auth/login.js โ€” after remediation
// Removed: localStorage.setItem('auth_jwt', token)
// Replaced with server-side httpOnly cookie:
res.cookie('session', token, {
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'strict',
  maxAge: 3600000,
});
zenveil scan repo .
# โœ“ No findings. Exiting with code 0.