Closes CodeQL alert #4 (Uncontrolled data used in path expression).
Both `owner_did` and `repo_name` come from URL parameters and were used
unsanitized to build a filesystem path, so a request like
`/did:key:foo/../../../etc/passwd.git/info/refs` could escape the repos
directory.
Fix:
- New `validate_owner_did` and `validate_repo_name` enforce a strict
allowlist before path construction (alphanumeric + `: . _ -` for DIDs;
alphanumeric + `. _ -` for repo names; rejects empty, `..`, leading
`.` or `-`, slashes, backslashes, null bytes, overlong inputs).
- `RepoStore::local_path` now returns `Result<…>` and the 5 callers
propagate the error.
- Defence in depth: even after the allowlist passes, the joined path is
checked to still be rooted at `repos_dir`.
- 17 unit tests cover normal DIDs/repo names plus every malicious shape
(`..`, `/`, `\`, leading dot/dash, null byte, overlong, did:web with
dots).
Co-Authored-By: OpenClaude <[email protected]>
Touch alone wasn't enough — cargo can still reuse cached fingerprint
state from the dependency-cache layer. Explicitly remove the dummy
binaries and their fingerprint dirs so the second build reproduces
real artifacts unconditionally.
Also drop the obsolete `version: '3.9'` from docker-compose.yml.
Co-Authored-By: OpenClaude <[email protected]>
Touch real source files after COPY so cargo's fingerprint check
detects the change and rebuilds from real source. Without this,
cargo can keep the dummy `fn main() {}` binaries from the
dependency-cache layer, causing the runtime container to exit
immediately with code 0.
Co-Authored-By: OpenClaude <[email protected]>