Skip to content

Availability: unauthenticated GET /ipfs/{cid} runs one blocking git cat-file per readable repo #164

Description

@beardthelion

Summary

An unauthenticated GET /ipfs/{cid} for a well-formed but nonexistent sha2-256 CID makes get_by_cid run one blocking git cat-file -t subprocess per readable repo, on the tokio runtime, with no rate limiter on the route. A single client spraying nonexistent CIDs scales blocking subprocess spawns by (concurrency × readable-repo count) and can starve async worker threads. This is an availability issue; no data is disclosed.

Pre-existing: the probe and the all-repos loop predate #134. It surfaced while reviewing #134, whose list_pins amplification is the same class on a different handler. Filing so it is tracked on its own.

Where

crates/gitlawb-node/src/api/ipfs.rs, get_by_cid: the handler loops over every row from list_all_repos() and, for each repo the caller can read at /, runs an inline blocking probe before any indexed lookup:

let obj_type = match store::object_type(&repo_path, &sha256_hex) {
    Ok(Some(t)) => t,
    Ok(None) => continue,
    Err(e) => { /* warn + continue */ }
};

store::object_type (crates/gitlawb-node/src/git/store.rs) is a synchronous Command::new("git").args(["cat-file","-t", …]).output(). Unlike the allowed-blob walk a few lines below it, this probe is not wrapped in spawn_blocking, so it blocks a tokio worker thread.

Reachability

  • /ipfs/{cid} carries only optional_signature (server.rs), so anonymous callers are accepted.
  • No limiter covers the route: rate_limit_by_did is only on the creation routes and rate_limit_by_ip only on the git-write routes.
  • A nonexistent CID never matches, so the loop probes every readable repo before returning 404. For an anonymous caller on a public-heavy node that is ~every repo, so the cost is O(repos) per request.

Repro (executed)

Seeded 20 public repos, each backed by a real bare repo on disk, and issued a single anonymous GET /ipfs/{absent-cid} with the object_type call path instrumented:

  • 404 after scanning every repo, and exactly 20 git cat-file -t spawns for the one request — one blocking probe per readable repo. Scales linearly with repo count.

Directions (verify before applying)

  • Wrap the object_type probe in spawn_blocking, matching the treatment of the blob walk immediately below it, so it does not block the async runtime.
  • Add an IP (or DID) rate limiter to the ipfs routes; they currently have none.
  • Prefer an indexed CID to repo lookup (pinned_cids/branch_cids already carry the mapping) over probing every repo for object existence.

Severity: medium. Availability only; per-probe cost is low, but it is unthrottled and unauthenticated, and grows with the repo count on the node.

Metadata

Metadata

Assignees

No one assigned

    Labels

    crate:nodegitlawb-node — the serving node and REST APIkind:securityVulnerability fix or hardeningsev:mediumDegraded but workaround existssubsystem:apiNode REST API request/response surface

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions