Skip to content

api(bounties): aggregates ignore repo visibility, exposing private-repo activity #477

Description

@euxaristia

Summary

bounty_stats and agent_bounty_stats (crates/gitlawb-node/src/api/bounties.rs:460-502) run unfiltered aggregates over all bounties (crates/gitlawb-node/src/db/mod.rs:4530-4565) with no repo-read gating. The sibling stats endpoint was deliberately scoped to anonymously listable repos (#104, server.rs:546-570), and row-level bounty routes are visibility-gated; these two aggregates were not updated.

Impact

An anonymous caller can infer private-repo bounty activity from aggregate deltas and read per-agent earnings totals. No repo identity is disclosed; aggregate-granularity signal only.

Remediation

  1. Restrict both aggregates to anonymously readable repos (mirror Unauthenticated GET /api/v1/stats leaks the count of private/mode-A repos (count oracle) #104) or gate the routes.

Proposed labels: kind:security, crate:node, subsystem:api.

Activity

  1. added
    crate:nodegitlawb-node — the serving node and REST API
    kind:securityVulnerability fix or hardening
    sev:highMajor break or real security/trust risk, no easy workaround
    subsystem:apiNode REST API request/response surface
    subsystem:visibilityPath-scoped visibility and content withholding
    on Sep 26, 2026
  2. added a commit that references this issue on Sep 26, 2026
  3. added
    sev:mediumDegraded but workaround exists
    and removed
    sev:highMajor break or real security/trust risk, no easy workaround
    on Sep 26, 2026
  4. beardthelion commented on Sep 26, 2026

    @beardthelion
    Collaborator

    Recalibrating to sev:medium. The leak is aggregate-granularity: deltas over all-bounty stats with no repo identity disclosed. That is weaker than #341 (medium), where a stranger could distinguish a specific private-repo bounty from a nonexistent one. A fix is already open at #483.

  5. Mystic-commits commented on Sep 27, 2026

    @Mystic-commits

    I've opened PR #495 to resolve this.

    Approach & Differentiators:

    • Zero N+1 DB Queries / No Table Walks: Mirrors the canonical stats() pattern (Unauthenticated GET /api/v1/stats leaks the count of private/mode-A repos (count oracle) #104, server.rs:546-570) by batch-loading repos and visibility rules once, evaluating listable_at_root in memory, and performing aggregation in PostgreSQL via unnest.
    • Fails Closed: Database errors collapse the visible set to empty, returning 0/empty without leaking private repo existence.
    • Includes Deny-Probe Tests: Added integration tests in crates/gitlawb-node/src/test_support.rs verifying anonymous probes cannot observe private repo bounties in global stats, leaderboard, or per-agent earnings.

    PR: #495

  6. added 3 commits that reference this issue on Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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 surfacesubsystem:visibilityPath-scoped visibility and content withholding

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions