Aller au contenu

Open source / Sentinel / cookie-sentinel

cookie-sentinel

Audit Set-Cookie headers: missing Secure/HttpOnly/SameSite, __Host-/__Secure- prefix violations, broad Domain, long expiry, cookies over HTTP, JWTs and emails in values, with a fix for each. Text, JSON, SARIF, Markdown; GitHub Action. Zero deps.

Sur npm
$ npm install cookie-sentinel

README

cookie-sentinel

CI npm version License: MIT Node

Audit the cookies your site sets before attackers do.

cookie-sentinel fetches your pages (following every redirect), collects every Set-Cookie header from every hop and audits each cookie against modern browser rules and security best practice. Every finding comes with a severity and a concrete fix. It also audits raw Set-Cookie lines offline from a file or stdin.

cookie-sentinel is the dedicated cookie authority in the sentinel set — use header-sentinel for HTTP security headers only (it keeps a minimal cookie presence check and points here for depth).

CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies. Optional Playwright / Puppeteer peer dependencies for JavaScript-set cookies.

Tests live under test/ and run in GitHub Actions CI (including import + require checks).

Rules

RuleDefault severityWhat it catches
missing-securehigh (session-like) / mediumCookie on an HTTPS site without Secure
cookie-over-httphigh (session-like) / mediumCookie set by a plain http:// response
missing-httponlyhighSession-like cookie (configurable name patterns) without HttpOnly
samesite-missingmedium (session-like) / lowNo SameSite attribute
samesite-nonemedium (session-like) / lowSameSite=None (cross-site sending, CSRF exposure)
samesite-none-insecurehighSameSite=None without Secure: browsers reject it
samesite-invalidmediumSameSite value that isn't Strict/Lax/None
host-prefixhigh__Host- cookie without Secure, with Domain, without Path=/, or set over HTTP; mis-cased prefixes
secure-prefixhigh__Secure- cookie without Secure or set over HTTP
broad-domainmedium (session-like) / low; high for a TLDDomain= a parent domain (sent to and overwritable by every subdomain) or Domain on a session cookie
domain-mismatchlowDomain that doesn't match the host (cookie rejected)
broad-pathinfoSensitive cookie with Path=/
long-expirymedium (session-like > 30 days) / low (> 400 days)Excessive Expires/Max-Age
large-cookielow (> 3072 B) / medium (> 4096 B)Very large cookies
sensitive-jwtmedium; high without HttpOnlyJWT in a cookie value (payload claims are readable)
jwt-alg-nonecriticalUnsigned JWT (alg: none)
sensitive-emailmediumEmail address in a value (plain, URL-encoded, base64 or JWT claims)
duplicate-cookielow / mediumSame cookie set twice in one response, or one name set with different Domain/Path scopes (cookie tossing)
partitioned-insecurehighPartitioned without Secure
partitioned-misuselowPartitioned without SameSite=None, or on session cookies

Deletion cookies (Max-Age<=0 or an expiry in the past) are ignored. Default session-like names include sess, sid, auth, token, jwt, login, remember, access, refresh, PHPSESSID, JSESSIONID, connect.sid and others. csrf/xsrf cookies are excluded from the HttpOnly check.

Classification (Open Cookie Database)

Each cookie is classified offline against a bundled snapshot of the Open Cookie Database (no network at runtime):

  • Party: first-party vs third-party (from Domain / page host)
  • Purpose: tracking vs essential (from OCDB category)
  • Vendor / category when the name matches the catalogue

Refresh the snapshot when you maintain the package:

npm run update-ocdb

Install and run

npx cookie-sentinel https://example.com
npx cookie-sentinel https://example.com -p /login -p /account --fail-on medium
npx cookie-sentinel https://app.example.com --crawl urls.txt          # URLs or paths, one per line
npx cookie-sentinel https://app.example.com --crawl-depth 2 --max-pages 40   # follow same-origin links
npx cookie-sentinel https://app.example.com/account -H "Cookie: session=…"   # audit logged-in pages
npx cookie-sentinel https://app.example.com --headers-file auth.json --cookies-file jar.txt
npx cookie-sentinel https://app.example.com --browser playwright      # optional peer dep
COOKIE_SENTINEL_BEARER=… npx cookie-sentinel https://api.example.com/me
curl -si https://example.com | npx cookie-sentinel --input -              # offline
npx cookie-sentinel --input set-cookies.txt --origin https://app.example.com -f sarif > cookies.sarif

Options

OptionDescription
<url…>URLs to fetch (https:// assumed if no scheme). Redirects are followed and cookies from every hop are audited
-p, --path <path>Extra path to fetch on every URL (repeatable)
--crawl <file>File of URLs or paths, one per line (# comments allowed)
--crawl-depth <n>Follow same-origin links up to depth n (0 = off)
--max-pages <n>Cap when crawling (default 25)
-H, --header "Name: value"Request header (repeatable), e.g. Cookie or Authorization
--headers-file <file>JSON headers for scripted auth
--cookies-file <file>Cookie jar file (JSON, name=value lines, or Netscape format)
--bearer <token>Authorization: Bearer shortcut (or COOKIE_SENTINEL_BEARER)
--browser <engine>playwright or puppeteer (optional peer dependencies)
-i, --input <file|->Audit raw Set-Cookie lines offline
--origin <url>URL context for --input (default https://example.com/)
-c, --config <file>JSON config (default .cookie-sentinel.json if present)
-f, --formattext (default), json, sarif, markdown
--fail-on <sev>critical, high (default), medium, low, info or none
--no-classifySkip Open Cookie Database enrichment

Exit codes: 0 passed, 1 findings at or above --fail-on, 2 usage error or every target failed to fetch.

Configuration

.cookie-sentinel.json:

{
  "failOn": "medium",
  "urls": ["https://app.example.com"],
  "paths": ["/login", "/account"],
  "headers": { "Accept-Language": "en" },
  "headersFile": "staging-headers.json",
  "cookiesFile": "staging-cookies.txt",
  "login": {
    "url": "https://app.example.com/api/login",
    "method": "POST",
    "headers": { "Content-Type": "application/json" },
    "body": "{\"user\":\"ci\",\"pass\":\"…\"}",
    "expectStatus": 200
  },
  "crawl": { "maxDepth": 2, "maxPages": 30 },
  "browser": { "engine": "playwright" },
  "sessionPatterns": ["^acme_u$"],
  "severity": { "broad-path": "off" },
  "allow": [{ "rule": "*", "cookie": "_ga*", "reason": "analytics allowlist" }]
}

GitHub Action

permissions:
  contents: read
  security-events: write
jobs:
  cookies:
    runs-on: ubuntu-latest
    steps:
      - uses: MrLouisWright/cookie-sentinel@<sha>
        with:
          urls: https://staging.example.com
          paths: /login /account
          bearer: ${{ secrets.STAGING_TOKEN }}
          fail-on: medium
          args: --crawl-depth 1 --max-pages 20
          sarif: cookies.sarif

Releases are published to npm with provenance (see release workflow).

Library

ESM and CommonJS:

import { auditUrls, auditSetCookies, formatText, classifyCookie } from 'cookie-sentinel';
// const { auditUrls } = require('cookie-sentinel'); // Node 18+

const report = await auditUrls(['https://example.com'], {
  paths: ['/login'],
  crawl: { maxDepth: 1, maxPages: 10 },
  headers: { Cookie: 'sid=…' },
});
console.log(formatText(report, { failOn: 'high' }));
console.log(report.classificationSummary);

Build types locally: npm run build (generates dist/index.cjs and dist/*.d.ts).

Support

If this project is useful to you, you can support its development with a donation: Buy me a coffee. It helps keep these tools maintained, documented and free for everyone.

Credit (optional)

Free to use under MIT, no attribution required. If cookie-sentinel helps you, a link back is appreciated:

<a href="https://louiswright.co.uk">Built with cookie-sentinel by Louis Wright</a>
[![Built with cookie-sentinel by Louis Wright](https://img.shields.io/badge/built%20with-cookie--sentinel-blue)](https://louiswright.co.uk)

License

MIT © Louis Wright (Louis Wright)