require-helmet
This rule detects Express.js applications that are missing the helmet middleware
Require helmet middleware for security headers in Express.js applications
Severity: π΄ High
CWE: CWE-693
Value & investment case
Why this rule pays for itself. Framework:
cicd-impact/philosophy.md.
| Dimension | Value |
|---|---|
| CWE | CWE-693 β Protection Mechanism Failure (missing security headers) |
| Feedback-loop tier | Editor / pre-commit (sub-second) β cheapest layer per the feedback-loop hierarchy |
| Defensive-layer leverage | ~10Γ cheaper than unit-test Β· ~1,000Γ cheaper than production rollback Β· 10,000+Γ cheaper than customer disclosure (cost-ratio anchors) |
| Niche relevance | Critical: B2B SaaS, B2C (any browser-facing surface) Β· High: fintech, healthtech, marketplaces Β· Medium: infra/devtools |
| Investor-frame impact | Missing Helmet β no CSP, X-Frame-Options, HSTS, etc. Defense-in-depth gap that SOC2 Common Criteria CC6.6 explicitly addresses. Lint-time enforcement = audit-grade evidence. |
Read also: philosophy.md Β§investor-frame Β· niche-presets.json Β· analyzer-evaluation-framework.md
Rule Details
This rule detects an Express application that renders documents to a browser without mounting helmet.
Missing security headers can expose a rendered page to:
- Clickjacking attacks (X-Frame-Options)
- XSS attacks (X-XSS-Protection, Content-Security-Policy)
The document precondition
Helmet's distinctive headers are instructions to a renderer β they are inert
on a response nothing renders. The rule therefore requires evidence that this
app returns documents: res.render(β¦), res.sendFile(β¦), or a configured view
engine (app.set('view engine', β¦) / app.engine(β¦)).
res.send(β¦) is deliberately not evidence: it is the generic responder for
JSON, text and buffers, and a linter cannot tell a document from a payload
without reading it.
The two headers that do apply to a machine client are owned elsewhere in this
plugin β require-strict-transport-security (HSTS) and
no-disabled-helmet-protections (X-Content-Type-Options) β so the
precondition removes a duplicate finding rather than a unique one.
The escape hatch
If the app binding leaves the file β module.exports = app,
export default app, return app, or setAppConfigurations(app) β the
middleware stack is assembled somewhere this rule cannot see, and it abstains.
Absence of evidence is not evidence of absence.
Partition with require-rate-limiting
Both rules used to report the identical express() node. require-rate-limiting
now reports the specific unthrottled credential endpoint instead, so the two are
disjoint by construction.
Examples
β Incorrect
import express from 'express';
const app = express();
// Renders documents, no helmet - VULNERABLE
app.set('view engine', 'pug');
app.get('/', (req, res) => res.render('home'));
app.listen(3000);β Correct
import express from 'express';
import helmet from 'helmet';
const app = express();
// Helmet adds security headers
app.use(helmet());
app.set('view engine', 'pug');
app.get('/', (req, res) => res.render('home'));
app.listen(3000);// A static asset server renders nothing itself. Not reported.
const app = express();
app.use(express.static('./public'));
app.listen(8080);
// The app is configured by its importer. Not reported.
const app2 = express();
module.exports = app2;Options
| Option | Type | Default | Description |
|---|---|---|---|
allowInTests | boolean | false | Allow missing helmet in test files |
alternativeMiddleware | string[] | [] | Alternative security headers middleware names to accept |
assumeHelmetMiddleware | boolean | false | Skip rule if security headers are provided elsewhere (e.g., reverse proxy) |
{
"rules": {
"express-security/require-helmet": [
"error",
{
"allowInTests": true,
"alternativeMiddleware": ["securityHeaders"]
}
]
}
}When Not To Use It
Never disable this rule in production. Security headers are a fundamental protection layer.
Known False Negatives
The following patterns are not detected due to static analysis limitations:
App Instance from Variable
Why: Express app stored in variable may not be recognized.
// β NOT DETECTED - App from factory function
const app = createExpressApp();
// Helmet might be applied in createExpressApp, or notMitigation: Apply rule to factory modules. Document helmet usage centrally.
Conditional Middleware
Why: Middleware applied inside conditions is not tracked.
// β NOT DETECTED - Conditional helmet
if (process.env.NODE_ENV === 'production') {
app.use(helmet());
}
// Development may run without headersMitigation: Always apply helmet unconditionally. Use environment-specific configuration inside helmet options.
Framework Wrappers
Why: Higher-level frameworks may include helmet internally.
// β FALSE POSITIVE RISK - Framework includes helmet
import { createServer } from '@my-company/express-framework';
const app = createServer(); // May include helmetMitigation: Configure alternativeMiddleware option. Add framework-specific patterns.
Late Middleware Application
Why: Helmet applied after route definitions is less effective.
// β NOT DETECTED - Helmet AFTER routes
app.get('/api', handler);
app.use(helmet()); // Security headers won't apply to /apiMitigation: Ensure helmet is among the first middleware. Review middleware order in code review.
Custom Security Headers
Why: Manual header setting without helmet is not recognized.
// β NOT DETECTED - Manual headers instead of helmet
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-Content-Type-Options', 'nosniff');
next();
});Mitigation: Use helmet for comprehensive coverage. Configure alternativeMiddleware for known patterns.
Further Reading
Did this rule catch something? Star the repo to get new CWE coverage as we ship it β or follow the AI-code-security benchmarks behind these rules.
require-express-body-parser-limits
The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:
require-query-type-guard
This rule detects string methods called on req.query values without a type guard β Express query values can be arrays or objects, not just strings