smithery/justinlevinedotme

security-express

|- 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. - 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 vali…

Installation

$ npx skills add smithery/justinlevinedotme --skill security-express

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/justinlevinedotme.

npx skills add smithery/justinlevinedotme

Browse all from smithery/justinlevinedotme

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,404 B
  • docs SUMMARY.md 636 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

<overview>

Security audit patterns for Express.js applications covering essential security middleware, CORS configuration, auth patterns, and common vulnerabilities.

</overview>

<rules>

Essential Security Middleware

Helmet.js (Security Headers)

// 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:

  • Content-Security-Policy
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Strict-Transport-Security
  • And more...

Disable X-Powered-By

// Default - header reveals framework
const app = express();

// MUST disable fingerprinting
app.disable('x-powered-by');
// or: app.set('x-powered-by', false);

CORS Configuration

// 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,
}));

Body Parser Limits

// 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>

Auth Middleware Patterns

Missing Auth on Routes

// 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());
});

Middleware Order Matters

// 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

Router-Level Auth Gaps

// 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

Common Vulnerabilities

SQL/NoSQL Injection

// 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' });

Path Traversal

// 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);
});

Error Handling

// 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' });
});

Session Security

// 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
  },
}));

Rate Limiting

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>

Quick Audit 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>

Hardening Checklist

  • Helmet.js MUST be installed and used
  • CORS MUST be restricted to specific origins
  • Body parser MUST have size limits
  • Auth middleware MUST be on all protected routes
  • Rate limiting SHOULD be on auth endpoints
  • Session cookies MUST be: secure, httpOnly, sameSite
  • MUST NOT have hardcoded secrets
  • Error handler MUST NOT leak stack traces
  • MUST have input validation on all user input
  • MUST use parameterized queries (no string concat)

</checklist>