Summary
GET /api/v1/repos/{owner}/{repo}/pulls/{n}/diff (crates/gitlawb-node/src/api/pulls.rs:136-190) runs on read_routes under optional_signature only - no brake, no permit (server.rs:342-431), with an acquire that has no timeout clamp (:150-155, unlike git_info_refs/ipfs). branch_diff (crates/gitlawb-node/src/git/store.rs:750-758) runs git diff <target>...<source> synchronously with .output() (full stdout buffered), then String::from_utf8_lossy(...).to_string() copies it again; the JSON response materializes the whole patch a third time.
Impact
Any authenticated agent can open a PR whose source branch diffs a large amount of content against a public repo (push packs reach 2 GB by default); every subsequent GET is anonymous. Each request pins a runtime worker for the subprocess duration and buffers the full diff twice, so concurrent requests stall all workers, taking /health down with them. The blocking-git-on-the-runtime aspect is #204's class; the unbounded diff serve on this route is not covered by it, and #407's ceiling covered blobs only.
Remediation
- Cap the diff size before serving (fail closed past a ceiling) or stream with a hard cap.
- Admit the route through a semaphore or per-IP brake like the git and ipfs groups.
- Move the subprocess to
spawn_blocking with a deadline and reaping, and clamp the acquire timeout like the smart-HTTP paths.
Proposed labels: kind:security, crate:node, subsystem:api (final severity yours).
Summary
GET /api/v1/repos/{owner}/{repo}/pulls/{n}/diff(crates/gitlawb-node/src/api/pulls.rs:136-190) runs onread_routesunderoptional_signatureonly - no brake, no permit (server.rs:342-431), with anacquirethat has no timeout clamp (:150-155, unlike git_info_refs/ipfs).branch_diff(crates/gitlawb-node/src/git/store.rs:750-758) runsgit diff <target>...<source>synchronously with.output()(full stdout buffered), thenString::from_utf8_lossy(...).to_string()copies it again; the JSON response materializes the whole patch a third time.Impact
Any authenticated agent can open a PR whose source branch diffs a large amount of content against a public repo (push packs reach 2 GB by default); every subsequent GET is anonymous. Each request pins a runtime worker for the subprocess duration and buffers the full diff twice, so concurrent requests stall all workers, taking
/healthdown with them. The blocking-git-on-the-runtime aspect is #204's class; the unbounded diff serve on this route is not covered by it, and #407's ceiling covered blobs only.Remediation
spawn_blockingwith a deadline and reaping, and clamp theacquiretimeout like the smart-HTTP paths.Proposed labels: kind:security, crate:node, subsystem:api (final severity yours).