Review Express.js security audit patterns for middleware and routes. Use for auditing Helmet.js, CORS, body-parser limits, and auth middleware. Use proactively when reviewing Express.js apps. Examples: - user: "Secure my Express app" → add Helmet.js and disable x-powered-by - user: "Check Express CORS config" → verify origin allowlists and credentials - user: "Review Express auth middleware" → check route order and coverage - user: "Scan for Express path traversal" → verify path normalization and validation - user: "Audit Express session config" → check secure, httpOnly, and sameSite flags
Security audit patterns for Express.js applications covering essential security middleware, CORS configuration, auth patterns, and common vulnerabilities.
</overview> <rules>// Missing security headers - MUST NOT do this
const app = express();
// MUST use Helmet
const helmet = require('helmet');
app.use(helmet());
MUST check if Helmet is installed and used. It sets:
// Default - header reveals framework
const app = express();
// MUST disable fingerprinting
app.disable('x-powered-by');
// or: app.set('x-powered-by', false);
// CRITICAL: Allow all origins - MUST NOT do this
app.use(cors());
app.use(cors({ origin: '*' }));
// HIGH: Reflect origin with credentials - MUST NOT do this
app.use(cors({
origin: true, // Reflects any origin!
credentials: true
}));
// MUST use explicit allowlist
app.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
credentials: true,
}));
// MAY use function for dynamic validation
app.use(cors({
origin: (origin, callback) => {
const allowed = ['https://app.example.com'];
if (!origin || allowed.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
}));
// No limit (DoS risk) - MUST NOT do this
app.use(express.json());
// MUST set reasonable limits
app.use(express.json({ limit: '100kb' }));
app.use(express.urlencoded({ extended: true, limit: '100kb' }));
</rules>
<vulnerabilities>
// No auth on admin routes - MUST NOT do this
app.get('/api/admin/users', async (req, res) => {
res.json(await User.find());
});
// MUST apply auth middleware
app.get('/api/admin/users', requireAuth, requireAdmin, async (req, res) => {
res.json(await User.find());
});
// Wrong order - static files before auth - MUST NOT do this
app.use(express.static('uploads')); // Exposed!
app.use(requireAuth);
// MUST place auth before protected static files
app.use('/public', express.static('public')); // Intentionally public
app.use(requireAuth);
app.use('/uploads', express.static('uploads')); // Now protected
// SHOULD check: Is auth applied to all routes in admin router?
const adminRouter = express.Router();
adminRouter.use(requireAuth); // Applied to all routes below
adminRouter.get('/users', getUsers);
adminRouter.delete('/users/:id', deleteUser);
// Watch for routes defined BEFORE the middleware
const apiRouter = express.Router();
apiRouter.get('/health', getHealth); // No auth (intentional?)
apiRouter.use(requireAuth);
apiRouter.get('/users', getUsers); // Has auth
// String interpolation - MUST NOT do this
const user = await db.query(`SELECT * FROM users WHERE id = ${req.params.id}`);
// MUST use parameterized query
const user = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
// MongoDB injection risk
const user = await User.findOne({ email: req.body.email }); // If email is { $gt: "" }
// MUST validate input type
if (typeof req.body.email !== 'string') return res.status(400).json({ error: 'Invalid email' });
// User-controlled path - MUST NOT do this
app.get('/files/:filename', (req, res) => {
res.sendFile(`./uploads/${req.params.filename}`); // ../../etc/passwd
});
// MUST validate and normalize
const path = require('path');
app.get('/files/:filename', (req, res) => {
const filename = path.basename(req.params.filename);
const filepath = path.join(__dirname, 'uploads', filename);
if (!filepath.startsWith(path.join(__dirname, 'uploads'))) {
return res.status(400).json({ error: 'Invalid path' });
}
res.sendFile(filepath);
});
// Stack traces in production - MUST NOT do this
app.use((err, req, res, next) => {
res.status(500).json({ error: err.stack }); // Leaks internals
});
// MUST use safe error handler
app.use((err, req, res, next) => {
console.error(err); // Log for debugging
res.status(500).json({ error: 'Internal server error' });
});
// Insecure session config - MUST NOT do this
app.use(session({
secret: 'keyboard cat', // Hardcoded!
cookie: { secure: false }, // No HTTPS requirement
}));
// MUST use secure config
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JS access
sameSite: 'strict', // CSRF protection
maxAge: 1000 * 60 * 60 * 24, // 24 hours
},
}));
SHOULD check for rate limiting on auth routes:
const rateLimit = require('express-rate-limit');
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts
message: 'Too many login attempts',
});
app.post('/api/login', authLimiter, loginHandler);
app.post('/api/register', authLimiter, registerHandler);
app.post('/api/forgot-password', authLimiter, forgotPasswordHandler);
</vulnerabilities>
<commands>
# Check if Helmet is used
rg -n 'helmet\\(' . -g "*.js" -g "*.ts"
# Check if x-powered-by is disabled
rg -n "x-powered-by" . -g "*.js" -g "*.ts"
# Check for helmet
rg "helmet" package.json
rg "require\\(['\"]helmet" .
rg "from ['\"]helmet" .
# Find CORS config
rg "cors\\(" . -g "*.js" -g "*.ts" -A 5
# Find routes without auth middleware
rg "app\\.(get|post|put|delete|patch)\\(" . -A 1 | grep -v "require.*[Aa]uth"
# Find string interpolation in queries
rg "(query|find|findOne|exec).*\\`" . -g "*.js" -g "*.ts"
# Check session config
rg "session\\(" . -A 10
</commands>
<checklist>
npx skills add justinlevinedotme/security-express下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer