Skip to content

SECURITY: harden local interfaces before browser actions - #52

Merged
merefield merged 3 commits into
mainfrom
security/local-interface-hardening
Sep 12, 2026
Merged

SECURITY: harden local interfaces before browser actions#52
merefield merged 3 commits into
mainfrom
security/local-interface-hardening

Conversation

@merefield

Copy link
Copy Markdown
Owner

Why

Strengthen the local terminal/browser security boundary before introducing browser approvals or prompt sending, without adding startup checks, authentication prompts, runtime services or dependency churn.

Changes

  • CI-only vulnerability checks: new read-only-permission Security workflow on PRs, pushes to main, weekly and manual dispatch. Pin govulncheck v1.8.0; scan the native Linux build using the Go version in go.mod with toolchain auto-switching disabled. Separately audit npm runtime dependencies and the complete lockfile including build tooling; high/critical advisories block, lower severities remain visible. No automatic dependency upgrades or package lifecycle scripts in the audit job.
  • Web HTTP hardening: retain existing loopback, Host/Origin/Fetch Metadata, pairing and bearer validation. Add a 15-second ordinary-response write timeout; SSE continues renewing its own five-second write deadline. Add same-origin resource policy and deny unused camera/microphone/geolocation/payment/USB features. No rate limiter or global failed-attempt lockout that could deny normal users.
  • Security regressions: both data endpoints reject missing/wrong/expired/query/cookie credentials and hostile origins/hosts without reserving stream slots or leaking tokens. Invalid pairing leaves the legitimate secret usable. Unsupported methods/mutation routes retain security headers. A real HTTP streaming test verifies SSE survives the ordinary-response timeout.
  • Terminal hardening: reuse existing context sanitisation for directory labels, short session IDs and source/thread metadata before styling/wrapping. Preserve original routing IDs, paths and command payloads. Short IDs now truncate at rune boundaries instead of splitting UTF-8. Existing reply/command sanitisation stays in place.
  • Rendering and fuzz coverage: synthetic malicious names, IDs, context headings, replies and commands remain inert browser text with no dialogs or external requests. Terminal tests cover OSC clipboard/hyperlink commands, screen control, bidi formatting and valid multilingual labels. Add sanitizer fuzz seeds/properties.
  • Documentation: SECURITY.md explains the threat model, developer checklist, limits, scan scope, triage, remaining risks and local reproduction commands; README links to it.

Deliberately unchanged

No browser write endpoints or approval permissions. No terminal/web launch changes, extra pairing steps, local certificates, authentication service or application-startup scanners. No Go/npm dependency version changes or frontend production asset changes. This is not complete protection against malicious local processes, browser compromise or denial of service. The Go vulnerability CI scan is Linux-native, not an exhaustive Windows/macOS-only dependency analysis.

Validation

  • Full go test -race -cover ./... passed (UI 91.2%, web 95.4%), including unchanged English presentation snapshots and new security regressions.
  • go vet ./..., gofmt and git diff --check passed.
  • make -j4 web-test passed: frontend check/build, embedded Go binary build and all 20 frontend tests. Production assets remain identical to main.
  • GOTOOLCHAIN=go1.26.6 go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...: no vulnerabilities found.
  • Both npm audit commands: zero vulnerabilities reported.
  • Short two-worker sanitizer fuzz run: 50,413 executions, passed.
  • Local binary rebuilt after committing. No release/version bump in this hardening PR.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The follow-up composer still renders an unsanitized raw thread ID, leaving a terminal-control injection path.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Hardens local web and terminal interfaces while adding security-focused CI, tests, and documentation.

Changes:

  • Adds vulnerability scanning and HTTP security controls.
  • Sanitizes terminal metadata and preserves Unicode IDs.
  • Expands web, terminal, and fuzz security coverage.
File summaries
File Description
.github/workflows/security.yml Adds Go and npm vulnerability scans.
SECURITY.md Documents the local security model.
README.md Links to security guidance.
internal/web/server.go Adds headers and write timeout.
internal/web/security_test.go Tests authentication, headers, and SSE timeout.
web/tests/browser.spec.ts Tests inert rendering of malicious content.
internal/ui/terminal_text.go Adds terminal label sanitization.
internal/ui/terminal_text_test.go Tests terminal-control removal.
internal/ui/monitor.go Sanitizes paths and Unicode-safe IDs.
internal/ui/monitor_detail.go Sanitizes detailed metadata.
internal/ui/monitor_context.go Sanitizes compact metadata.
internal/ui/monitor_context_modes.go Sanitizes expanded metadata.
internal/codex/session_context_security_test.go Adds sanitizer fuzz properties.
Review details
  • Files reviewed: 13/13 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/ui/terminal_text.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Security regression tests do not fully lock down the added permission directives, timeout values, and command sanitization path.

Review details

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

internal/ui/terminal_text_test.go:35

  • The malicious CommandDetails value is never rendered because contextTestModel leaves this preview as SessionContextReply, while command details are displayed only for approvals. Consequently, removing command sanitization would not fail this test; append an approval-detail render while retaining the reply render that exercises the composer.
    internal/web/security_test.go:21
  • The security-header helper only checks camera=(), so deleting the newly promised microphone, geolocation, payment, or USB restrictions would leave this regression suite green. Assert the complete Permissions-Policy value so every added restriction is protected.

This issue also appears on line 102 of the same file.

internal/web/security_test.go:102

  • This check only proves each timeout is nonzero, not the documented 5s/10s/15s/30s security bounds. A change to an ineffective duration (or an ordinary write timeout that is too short) would still pass; assert the configured values exactly.
	if cfg.ReadHeaderTimeout <= 0 || cfg.ReadTimeout <= 0 || cfg.WriteTimeout <= 0 || cfg.IdleTimeout <= 0 || cfg.MaxHeaderBytes != 8192 {
  • Files reviewed: 14/14 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

The hardening changes preserve existing security boundaries and include comprehensive regression coverage.

Review details
  • Files reviewed: 14/14 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@merefield
merefield merged commit 6928a78 into main Sep 12, 2026
7 checks passed
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.

2 participants