vibesec
Version:
Security scanner for AI-generated code - detects vulnerabilities in vibe-coded projects
477 lines (434 loc) • 13.6 kB
YAML
# HTTP Security Headers Rules
# Detects missing or misconfigured HTTP security headers
rules:
- id: missing-csp-header
name: Missing Content Security Policy
description: Content-Security-Policy header is missing, allowing XSS and data injection attacks
severity: high
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "app\\.(use|get|post)\\s*\\((?!.*Content-Security-Policy)"
flags: gi
- regex: "express\\(\\)(?!.*helmet|.*csp)"
flags: gi
fix:
template: |
Implement Content-Security-Policy header using helmet or manually.
Before:
const app = express();
After:
const helmet = require('helmet');
const app = express();
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"]
}
}));
// Or manually:
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
);
next();
});
references:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
- https://owasp.org/www-project-secure-headers/#content-security-policy
metadata:
cwe: CWE-693
owasp: "A05:2021"
tags:
- headers
- csp
- xss
- id: missing-hsts-header
name: Missing HTTP Strict Transport Security
description: HSTS header missing, allowing protocol downgrade attacks
severity: high
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "https\\.createServer|createServer\\([^)]*https(?!.*Strict-Transport-Security|.*hsts)"
flags: gi
- regex: "app\\.listen\\(443(?!.*Strict-Transport-Security|.*helmet)"
flags: gi
fix:
template: |
Add HSTS header to enforce HTTPS connections.
Before:
const app = express();
After:
const helmet = require('helmet');
app.use(helmet.hsts({
maxAge: 31536000, // 1 year in seconds
includeSubDomains: true,
preload: true
}));
// Or manually:
app.use((req, res, next) => {
res.setHeader(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
next();
});
references:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
- https://owasp.org/www-project-secure-headers/#http-strict-transport-security
metadata:
cwe: CWE-319
owasp: "A02:2021"
tags:
- headers
- hsts
- https
- id: missing-x-frame-options
name: Missing X-Frame-Options Header
description: Missing clickjacking protection header
severity: medium
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "app\\.use\\((?!.*X-Frame-Options|.*frameguard|.*helmet)"
flags: gi
- regex: "res\\.render\\((?!.*X-Frame-Options)"
flags: gi
fix:
template: |
Add X-Frame-Options header to prevent clickjacking.
Before:
const app = express();
After:
const helmet = require('helmet');
app.use(helmet.frameguard({ action: 'deny' }));
// Or manually:
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
// Or: res.setHeader('X-Frame-Options', 'SAMEORIGIN');
next();
});
references:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
- https://owasp.org/www-project-secure-headers/#x-frame-options
metadata:
cwe: CWE-1021
owasp: "A04:2021"
tags:
- headers
- clickjacking
- x-frame-options
- id: missing-x-content-type-options
name: Missing X-Content-Type-Options Header
description: Missing MIME type sniffing protection
severity: medium
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "app\\.use\\((?!.*X-Content-Type-Options|.*noSniff|.*helmet)"
flags: gi
fix:
template: |
Add X-Content-Type-Options header to prevent MIME sniffing.
Before:
const app = express();
After:
const helmet = require('helmet');
app.use(helmet.noSniff());
// Or manually:
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
next();
});
references:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
- https://owasp.org/www-project-secure-headers/#x-content-type-options
metadata:
cwe: CWE-693
owasp: "A05:2021"
tags:
- headers
- mime-sniffing
- x-content-type-options
- id: missing-referrer-policy
name: Missing Referrer-Policy Header
description: Referrer-Policy header missing, potentially leaking sensitive information
severity: low
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "app\\.use\\((?!.*Referrer-Policy|.*referrerPolicy|.*helmet)"
flags: gi
fix:
template: |
Add Referrer-Policy header to control referrer information.
Before:
const app = express();
After:
const helmet = require('helmet');
app.use(helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' }));
// Or manually:
app.use((req, res, next) => {
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
next();
});
references:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
- https://owasp.org/www-project-secure-headers/#referrer-policy
metadata:
cwe: CWE-200
owasp: "A01:2021"
tags:
- headers
- referrer-policy
- privacy
- id: missing-permissions-policy
name: Missing Permissions-Policy Header
description: Permissions-Policy (formerly Feature-Policy) header missing
severity: low
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "app\\.use\\((?!.*Permissions-Policy|.*Feature-Policy|.*helmet)"
flags: gi
fix:
template: |
Add Permissions-Policy header to control browser features.
Before:
const app = express();
After:
const helmet = require('helmet');
app.use(helmet.permittedCrossDomainPolicies());
// Or manually:
app.use((req, res, next) => {
res.setHeader(
'Permissions-Policy',
'geolocation=(), microphone=(), camera=()'
);
next();
});
references:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy
- https://owasp.org/www-project-secure-headers/#permissions-policy
metadata:
cwe: CWE-693
owasp: "A05:2021"
tags:
- headers
- permissions-policy
- privacy
- id: weak-csp-policy
name: Weak Content Security Policy
description: CSP allows 'unsafe-inline' or 'unsafe-eval' which weakens XSS protection
severity: medium
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "Content-Security-Policy[\"'].*[\"']unsafe-inline[\"']"
flags: gi
- regex: "Content-Security-Policy[\"'].*[\"']unsafe-eval[\"']"
flags: gi
- regex: "scriptSrc\\s*:\\s*\\[[^\\]]*[\"']unsafe-inline[\"']"
flags: gi
- regex: "scriptSrc\\s*:\\s*\\[[^\\]]*[\"']unsafe-eval[\"']"
flags: gi
fix:
template: |
Remove 'unsafe-inline' and 'unsafe-eval' from CSP. Use nonces or hashes.
Before:
app.use(helmet.contentSecurityPolicy({
directives: {
scriptSrc: ["'self'", "'unsafe-inline'"]
}
}));
After:
// Option 1: Use nonces
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString('base64');
next();
});
app.use(helmet.contentSecurityPolicy({
directives: {
scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`]
}
}));
// In your template:
// <script nonce="<%= nonce %>">...</script>
// Option 2: Use hashes for inline scripts
app.use(helmet.contentSecurityPolicy({
directives: {
scriptSrc: ["'self'", "'sha256-xyz...'"]
}
}));
references:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
- https://content-security-policy.com/
metadata:
cwe: CWE-693
owasp: "A05:2021"
tags:
- headers
- csp
- unsafe-inline
- id: x-powered-by-header
name: X-Powered-By Header Exposure
description: X-Powered-By header reveals technology stack information
severity: low
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "express\\(\\)(?!.*app\\.disable\\([\"']x-powered-by[\"']\\)|.*helmet\\.hidePoweredBy)"
flags: gi
- regex: "X-Powered-By"
flags: gi
fix:
template: |
Remove or disable X-Powered-By header.
Before:
const app = express();
After:
const app = express();
app.disable('x-powered-by');
// Or use helmet:
app.use(helmet.hidePoweredBy());
references:
- https://owasp.org/www-project-secure-headers/
- https://expressjs.com/en/advanced/best-practice-security.html
metadata:
cwe: CWE-200
owasp: "A05:2021"
tags:
- headers
- information-disclosure
- x-powered-by
- id: cors-allow-all-origins
name: Permissive CORS Configuration
description: CORS allows all origins (*), potentially exposing sensitive data
severity: medium
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "Access-Control-Allow-Origin[\"']\\s*,\\s*[\"']\\*[\"']"
flags: gi
- regex: "cors\\s*\\(\\s*\\{[^}]*origin\\s*:\\s*[\"']\\*[\"']"
flags: gi
fix:
template: |
Specify allowed origins instead of using wildcard.
Before:
app.use(cors({ origin: '*' }));
After:
const allowedOrigins = ['https://example.com', 'https://app.example.com'];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));
references:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
- https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client-side_Testing/07-Testing_Cross_Origin_Resource_Sharing
metadata:
cwe: CWE-346
owasp: "A05:2021"
tags:
- headers
- cors
- access-control
- id: cache-control-sensitive-data
name: Missing Cache-Control for Sensitive Data
description: Sensitive endpoints lack proper Cache-Control headers
severity: medium
category: headers
languages:
- javascript
- typescript
- python
- php
enabled: true
patterns:
- regex: "app\\.(get|post)\\s*\\([\"']/api/(user|account|payment|auth)[^\"']*[\"'](?!.*Cache-Control|.*no-store)"
flags: gi
- regex: "res\\.json\\([^)]*password[^)]*\\)(?!.*no-cache|.*no-store)"
flags: gi
fix:
template: |
Add proper Cache-Control headers for sensitive data.
Before:
app.get('/api/user/profile', (req, res) => {
res.json({ user: userData });
});
After:
app.get('/api/user/profile', (req, res) => {
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, private');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
res.json({ user: userData });
});
references:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
- https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/04-Authentication_Testing/06-Testing_for_Browser_Cache_Weaknesses
metadata:
cwe: CWE-524
owasp: "A04:2021"
tags:
- headers
- cache-control
- sensitive-data