Files
Frank_zhu 24fe63417d docs(assurance): correct API key storage claims (#1449)
* docs(assurance): correct API key storage claims

ASSURANCE_CASE.md said keys are env-only and never stored in config
files. The CLI persists api_key/auth_token to ~/.opencodereview/config.json
with mode 0600. Align the assurance case with that behavior.

Fixes #1440

* fix(config): enforce 0600 on existing config.json after save

WriteFile only applies mode on create; keep os.Chmod(path, 0o600) after
every saveConfig write so a prior 0644 config is tightened when an API
key is persisted. Document that behavior in ASSURANCE_CASE and add a
regression test for the existing-file case.

Signed-off-by: Frank_zhu <[email protected]>

* fix(config): chmod existing config to 0600 before rewrite (#1440)

* fix(config): align saveConfig chmod with review suggestion (#1440)

* test(config): chmod seed to 0644 independent of umask (#1440)

WriteFile mode is masked by umask 077; set seed permissions explicitly
so TestSaveConfigEnforcesModeOnExistingFile stays umask-independent.

Signed-off-by: Frank_zhu <[email protected]>

---------

Signed-off-by: Frank_zhu <[email protected]>
2026-09-21 19:32:21 +08:00

11 KiB

Security Assurance Case

This document provides a security assurance case for Open Code Review (OCR), justifying that security requirements are met through secure design principles and countermeasures against common implementation weaknesses.

Threat Model

System Description

OCR is a CLI tool that:

  1. Reads git diff output from a local repository.
  2. Sends code diffs to a configured LLM provider (OpenAI, Anthropic, etc.) via HTTPS.
  3. Receives review comments from the LLM and presents them to the user.
  4. Optionally serves a local web viewer for browsing review session history.

Actors

Actor Trust Level
Local user Trusted — invokes the CLI with full control over configuration
LLM provider API Semi-trusted — responses are validated before use
Git repository Semi-trusted — diffs may contain adversarial content
Network Untrusted — all communication uses TLS
Web browser (viewer) Untrusted — may be exploited via DNS rebinding

Trust Boundaries

┌──────────────────────────────────────────────────┐
│  User Machine (Trusted Zone)                     │
│                                                  │
│  ┌──────────┐    ┌──────────┐    ┌────────────┐  │
│  │ Git Repo │───▶│ OCR CLI  │───▶│ Local File │  │
│  │ (diffs)  │    │ (core)   │    │  (output)  │  │
│  └──────────┘    └────┬─────┘    └────────────┘  │
│        Trust ────────▶│◀──────── Trust            │
│        Boundary 1     │         Boundary 3        │
│                       │                           │
│                  ┌────┴─────┐                     │
│                  │  Viewer  │◀── Trust Boundary 4  │
│                  │ (HTTP)   │    (Browser access)  │
│                  └──────────┘                     │
└───────────────────────┼───────────────────────────┘
           Trust ──────▶│◀────── Boundary 2
                        │
              ┌─────────┴──────────┐
              │  LLM Provider API  │
              │  (HTTPS only)      │
              └────────────────────┘
  1. Git → CLI: Diff content may contain crafted payloads. Parsed with strict format validation.
  2. CLI → LLM API: API keys transmitted over HTTPS only. Responses validated before use.
  3. CLI → Local output: File writes constrained to the working directory.
  4. Browser → Viewer: Host-header allowlist enforces access control; blocks DNS rebinding.

Threat Summary

ID Threat Boundary Mitigation
T1 Command injection via crafted diff content 1 All external commands are git only, with hardcoded subcommands; no shell expansion; --end-of-options used to prevent flag injection
T2 API key leakage 2 Keys may come from env vars, api_key_cmd/auth_token_cmd, or the user config file (see Credential sources). Never logged, written to review output files, or transmitted beyond the configured LLM endpoint
T3 Path traversal via LLM-suggested file paths 3 pathutil.WithinBase() validates all file paths against the repository root, both before and after symlink resolution
T4 DNS rebinding against local viewer 4 Host-header allowlist rejects requests from non-loopback origins; configurable via OCR_VIEWER_ALLOWED_HOSTS
T5 Man-in-the-middle on API communication 2 Go's net/http enforces TLS 1.2+ with full certificate verification by default; InsecureSkipVerify is never set
T6 Malicious LLM response 2 JSON schema validation on response structure; line number bounds checking against actual diff ranges
T7 Dependency vulnerabilities All govulncheck runs in CI; Dependabot monitors for updates; go.sum provides integrity verification

Credential sources

API keys are never hardcoded in the binary. They may be supplied at runtime from:

  1. Environment variables (OCR_LLM_TOKEN, provider-specific variables such as ANTHROPIC_API_KEY). Env-only use is supported: a complete OCR_LLM_* or Claude Code ANTHROPIC_* environment needs no config file.
  2. A credential command (api_key_cmd / auth_token_cmd) whose stdout is the secret. The command string may be stored in config; the secret itself is not.
  3. Optional persistence in the user config file (~/.opencodereview/config.json) as api_key or auth_token. ocr config set and interactive provider setup write this file in plaintext; saveConfig (cmd/opencodereview/provider_cmd.go) creates new config files with mode 0600 and tightens existing config files to 0600 before persisting credentials.

ocr config set masks api_key/auth_token when echoing the value. Sensitive request headers are redacted in raw logs. Keys are not written to review output files.

Secure Design Principles

The following analysis maps Saltzer & Schroeder's design principles to the project's implementation.

Principle How Applied
Least privilege CGO_ENABLED=0 eliminates C library attack surface. The CLI requires no elevated permissions. No network listeners except the opt-in viewer.
Fail-safe defaults Credentials are not compiled in; they must be supplied via environment variables, api_key_cmd/auth_token_cmd, or the user config file. The viewer binds to localhost by default; non-loopback hosts require explicit allowlisting.
Complete mediation Every viewer HTTP request is checked against the host allowlist (internal/viewer/hostguard.go). Every file path from agent tools is validated against the repository root (internal/tool/filereader.go:98, internal/pathutil/path.go).
Economy of mechanism External process execution is limited to git with hardcoded subcommands — no shell invocation, no arbitrary command execution.
Open design Fully open-source (Apache-2.0). Security relies on TLS, not obscurity.
Separation of privilege Credential material is never compiled into the binary; optional persistence is limited to the user config file, which saveConfig keeps at mode 0600 (pre-write chmod on existing files; new files created as 0600). The viewer's host guard is a distinct middleware layer.
Least common mechanism Each review session writes to its own JSONL file. No shared state between sessions.
Psychological acceptability Security defaults (HTTPS, localhost binding, host allowlist) require no user configuration. Overrides (OCR_VIEWER_ALLOWED_HOSTS) are explicit and documented.

Countermeasures Against Common Weaknesses

The following maps OWASP Top 10 and CWE/SANS Top 25 categories to the project.

Weakness Applicability Countermeasure
A03:2021 Injection (CWE-78 OS Command Injection) Every exec.Command call passes an explicit argument list; none interpolates untrusted input into a shell string. Repository access always invokes git directly, with --end-of-options to prevent flag injection. Four kinds of call site run something other than git, and all of them execute only what the local user configured, never remote or LLM-supplied input: the API-key command (internal/llm/keycmd_unix.go, keycmd_windows.go) and the agent shell tool (cmd/opencodereview/shell_unix.go, shell_windows.go) run a user-authored script through sh -c / cmd /c by design; the MCP client (internal/mcp/client.go) spawns the server command from the user's MCP config; the viewer's browser opener (internal/viewer/browser.go) runs a fixed platform command (or $BROWSER) with a URL built from the user's own --addr. Mitigated
A03:2021 Injection (CWE-79 Cross-site Scripting) Viewer template output is HTML-escaped by html/template. Defense-in-depth: every viewer response carries a strict Content-Security-Policy (default-src 'self', no unsafe-inline) plus X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy, and Permissions-Policy (internal/viewer/securityheaders.go). Mitigated
A01:2021 Broken Access Control (CWE-22 Path Traversal) Agent file-read tool validates paths with pathutil.WithinBase() before and after symlink resolution (internal/tool/filereader.go:91-112). Mitigated
A02:2021 Cryptographic Failures All API communication uses HTTPS/TLS 1.2+. Go's default TLS configuration is used without weakening. InsecureSkipVerify is never set. Mitigated
A07:2021 Auth Failures (CWE-798 Hard-coded Credentials) API keys are never hardcoded in source. They may be read from environment variables, api_key_cmd/auth_token_cmd, or the user config file (~/.opencodereview/config.json); saveConfig enforces mode 0600 on create and tightens existing config files to 0600 before persisting credentials. Never logged; ocr config set masks them on display. Mitigated
A05:2021 Security Misconfiguration Secure defaults: localhost-only viewer, HTTPS-only API calls, CGO_ENABLED=0. The go vet and govulncheck tools run in CI. Mitigated
A06:2021 Vulnerable Components (CWE-1104) Dependabot monitors Go modules and GitHub Actions. govulncheck runs on every push/PR. Dependencies are locked via go.sum. Mitigated
A08:2021 Software Integrity Release binaries include SHA-256 checksums. Release tags are cryptographically signed (SSH). CGO_ENABLED=0 produces static binaries with no external shared library dependencies. Mitigated
A09:2021 Logging Failures API keys and sensitive headers are excluded from all log output and telemetry. Mitigated
A10:2021 SSRF The CLI only makes outbound requests to user-configured LLM API endpoints. The viewer does not make outbound requests. Not applicable
CWE-416 Use After Free / CWE-787 Out-of-bounds Write Go is a memory-safe language. CGO_ENABLED=0 eliminates C memory risks. Race detector (-race) runs in CI. Not applicable (memory-safe language)

Automated Verification

Check Tool When
Static analysis go vet Every push and PR (CI)
Known vulnerability scan govulncheck Every push and PR (CI)
Data race detection go test -race Every push and PR (CI)
Dependency monitoring Dependabot Continuous
Build integrity CGO_ENABLED=0, go.sum checksums Every build