https://github.com/didrod205/cookie-doctor
Lint Set-Cookie headers for security locally โ missing HttpOnly/Secure/SameSite, SameSite=None without Secure, over-broad Domain, and __Host-/__Secure- prefix violations that make browsers silently drop your cookie. Deterministic CLI, JSON/MD reports.
https://github.com/didrod205/cookie-doctor
appsec cli cookie csrf devsecops httponly linter samesite secure security security-headers session set-cookie typescript web-security
Last synced: 2 days ago
JSON representation
Lint Set-Cookie headers for security locally โ missing HttpOnly/Secure/SameSite, SameSite=None without Secure, over-broad Domain, and __Host-/__Secure- prefix violations that make browsers silently drop your cookie. Deterministic CLI, JSON/MD reports.
- Host: GitHub
- URL: https://github.com/didrod205/cookie-doctor
- Owner: didrod205
- License: mit
- Created: 2026-06-05T04:30:55.000Z (13 days ago)
- Default Branch: main
- Last Pushed: 2026-06-05T05:16:18.000Z (13 days ago)
- Last Synced: 2026-06-05T07:29:36.251Z (13 days ago)
- Topics: appsec, cli, cookie, csrf, devsecops, httponly, linter, samesite, secure, security, security-headers, session, set-cookie, typescript, web-security
- Language: TypeScript
- Homepage: https://didrod205.github.io/cookie-doctor/
- Size: 56.6 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# ๐ช cookie-doctor
### Lint your `Set-Cookie` headers for security โ locally, no website.
[](https://www.npmjs.com/package/cookie-doctor)
[](https://bundlephobia.com/package/cookie-doctor)
[](https://github.com/didrod205/cookie-doctor/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/cookie-doctor)
[](./LICENSE)
**[๐ Try the browser playground โ](https://didrod205.github.io/cookie-doctor/)** ย ยทย paste a `Set-Cookie`, see its security holes. Nothing is uploaded โ it all runs client-side.
You set a session cookie and shipped it. But it went out without `HttpOnly` (so any
XSS can steal it), without `Secure` (so it rides plain HTTP), with `SameSite=None`
but no `Secure` (so the browser **rejects it entirely**), or with a `__Host-` prefix
whose strict rules it quietly violates โ and now logins mysteriously don't stick.
You find out from a pentest, a console warning, or a 3 a.m. incident.
**cookie-doctor lints a `Set-Cookie` for these problems locally and
deterministically** โ from a string, a `curl -I` response, or a config file. It
knows the `__Host-`/`__Secure-` prefix rules and the `SameSite=None`โ`Secure`
interaction, the exact spec people get wrong.
```bash
npx cookie-doctor scan -c "sid=abc; SameSite=None"
```
```
sid 46/100 (F)
โ SameSite=None without Secure โ the browser rejects the cookie [SameSite]
โ No HttpOnly โ this looks like a session cookie, JS can read it [HttpOnly]
โ No Secure โ sent over plaintext HTTP [Secure]
```
---
## Why cookie-doctor?
- ๐งจ **It catches the "silently dropped" bugs.** `__Host-` without `Secure`, with a
`Domain`, or without `Path=/`; `SameSite=None` without `Secure` โ the browser
rejects these cookies outright, so your session just *doesn't work*. cookie-doctor
flags them as **validity** errors, not style nits.
- ๐ฏ **Severity that knows what a session cookie is.** A missing `HttpOnly` on
`sid`/`auth`/a JWT-valued cookie is an **error**; on `theme=dark` it's a gentle
warning. No noise.
- ๐ **Local & deterministic.** No website, no API key, runs offline and in CI.
Same cookie โ same result. Fail the PR that ships an insecure session cookie.
- ๐งฉ **Reads it from anywhere.** A raw `Set-Cookie`, a `curl -I` dump, an nginx
`add_header`, an Apache `Header set`, or `vercel.json`.
Why not paste it into an LLM? The `__Host-`/`__Secure-` rules and the
`SameSite=None`/`Secure` interaction are exact spec a chatbot gets subtly wrong โ
and you want this gating session config on **every** PR, not once.
## Install
```bash
# run it now
npx cookie-doctor scan -c ""
# or add it
npm install -g cookie-doctor # global CLI
npm install -D cookie-doctor # CI dependency
```
Node โฅ 18. The core is dependency-free and browser-safe.
## Quick start
```bash
cookie-doctor scan -c "__Host-sid=abc; Secure; HttpOnly; SameSite=Lax; Path=/"
curl -sI https://example.com | cookie-doctor scan # straight from a response
cookie-doctor scan headers.txt _headers vercel.json # from configs
cookie-doctor scan -c "" --min-score 80 # CI gate
cookie-doctor scan headers.txt --md cookies.md # Markdown report
cookie-doctor init # write a config
```
See [`examples/sample-report.md`](./examples/sample-report.md), and
[`examples/strong.cookie.txt`](./examples/strong.cookie.txt) for a cookie that
scores 100.
## What it checks
| Group | Examples |
| ----- | -------- |
| **Will be dropped by the browser** | `__Host-`/`__Secure-` prefix rules (Secure required, no Domain, `Path=/`), `SameSite=None` without `Secure`, invalid `SameSite`, oversized (> 4 KB) |
| **XSS / theft** | missing `HttpOnly` (error on session/token cookies) |
| **CSRF** | missing `SameSite` |
| **Transport** | missing `Secure` |
| **Scope** | `Domain` shared with subdomains, legacy leading-dot `Domain` |
| **Lifetime** | long-lived auth cookies (configurable threshold) |
Each finding is a weighted error / warning / info; the cookie rolls up to a 0โ100
score and an AโF grade you can gate in CI.
## Real scenarios
**1. Gate session-cookie security in CI.** A PR that adds an auth cookie without
`HttpOnly`/`Secure`, or breaks a `__Host-` rule, fails the build:
```yaml
# .github/workflows/ci.yml
- run: curl -sI http://localhost:3000/login | npx cookie-doctor scan --min-score 90
```
**2. Audit what your framework actually sends.** Pipe a real response through it โ
`express-session`, NextAuth, a reverse proxy โ and see the attributes you *thought*
were set.
**3. Triage a security finding.** A scanner said "insecure cookie" โ `cookie-doctor`
tells you *which* attribute and *why*, with the exact fix.
## Configuration
`cookie-doctor init` writes `cookie-doctor.config.json`:
```jsonc
{
"ignore": [], // rule ids to skip
"sessionNames": ["session", "sid", "auth", "token", "jwt", "..."],
"maxAgeDays": 30, // warn above this lifetime
"minScore": 0 // CI gate threshold
}
```
## Library API
```ts
import { analyzeSetCookie, DEFAULT_CONFIG } from "cookie-doctor";
const report = analyzeSetCookie("inline", "sid=abc; Secure", DEFAULT_CONFIG, Date.now());
for (const f of report.findings) console.log(f.severity, f.rule, f.attribute);
```
Also exported: `parseSetCookie`, `checkCookie`, `extractCookies`, `lifetimeSeconds`,
and types. The core has zero runtime dependencies.
## Roadmap
- ๐ค **Optional `--ai` layer (bring-your-own key)** to explain a cookie's risk in
context / suggest a hardened header. The core stays 100% offline and deterministic.
- `Partitioned` (CHIPS) attribute awareness.
- Read cookies from a saved `.har` or a browser cookie export.
- โ
**A browser playground** โ paste a `Set-Cookie`, see the audit, nothing uploaded.
[Live here](https://didrod205.github.io/cookie-doctor/).
## ๐ Sponsor
cookie-doctor is free and MIT-licensed, built and maintained in spare time. If it
caught an insecure cookie before your users did, please consider supporting it:
- โญ **Star this repo** โ the simplest free way to help others find it.
- ๐ **[Sponsor via Lemon Squeezy](https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010)** โ one-time or recurring.
## License
[MIT](./LICENSE) ยฉ cookie-doctor contributors