Install
$ agentstack add skill-dolphinllc-claude-security-skills-express-security-scan Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Dangerous shell/eval execution.
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ● Dynamic code execution Used
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Express Security Scan
Defensive scan for Express.js (4.x / 5.x) applications. Reports findings using the [shared scoring schema](../../../SCORING.md).
Scope
- Files importing
express,express.Router, or registering middleware on anapp - Session / cookie / CSRF middleware setup
- Route handlers under
routes/,controllers/,api/
Procedure
- Find the
app = express()initializer and followapp.use(...)calls in order — middleware order matters. - For each route handler, trace
req.body/req.params/req.queryinto sinks. - Apply the rules below.
Rules
| ID | Severity | Detection | Fix | |----|----------|-----------|-----| | EXP-HDR-001 | medium | helmet() not registered before routes | app.use(helmet()) early in middleware chain | | EXP-BODY-001 | high | express.json() / express.urlencoded() without limit: (default 100kb is fine, explicit is safer; missing limit on body-parser is the actual hit) | Set limit: '1mb' (or smaller) explicitly | | EXP-PROXY-001 | high | app.set('trust proxy', true) (boolean true) when running behind a single known proxy | Use a numeric hop count or specific subnet — true lets clients spoof X-Forwarded-For | | EXP-COOKIE-001 | high | Cookie/session set without httpOnly: true and secure: true | Add both; add sameSite: 'lax' or 'strict' | | EXP-SESS-001 | high | express-session with secret literal in code, or secret: 'keyboard cat' example secret | Read from env; rotate on deploy | | EXP-SESS-002 | medium | express-session default MemoryStore in production code path | Use Redis/SQL store for prod | | EXP-CSRF-001 | high | State-changing routes (POST/PUT/PATCH/DELETE) accept session cookies but no CSRF token check | Use csurf (or framework equivalent) on cookie-auth endpoints; or require custom header + SameSite=strict | | EXP-ORDER-001 | critical | Auth middleware registered after a sensitive route on the same router | Register auth (app.use(auth)) before routes | | EXP-CORS-001 | high | cors({ origin: true, credentials: true }) (reflects any Origin) | Pin origin to allowlist function | | EXP-FILE-001 | high | res.sendFile(path.join(BASE, req.params.x)) without a containment check | Resolve and verify with path.resolve(BASE, x).startsWith(BASE + path.sep) | | EXP-EVAL-001 | critical | eval(req...) / new Function(req...) / vm.runInNewContext(req...) | Never; redesign | | EXP-JWT-001 | high | jsonwebtoken.verify(token, secret) without algorithms: option | Pass algorithms: ['HS256'] (or your alg); rejects none/alg confusion | | EXP-ERR-001 | medium | Default error handler missing → stack traces leak in production | Add (err, req, res, next) => { logger.error(err); res.status(500).send('error'); } | | EXP-RATE-001 | medium | No express-rate-limit on /login, /register, /forgot | Apply per-IP limiter |
Wrong vs. right
EXP-ORDER-001 (auth registered after route)
// ❌ /admin is reachable without auth
app.get('/admin', (req, res) => res.json(stats));
app.use(requireAuth);
// ✅ Auth first
app.use(requireAuth);
app.get('/admin', (req, res) => res.json(stats));
EXP-FILE-001 (path traversal)
// ❌ ../../etc/passwd
app.get('/files/:name', (req, res) => {
res.sendFile(path.join('/var/uploads', req.params.name));
});
// ✅ Containment check
const BASE = path.resolve('/var/uploads');
app.get('/files/:name', (req, res) => {
const target = path.resolve(BASE, req.params.name);
if (!target.startsWith(BASE + path.sep)) return res.sendStatus(404);
res.sendFile(target);
});
EXP-JWT-001 (algorithm not pinned)
// ❌ Attacker can submit alg=none or HS256-signed token where RS256 expected
jwt.verify(token, secret);
// ✅ Pinned
jwt.verify(token, publicKey, { algorithms: ['RS256'], audience, issuer });
References
- Express security best practices: https://expressjs.com/en/advanced/best-practice-security.html
- helmet: https://helmetjs.github.io/
- OWASP Cheat Sheet — Node.js: https://cheatsheetseries.owasp.org/cheatsheets/NodejsSecurityCheat_Sheet.html
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Dolphinllc
- Source: Dolphinllc/claude-security-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.