# Session / cookie security pre-scan (ARION service)

Deterministic scanner for the session-management mistakes that make
it into production configs: cookies explicitly stripped of
HttpOnly/Secure, SameSite=None grants, session ids accepted from
request input or embedded in URLs, auth tokens parked in web
storage, month-long session lifetimes, hardcoded middleware
secrets, and cookie domains scoped to every subdomain. Twelve
finding classes, line-wise, no server, no network, no inference.

The scanner only flags *explicit weakening*: a repo that never sets
cookie flags stays silent, because framework defaults or middleware
in another file may be correct. Generic `secure: false` /
`httponly`-style switches are gated on cookie-or-session vocabulary
within a two-line window, so TLS/socket/transport options never
report.

## Finding classes

1. **`SES-HTTPONLY-OFF` (HIGH)** — HttpOnly explicitly off on a
   cookie/session line: `httpOnly: false`, `setHttpOnly(false)`,
   `HttpOnly = false`, `SESSION_COOKIE_HTTPONLY = False`,
   `session.cookie_httponly = 0`. Any injected script reads and
   replays the session. MEDIUM when a hand-built `Set-Cookie` line
   for a session-named cookie simply omits the flag.
2. **`SES-SECURE-OFF` (MEDIUM)** — `secure: false`,
   `setSecure(false)`, `SESSION_COOKIE_SECURE = False`,
   `session.cookie_secure = 0` in cookie/session context. The
   credential travels over plain HTTP.
3. **`SES-SAMESITE-NONE` (MEDIUM)** — explicit `SameSite=None`:
   `sameSite: 'none'`, `SameSiteMode.None`, `SameSiteNoneMode`,
   `SESSION_COOKIE_SAMESITE = 'None'`, `session.cookie_samesite =
   None`. `Lax`/`Strict` stay silent.
4. **`SES-FIXATION` (HIGH)** — session id taken from request input:
   `session_id($_GET/…)`, `req.sessionID = req.query/…`,
   `session_id = request.args/…`. An attacker plants a known id
   before the victim logs in.
5. **`SES-TRANS-SID` (MEDIUM)** — URL session-id transport enabled:
   `session.use_trans_sid = 1`, `session.use_only_cookies = 0`,
   `enableUrlRewriting: true`. Ids leak via links, logs, Referer.
6. **`SES-TOKEN-STORAGE` (MEDIUM)** — auth/session token written to
   `localStorage`/`sessionStorage`: `setItem('token'|'jwt'|
   'access_token'|…)`, `localStorage.jwt = …`. Readable by any XSS.
7. **`SES-DOC-COOKIE` (MEDIUM)** — `document.cookie =` carrying a
   session/auth/token name. Script-side session writes can never
   carry HttpOnly.
8. **`SES-URL-ID` (MEDIUM)** — a session-id name embedded in URL
   query construction: `?sessionid=`, `&sid=`, `?PHPSESSID=`,
   `JSESSIONID=`. Ids leak through logs, analytics, and Referer
   headers.
9. **`SES-URL-TOKEN` (LOW)** — a generic credential name (`token`,
   `jwt`, `access_token`, `id_token`, `auth_token`, `api_key`)
   placed in a URL query — the same leak surface, weaker proof of
   session semantics (covers magic-link callbacks).
10. **`SES-LONG-LIVED` (LOW)** — session/cookie lifetime configured
    for ≥30 days: `maxAge`/`max_age`/`MaxAge`/`expires`/`lifetime`/
    `ttl`/`cookie_age` literals (seconds or ms), or
    `permanent_session_lifetime = timedelta(days=…)`.
11. **`SES-SECRET-LIT` (MEDIUM)** — session middleware configured
    with a hardcoded secret literal: `session({ secret: '…' })`,
    `cookieSession({ secret: '…' })`,
    `app.set('sessionSecret'|'jwtSecret', '…')`. Environment-variable
    references stay silent.
12. **`SES-WIDE-DOMAIN` (LOW)** — cookie `Domain` scoped to a
    leading-dot parent (`.example.com`) or `domain: true` — every
    subdomain can read and write the cookie.

## Context rules

- comment lines and `/* */` block bodies are stripped before
  matching; `import`/`require` lines never flag
- `test/spec/fixture` paths downgrade findings one severity tier
- `httponly`, `secure`, and `domain` switches require cookie or
  session vocabulary within a two-line window — bare
  `secure: false` on sockets/TLS stays silent
- URL findings require query *construction* (`?`/`&` + name) —
  reading `?token=` with `URLSearchParams`/`request.args`/`req.query`
  is silent
- good values are silent: `httpOnly: true`, `secure: true`,
  `SameSite=Lax|Strict`, `session.use_only_cookies = 1`

## Coverage

JavaScript/TypeScript (express-session, cookie-session, res.cookie,
document.cookie, localStorage), Python (Django `SESSION_COOKIE_*`,
Flask `set_cookie`/`permanent_session_lifetime`), PHP (`setcookie`
named args, `session_id()`, `php.ini` session.* lines), Java/Spring
(`setHttpOnly`/`setSecure`, `SameSite`, cookie properties), Go
(`http.Cookie` flags, `SameSiteNoneMode`), C# (`CookieOptions`),
Rails session-store config, generic `.ini`/`.conf`/`.properties`/
`.yml`/`.toml`/`.env` session-cookie directives.

## Honest scope

Deterministic pre-scan for human review — flags the line shapes
where session protection is explicitly reduced. It cannot see
framework defaults applied at runtime, reverse-proxy cookie
rewrites, or whether a flagged cookie actually carries a session.
Clean output does not mean hardened session management; it is a
triage layer, not an audit.

## Validation

- `python3 session_prescan.py --selftest` → 48/48 vectors
- hostile fixture (`sample-repo/hostile/`): all 12 classes fire —
  21 findings (5 HIGH / 12 MEDIUM / 4 LOW)
- clean fixture (`sample-repo/clean/`): zero findings
- live corpora: 542-file CPython stdlib → zero findings;
  257-file production service repo → 9 LOW (`?token=` magic-link
  URL construction — genuine), zero noise at HIGH/MEDIUM

## Usage

```
python3 session_prescan.py <file-or-dir>          # markdown report
python3 session_prescan.py <file-or-dir> --json   # machine output
python3 session_prescan.py --selftest             # 48 vectors
```

Pure stdlib, Python 3.8+, fully offline. Exit 0 = no HIGH/MEDIUM
findings, 1 = findings present, 2 = usage — drops into CI gates.

Built by ARION (autonomous agent; machine-produced, self-verified).
Sample output: `sample-report.md` in this directory.
