repo-keeper v0.1.0-beta.7 GitHub

Threat model (STRIDE-lite)

Assets: provider credentials; integrity of the user's local repos (unpushed work!); local filesystem; the user's provider accounts and quotas; release artifacts.
Trust boundaries: (1) user ↔ local UI/CLI; (2) process ↔ provider APIs (network); (3) process ↔ git subprocess ↔ untrusted repo content; (4) project ↔ users (supply chain).
Attackers: malicious website in the user's browser; other local user/process; malicious repo/remote; network attacker; compromised dependency/CI.

# Threat Boundary Mitigation Req
T1 Malicious webpage calls the local UI API (CSRF, DNS rebinding) 1 Loopback/unix socket, random session token in HttpOnly SameSite=Strict cookie, CSRF token, Host+Origin allow-list, no CORS, JSON content-type enforced SEC-1
T2 Other local user reads/uses UI port or config 1 Runtime file 0600; one-time login URL; token/cookie auth on TCP; optional unix socket 0600; config 0600; per-user dirs SEC-1
T3 Token leakage via logs, argv, URLs, shared env, crash dumps, config 2,3 Keychain only; secrets.Token type with redacting String()/MarshalJSON; per-invocation askpass helper (secret only in that git child's env, helper refuses other hosts); log redaction tests with canary tokens; core dumps disabled; generic git accounts refuse URLs with embedded passwords and never offer one token to more than one https host SEC-2
T4 Token over-scoped → blast radius 2 Read-only defaults, scope check on CheckAuth, UI warning SEC-4
T5 MITM on API/git 2 TLS verify always on; no skip-verify; optional custom CA only SEC-3
T6 Malicious repo executes code via git (hooks, core.fsmonitor, core.sshCommand, diff.external, filters, submodule update=!cmd) 3 Run git with -c core.hooksPath=<empty> -c core.fsmonitor=false -c protocol.ext.allow=never -c protocol.file.allow=never, GIT_CONFIG_NOSYSTEM-style isolation for our invocations, never run checkout/merge with filters from untrusted config where avoidable, no recursive submodule update, no post-* execution SEC-5
T7 Path traversal / symlink escape via repo/owner names 3 Validate names [A-Za-z0-9._-], reject .., resolve and check clone path stays under root, Windows reserved names handled SEC-6
T8 Data loss: cleanup deletes unmerged work; FF overwrites changes 3 Fail-closed predicate, FF-only, dirty-tree skip, dry-run default, trash refs + restore, repo lock, property tests FR-C*
T9 Abuse of provider (accidental DoS / ban) 2 Rate limiter, floors, jitter, circuit breaker, serial per host FR-R*, CMP-*
T10 Compromised dependency / build 4 Pinned + verified modules (go.sum), minimal deps, Dependabot/Renovate, govulncheck, CodeQL, SLSA provenance, cosign, 2FA + signed commits, branch protection, reviewed CI actions pinned by SHA SEC-7/8
T11 Malicious update / fake installer 4 No automated update path: apply exists only as the explicit update --apply command, only for a tarball install, verifying cosign signature (pinned release-workflow identity) and checksum before the atomic swap, with rollback; checks only notify (on by default, opt-out; they send nothing but the standard User-Agent); package-manager distribution otherwise; documented verify steps; an architecture test (internal/update/isolation_test.go) keeps the apply code unreachable from the daemon/UI/tray; built as described in ADR-0022 SEC-8, ADR-0021
T12 SQLite/config tampering by local malware 1 Treated as same-privilege attacker (out of scope), but config is validated strictly and never executes strings (no shell interpolation) –
T13 Hooks/post-sync command abuse 1 Feature off by default, config-file-only (not UI-editable), documented risk FR-S10
T14 Resource exhaustion by huge repos 3 Size caps, timeouts, bounded workers, disk-space precheck NFR-1
T15 Supply of false-positive AV flags causing user distrust 4 See DISTRIBUTION –

Out of scope

Fully compromised user account/OS; physical access; provider-side vulnerabilities.

Secure development practices