fix: security audit remediation — 12 fixes, 20 tests (v0.13.1.0) (#595)

* fix: remove auth token from /health, secure extension bootstrap (CRITICAL-02 + HIGH-03)

- Remove token from /health response (was leaked to any localhost process)
- Write .auth.json to extension dir for Manifest V3 bootstrap
- sidebar-agent reads token from state file via BROWSE_STATE_FILE env var
- Remove getToken handler from extension (token via health broadcast)
- Extension loads token before first health poll to prevent race condition

* fix: require auth on cookie-picker data routes (CRITICAL-01)

- Add Bearer token auth gate on all /cookie-picker/* data/action routes
- GET /cookie-picker HTML page stays unauthenticated (UI shell)
- Token embedded in served HTML for picker's fetch calls
- CORS preflight now allows Authorization header

* fix: add state file TTL and plaintext cookie warning (HIGH-02)

- Add savedAt timestamp to state save output
- Warn on load if state file older than 7 days
- Auto-delete stale state files (>7 days) on server startup
- Warning about plaintext cookie storage in save message

* fix: innerHTML XSS in extension content script and sidepanel (MEDIUM-01)

- content.js: replace innerHTML with createElement/textContent for ref panel
- sidepanel.js: escape entry.command with escapeHtml() in activity feed
- Both found by security audit + Codex adversarial red team

* fix: symlink bypass in validateReadPath (MEDIUM-02)

- Always resolve to absolute path first (fixes relative path bypass)
- Use realpathSync to follow symlinks before boundary check
- Throw on non-ENOENT realpathSync failures (explicit over silent)
- Resolve SAFE_DIRECTORIES through realpathSync (macOS /tmp → /private/tmp)
- Resolve directory part for non-existent files (ENOENT with symlinked parent)

* fix: freeze hook symlink bypass and prefix collision (MEDIUM-03)

- Add POSIX-portable path resolution (cd + pwd -P, works on macOS)
- Fix prefix collision: /project-evil no longer matches /project freeze dir
- Use trailing slash in boundary check to require directory boundary

* fix: shell script injection in gstack-config and telemetry (MEDIUM-04)

- gstack-config: validate keys (alphanumeric+underscore only)
- gstack-config: use grep -F (fixed string) instead of -E (regex)
- gstack-config: escape sed special chars in values, drop newlines
- gstack-telemetry-log: sanitize REPO_SLUG and BRANCH via json_safe()

* test: 20 security tests for audit remediation

- server-auth: verify token removed from /health, auth on /refs, /activity/*
- cookie-picker: auth required on data routes, HTML page unauthenticated
- path-validation: symlink bypass blocked, realpathSync failure throws
- gstack-config: regex key rejected, sed special chars preserved
- state-ttl: savedAt timestamp, 7-day TTL warning
- telemetry: branch/repo with quotes don't corrupt JSON
- adversarial: sidepanel escapes entry.command, freeze prefix collision

* chore: bump version and changelog (v0.13.1.0)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: tone down changelog — defense in depth, not catastrophic bugs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-03-28 08:35:24 -06:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 78bc1d1968
commit 7450b5160b
24 changed files with 489 additions and 67 deletions
+19
View File
@@ -1,5 +1,24 @@
# Changelog
## [0.13.1.0] - 2026-03-28 — Defense in Depth
The browse server runs on localhost and requires a token for access, so these issues only matter if a malicious process is already running on your machine (e.g., a compromised npm postinstall script). This release hardens the attack surface so that even in that scenario, the damage is contained.
### Fixed
- **Auth token removed from `/health` endpoint.** Token now distributed via `.auth.json` file (0o600 permissions) instead of an unauthenticated HTTP response.
- **Cookie picker data routes now require Bearer auth.** The HTML picker page is still open (it's the UI shell), but all data and action endpoints check the token.
- **CORS tightened on `/refs` and `/activity/*`.** Removed wildcard origin header so websites can't read browse activity cross-origin.
- **State files auto-expire after 7 days.** Cookie state files now include a timestamp and warn on load if stale. Server startup cleans up files older than 7 days.
- **Extension uses `textContent` instead of `innerHTML`.** Prevents DOM injection if server-provided data ever contained markup. Standard defense-in-depth for browser extensions.
- **Path validation resolves symlinks before boundary checks.** `validateReadPath` now calls `realpathSync` and handles macOS `/tmp` symlink correctly.
- **Freeze hook uses portable path resolution.** POSIX-compatible (works on macOS without coreutils), fixes edge case where `/project-evil` could match a freeze boundary set to `/project`.
- **Shell config scripts validate input.** `gstack-config` rejects regex-special keys and escapes sed patterns. `gstack-telemetry-log` sanitizes branch/repo names in JSON output.
### Added
- 20 regression tests covering all hardening changes.
## [0.13.0.0] - 2026-03-27 — Your Agent Can Design Now
gstack can generate real UI mockups. Not ASCII art, not text descriptions of hex codes, real visual designs you can look at, compare, pick from, and iterate on. Run `/office-hours` on a UI idea and you'll get 3 visual concepts in Chrome with a comparison board where you pick your favorite, rate the others, and tell the agent what to change.