Skip to content

Add Set-Cookie attribute analysis (Secure / HttpOnly / SameSite) — a standard header-scanner check this tool doesn't do #95

Description

@dmchaledev

Problem

src/rules.ts checks 7 categories (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Cross-Origin policies) but never inspects Set-Cookie. grep -ri "set-cookie" src/ returns nothing — there is no rule, no test, and no README mention of cookie attributes at all.

Every comparable tool in this space checks this:

  • securityheaders.com flags missing Secure/HttpOnly/SameSite on session-looking cookies.
  • Mozilla HTTP Observatory has a dedicated Cookie test.
  • OWASP's Secure Headers project treats cookie flags as part of the same "response header hardening" surface as CSP/HSTS.

Concretely, today:

import { analyzeHeaders } from '@hailbytes/security-headers';

const r = analyzeHeaders({
  'strict-transport-security': 'max-age=31536000; includeSubDomains; preload',
  'content-security-policy': "default-src 'self'; form-action 'self'; base-uri 'self'",
  'x-frame-options': 'DENY',
  'x-content-type-options': 'nosniff',
  'referrer-policy': 'strict-origin-when-cross-origin',
  'permissions-policy': 'camera=(), microphone=(), geolocation=()',
  'set-cookie': 'sessionid=abc123; Path=/', // no Secure, no HttpOnly, no SameSite
});

console.log(r.grade); // A+ — with a session cookie wide open to theft via XSS and CSRF

A site can score A+ while shipping a session cookie with none of Secure, HttpOnly, or SameSite set — i.e. readable by any injected script, replayable over plain HTTP, and attached to cross-site requests. The tool's own stated purpose ("analyze HTTP security headers... A-F grading... remediation steps") implies exactly this class of misconfiguration should be caught, and currently isn't.

Why this is high-leverage

  • It's a well-scoped, self-contained addition (new checkSetCookie in rules.ts, wired into analyzer.ts, no changes to fetch.ts/cli.ts plumbing) — same shape as the existing 7 rules.
  • It closes a real blind spot rather than refining an existing one: right now this tool can hand out a top grade to a session-hijack-friendly response, which undercuts the "A-F grade reflects your security posture" promise the README makes.
  • fetchHeaders (src/fetch.ts:15-16) already builds a flat Record<string, string> via res.headers.forEach. Multiple Set-Cookie headers get silently collapsed by the underlying Headers object's iteration (they're normally combined or only the last one wins depending on the fetch implementation) — worth confirming/handling as part of this work, since a page setting several cookies should have each one evaluated independently.

Proposed approach

  1. In src/rules.ts, add checkSetCookie(headers):
    • Parse the Set-Cookie value(s) for Secure, HttpOnly, and SameSite=Strict|Lax (flag SameSite=None without Secure as broken, per spec — browsers reject that combination).
    • Missing header entirely → treat as N/A (status: 'good', 0 findings) rather than missing, since not every response sets cookies and penalizing absence would be a false positive.
    • Present but missing Secure and/or HttpOnly and/or a safe SameSitewarning with one finding + remediation per missing attribute.
    • If multiple cookies are present, evaluate each and roll up the worst-case findings (don't just check the first).
  2. Wire it into analyzer.ts's checks array and give it a maxScore (10 seems consistent with X-Content-Type-Options/Referrer-Policy/Permissions-Policy). Since grading is percentage-based (score/maxScore*100), adding a category doesn't require rebalancing existing weights, but it does mean:
    • The maxScore is 100 test in test/analyzer.test.ts needs updating to the new total.
    • The hand-computed grade-boundary tests (describe('grade boundaries', ...)) need their header fixtures/comments updated since they currently assume a 100-point total.
  3. Update the README's "Headers Checked" table with the new row and grading example.
  4. Add unit tests in test/analyzer.test.ts mirroring the existing per-rule describe blocks: no cookie → good/N/A, cookie missing all three attributes → warning with 3 findings, cookie with all three set correctly → good, SameSite=None without Secure → flagged as broken.

Acceptance criteria

  • checkSetCookie exists in src/rules.ts and is included in analyzeHeaders's checks
  • A response with no Set-Cookie header does not lose points (no false-positive missing)
  • A cookie missing Secure/HttpOnly/SameSite produces distinct findings + remediations for each missing attribute
  • SameSite=None without Secure is flagged as an invalid/broken combination
  • Multiple Set-Cookie values (if present) are each evaluated, not just the first
  • README "Headers Checked" table and grading example updated
  • Tests added; maxScore/grade-boundary tests updated to the new total

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions