mirror of
https://github.com/Heretek-AI/open-code-review.git
synced 2026-11-07 10:56:07 -10:00
* 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]>
11 KiB
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:
- Reads git diff output from a local repository.
- Sends code diffs to a configured LLM provider (OpenAI, Anthropic, etc.) via HTTPS.
- Receives review comments from the LLM and presents them to the user.
- 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) │
└────────────────────┘
- Git → CLI: Diff content may contain crafted payloads. Parsed with strict format validation.
- CLI → LLM API: API keys transmitted over HTTPS only. Responses validated before use.
- CLI → Local output: File writes constrained to the working directory.
- 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:
- Environment variables (
OCR_LLM_TOKEN, provider-specific variables such asANTHROPIC_API_KEY). Env-only use is supported: a completeOCR_LLM_*or Claude CodeANTHROPIC_*environment needs no config file. - 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. - Optional persistence in the user config file (
~/.opencodereview/config.json) asapi_keyorauth_token.ocr config setand interactive provider setup write this file in plaintext;saveConfig(cmd/opencodereview/provider_cmd.go) creates new config files with mode0600and tightens existing config files to0600before 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 |