Skip to content

Security/cookie auth hardening#603

Open
zvrr wants to merge 3 commits intojackwener:mainfrom
zvrr:security/cookie-auth-hardening
Open

Security/cookie auth hardening#603
zvrr wants to merge 3 commits intojackwener:mainfrom
zvrr:security/cookie-auth-hardening

Conversation

@zvrr
Copy link
Copy Markdown

@zvrr zvrr commented Mar 30, 2026

Description

Related issue:

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 🌐 New site adapter
  • 📝 Documentation
  • ♻️ Refactor
  • 🔧 CI / build / tooling

Checklist

  • I ran the checks relevant to this PR
  • I updated tests or docs if needed
  • I included output or screenshots when useful

Documentation (if adding/modifying an adapter)

  • Added doc page under docs/adapters/ (if new adapter)
  • Updated docs/adapters/index.md table (if new adapter)
  • Updated sidebar in docs/.vitepress/config.mts (if new adapter)
  • Updated README.md / README.zh-CN.md when command discoverability changed
  • Used positional args for the command's primary subject unless a named flag is clearly better
  • Normalized expected adapter failures to CliError subclasses instead of raw Error

Screenshots / Output

zz and others added 3 commits March 30, 2026 16:51
Addresses five security issues identified in code audit:

1. Daemon Bearer-Token Authentication
   - Generate crypto.randomBytes(32) token at startup
   - Write to ~/.opencli/daemon.token (mode 0o600)
   - Require Authorization: Bearer <token> on all non-/ping endpoints
   - Constant-time comparison prevents timing side-channels
   - daemon-client.ts lazily reads & caches token; auto-refreshes on 401
   - Token file deleted on daemon shutdown

2. Extension ID Pinning (optional hardening)
   - New OPENCLI_EXTENSION_ID env var for strict WebSocket origin check
   - Rejects any chrome-extension:// origin that doesn't match the pin

3. HttpOnly Cookie Warning + Redaction
   - OPENCLI_VERBOSE=1 emits warning when httpOnly cookies are returned
   - OPENCLI_REDACT_COOKIES=1 or getCookies({ redact: true }) replaces
     httpOnly and sensitive-named cookie values with '[REDACTED]'
   - New isSensitiveCookieName() / redactCookies() helpers in types.ts

4. CDP Endpoint Localhost Enforcement
   - assertLocalhostEndpoint() blocks non-loopback OPENCLI_CDP_ENDPOINT
   - Override with OPENCLI_CDP_ALLOW_REMOTE=1 for advanced use cases

5. Pipeline fetch credentials:"include" risk comment
   - Documents CSRF risk and required URL trust requirement

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@Astro-Han Astro-Han left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security hardening looks well thought out — token lifecycle (generate → 0o600 write → constant-time verify → cleanup on shutdown), 401 retry in daemon-client, and CDP localhost guard all make sense. Test coverage is good too.

Two observations:

  1. The xiaohongshu blob-URL video download fix is a separate concern from the security changes. Might be cleaner as its own PR so each can be reviewed/reverted independently.

  2. isSensitiveCookieName uses substring matching (lower.includes(s)), which casts a wide net — e.g. a cookie named authenticate_preference would match on auth. For security redaction that's probably the right tradeoff (better to over-redact), but a brief comment noting the intentional broad matching would help future readers.

@Astro-Han
Copy link
Copy Markdown
Contributor

Astro-Han commented Apr 2, 2026

Another concrete risk here: CDPBridge.connect() now creates and attaches a target for browser-level CDP endpoints, but that whole block still sits inside the broad try/catch in src/browser/cdp.ts. If Target.createTarget or Target.attachToTarget fails, the exception is swallowed and connect() still resolves a CDPPage.

That means callers can get a seemingly successful connection even though no target session was established, and the first real browser command fails later with a much less obvious error. I think target attach failure should still reject connect(), with only the post-attach niceties like Page.enable staying best-effort.

Another issue in the auth hardening path: src/daemon.ts treats token-file persistence failure as non-fatal (could not write token file), but the daemon still requires Authorization: Bearer <token> on every non-/ping HTTP request. On the client side, src/browser/daemon-client.ts can only discover that token by reading ~/.opencli/daemon.token; if the file is missing or unreadable it simply omits the header.

So a daemon that starts with an in-memory token but fails to persist the file becomes unusable to the CLI: every /status and /command request will get 401, with no real fallback path. I think token-file write failure either needs to be fatal at startup, or the daemon needs an actual compatibility mode instead of logging "non-fatal" and proceeding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants