Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 5 additions & 28 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,11 @@
This file gives Claude (and other AI coding assistants) the context needed to work
accurately on express-audit without exploring the codebase from scratch.

---

## What this project is

express-audit is a static security analysis CLI for Express.js applications. It parses
source files into an AST using `@babel/parser`, runs structured rule visitors over the
tree, and reports findings with file path, line number, severity, impact, and fix.

It does not execute application code, make network requests, or write to the project
being scanned.
express-audit is a static security analysis CLI for Express.js applications. It parses source files into an AST using `@babel/parser`, runs structured rule visitors over the tree, and reports findings with file path, line number, severity, impact, and fix.

---
It does not execute application code, make network requests, or write to the project being scanned.

## Commands

Expand All @@ -28,7 +21,7 @@ node scripts/generate-version.mjs
# Build TypeScript to dist/
npm run build

# Run all tests (150 tests across 15 files)
# Run all tests (182 tests across 16 files)
npm test

# Run tests in watch mode
Expand All @@ -49,8 +42,6 @@ node dist/cli.js ./src
`node scripts/generate-version.mjs` has not been run first. The `prebuild` script runs
it automatically before `npm run build`.

---

## Project structure

```
Expand Down Expand Up @@ -98,8 +89,6 @@ examples/
secure-app/app.js Well-secured Express app
```

---

## Core types

```typescript
Expand Down Expand Up @@ -141,8 +130,6 @@ interface Finding {
}
```

---

## How to write a rule

1. Create `src/rules/<category>/<descriptive-name>.ts`
Expand Down Expand Up @@ -199,8 +186,6 @@ export const myRule: Rule = {
};
```

---

## Key AST helpers (`src/core/ast-helpers.ts`)

| Helper | What it does |
Expand All @@ -215,8 +200,6 @@ export const myRule: Rule = {
| `getNodeColumn(node)` | Column number from AST node |
| `getCalleeName(call)` | Returns `"obj.method"` string from a call expression |

---

## Key design decisions

**Entry-file scoping.** Project-level rules (HTTP001, CSP001, RATE001, HEADER001, ERR002)
Expand All @@ -241,8 +224,6 @@ post-discovery filter in `engine.ts`.
remediation strings showing both ESM and CJS import patterns. Use it for any rule that
recommends installing a package.

---

## How to write tests

Tests use Vitest. Test helpers are in `tests/helpers.ts`.
Expand Down Expand Up @@ -276,8 +257,6 @@ fixture name so path-based rules (e.g. `'app.js'`, `'Dockerfile'`) behave correc
Every rule must have at minimum one test that fires (true positive) and one that does not
(false positive check).

---

## Rule ID registry

| Prefix | Category | Used |
Expand All @@ -286,6 +265,8 @@ Every rule must have at minimum one test that fires (true positive) and one that
| `AUTH` | Authentication | 001–002 |
| `AUTHZ` | Authorization | 001–002 |
| `VAL` | Input Validation | 001–002 |
| `PP` | Prototype Pollution | 001 |
| `INJECT` | Code Injection | 001 |
| `SQL` | SQL Security | 001–002 |
| `HTTP` | HTTP Security | 001 |
| `HEADER` | HTTP Headers | 001 |
Expand All @@ -306,17 +287,13 @@ Every rule must have at minimum one test that fires (true positive) and one that

Pick the next available number in the appropriate prefix range.

---

## Scoring

- Each category starts at 100 points
- Deductions per finding: critical −25, high −10, medium −5, low −2, info −0
- Overall score is a weighted average across categories (weights in `CATEGORY_WEIGHTS`)
- Score of 100 means no findings — it does not mean the app is secure

---

## Dependencies (runtime only)

| Package | Purpose |
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ express-audit is designed to be deterministic, transparent, and privacy-friendly

**Every rule maps to a published standard.** All findings reference OWASP, RFCs, CWE, or W3C specifications — not internal opinions. See [Standards & References](./docs/standards.md) for the full per-rule mapping.

**150 tests, all passing.** Every rule is tested against both a vulnerable and a secure implementation before release. See [How We Ensure Accuracy](./docs/accuracy.md) for the testing methodology and false-positive strategy.
**182 tests, all passing.** Every rule is tested against both a vulnerable and a secure implementation before release. See [How We Ensure Accuracy](./docs/accuracy.md) for the testing methodology and false-positive strategy.

**Rules are documented with rationale.** Every rule in [`docs/rules/`](./docs/rules/) includes a description, a vulnerable example, a secure example, the security impact, and references to OWASP, RFCs, or official documentation — so you understand *why* a finding matters, not just *that* it fired.

Expand Down Expand Up @@ -271,7 +271,8 @@ Hardcoded JWT secrets, missing token expiration, weak bcrypt cost factors, plain
Sensitive routes (`DELETE`, `PATCH`, `PUT`) without authentication middleware, admin endpoints without role checks.

### Input Validation
Direct use of `req.body` and `req.query` without a validation library (Zod, Joi, express-validator).

Direct use of `req.body` and `req.query` without a validation library (Zod, Joi, express-validator). Prototype pollution via `Object.assign`, `_.merge`, and computed property assignment with user-controlled input. Code injection via `eval()`, `new Function()`, and the Node.js `vm` module with user-controlled input.

### SQL Security
Raw query string concatenation with user input, unsafe Prisma `$queryRawUnsafe` / `$executeRawUnsafe` calls.
Expand Down Expand Up @@ -319,7 +320,7 @@ Dozens of built-in rules across 16 categories. Full documentation for each rule
|---|---|---|
| `JWT`, `AUTH` | Authentication | JWT001, JWT002, AUTH001, AUTH002 |
| `AUTHZ` | Authorization | AUTHZ001, AUTHZ002 |
| `VAL` | Input Validation | VAL001, VAL002 |
| `VAL`, `PP`, `INJECT` | Input Validation | VAL001, VAL002, PP001, INJECT001 |
| `SQL` | SQL Security | SQL001, SQL002 |
| `HTTP`, `HEADER`, `CSP` | HTTP Security | HTTP001, CSP001, HEADER001 |
| `COOKIE`, `SESSION` | Cookies & Sessions | COOKIE001, SESSION001 |
Expand Down
8 changes: 0 additions & 8 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ backported patches.
Once the project reaches 1.0.0, this table will be updated to reflect a formal
long-term support window.

---

## Reporting a Vulnerability

**Please do not open a public GitHub issue for security vulnerabilities.**
Expand Down Expand Up @@ -57,8 +55,6 @@ A useful report contains:
If a reported issue turns out not to be a vulnerability, we will let you know
promptly and explain the reasoning.

---

## Responsible Disclosure

We ask that you:
Expand Down Expand Up @@ -92,17 +88,13 @@ Out of scope:
vulnerable for demonstration purposes
- General bugs that have no security impact (open a regular issue instead)

---

## Vulnerability Disclosure History

No vulnerabilities have been reported or disclosed to date.

This section will be updated with CVE identifiers and release links as the
project matures.

---

## Contact

Maintainer: **Muhammad Lahin**
Expand Down
65 changes: 65 additions & 0 deletions docs/false-positives.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,71 @@ await myRepo.fetch(item.id); // 'fetch' is not a recognized DB method

---

### PP001 — Prototype Pollution via Object Merge

**Might report when code is fine (false positive)**

```js
// May fire — bare function named 'merge' that is unrelated to object merging
merge(outputStream, inputStream); // stream merge, not object merge
```

The rule matches any bare function call named `merge`, `deepMerge`, `extend`, or
`defaults` with a user-input argument. A stream utility or custom function that happens
to share one of those names will trigger it.

**Fix:** rename the function or suppress the finding on that line.

**Might stay silent when code is vulnerable (false negative)**

```js
// ❌ Will NOT fire — user input assigned to a variable first
const data = req.body;
Object.assign(config, data);

// ❌ Will NOT fire — custom recursive merge function not in the known list
myDeepClone(target, req.body);

// ❌ Will NOT fire — spread into an object literal
const merged = { ...defaults, ...req.body };
```

Object spread (`{ ...req.body }`) is the most common false negative. It is functionally
equivalent to `Object.assign` for prototype pollution purposes but produces a different
AST node (`ObjectExpression` with `SpreadElement`) that this rule does not currently
cover. A separate rule or an expansion of PP001 would be needed to catch it.

---

### INJECT001 — Code Injection via eval or new Function

**Might report when code is fine (false positive)**

Almost none. The rule requires both a dangerous sink (`eval`, `new Function`, `vm.*`) AND traceable user input (`req.body`, `req.query`, `req.params`, `req.headers`) in the same expression. A hardcoded string passed to `eval` will not fire.

```js
eval('1 + 1'); // will NOT fire — no user input
new Function('a', 'return a'); // will NOT fire — all string literals
```

**Might stay silent when code is vulnerable (false negative)**

```js
// ❌ Will NOT fire — user input stored in a variable first
const code = req.body.script;
eval(code);

// ❌ Will NOT fire — user input passed through a function call
eval(sanitize(req.body.code)); // even if sanitize() does nothing useful

// ❌ Will NOT fire — indirect eval via setTimeout string form
setTimeout(req.body.code, 0);
```

The variable-assignment gap is the most important one. If the code assigns user input to a variable and then passes that variable to `eval`, the rule does not fire. This is a known limitation of static analysis without data-flow tracking.

---

## The bottom line

| What the tool is good at | What it misses |
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Complete documentation for all express-audit rules.
|------|----------|-------|
| VAL001 | 📋 Medium | Unvalidated Request Body |
| VAL002 | 📋 Medium | Unvalidated Query Parameters |
| PP001 | ⚠️ High | Prototype Pollution via Object Merge |
| INJECT001 | 🔴 Critical | Code Injection via eval or new Function |

## SQL Security

Expand Down
25 changes: 25 additions & 0 deletions docs/standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,31 @@ connection strings, SendGrid API keys, and generic hardcoded passwords.

---

## Input Validation

### PP001 — Prototype Pollution via Object Merge

| Standard | Reference |
|---|---|
| OWASP Top 10 2021 | [A03: Injection](https://owasp.org/Top10/A03_2021-Injection/) |
| OWASP ASVS v4.0 | [V5.1: Input Validation](https://owasp.org/www-project-application-security-verification-standard/) |
| OWASP | [Prototype Pollution](https://owasp.org/www-community/vulnerabilities/Prototype_Pollution) |
| CWE | [CWE-1321: Improperly Controlled Modification of Object Prototype](https://cwe.mitre.org/data/definitions/1321.html) |
| Snyk | [Prototype Pollution Guide](https://learn.snyk.io/lesson/prototype-pollution/) |

### INJECT001 — Code Injection via eval or new Function

| Standard | Reference |
|---|---|
| OWASP Top 10 2021 | [A03: Injection](https://owasp.org/Top10/A03_2021-Injection/) |
| OWASP ASVS v4.0 | [V5.2: Sanitization and Sandboxing](https://owasp.org/www-project-application-security-verification-standard/) |
| OWASP | [Code Injection](https://owasp.org/www-community/attacks/Code_Injection) |
| CWE | [CWE-94: Improper Control of Generation of Code](https://cwe.mitre.org/data/definitions/94.html) |
| CWE | [CWE-95: Improper Neutralization of Directives in eval()](https://cwe.mitre.org/data/definitions/95.html) |
| Node.js | [vm Module Documentation](https://nodejs.org/api/vm.html) |

---

## SQL Security

### SQL001 — SQL Injection Risk
Expand Down
Loading
Loading