POST /api/v1/sync/trigger is reachable unauthenticated in the default configuration and drives the node to fan out HTTP requests to every known peer and enqueue per-repo sync work, with no caller identity check or effective rate limit.
Where
require_signed_peer_writes defaults to false (crates/gitlawb-node/src/config.rs:48).
- With the flag false, the route is gated only by
auth::optional_signature (server.rs:248), which passes unsigned requests through (auth/mod.rs:239).
trigger_sync (crates/gitlawb-node/src/api/peers.rs:184) takes no auth extractor and no body, loops over all peers issuing outbound GETs to {peer}/api/v1/repos, and enqueues a sync row per returned repo.
- The route is publicly merged (
server.rs:248). The per-DID rate limiter (rate_limit.rs) keys on the authenticated DID and early-returns when none is present, so it does nothing for an unauthenticated caller.
Impact
An anonymous caller can repeatedly force outbound fan-out and flood the sync queue (amplification/abuse). Combined with the outbound-redirect behavior and any poisoned peer row, it also becomes an unauthenticated trigger for the SSRF path.
Suggested fix
Require a valid signature on sync/trigger even when require_signed_peer_writes is false. (A "known-peer" check doesn't fit here: the request carries no peer identity and pulls from all peers.) Rate-limiting only helps once auth is enforced, since the limiter keys on the authenticated DID, so the two changes are coupled rather than independent. The sole caller, gl sync trigger, already signs when an identity is present, so requiring a signature is low-collateral. notify_sync is acceptable as-is since it gates on a known-peer DID.
POST /api/v1/sync/triggeris reachable unauthenticated in the default configuration and drives the node to fan out HTTP requests to every known peer and enqueue per-repo sync work, with no caller identity check or effective rate limit.Where
require_signed_peer_writesdefaults to false (crates/gitlawb-node/src/config.rs:48).auth::optional_signature(server.rs:248), which passes unsigned requests through (auth/mod.rs:239).trigger_sync(crates/gitlawb-node/src/api/peers.rs:184) takes no auth extractor and no body, loops over all peers issuing outbound GETs to{peer}/api/v1/repos, and enqueues a sync row per returned repo.server.rs:248). The per-DID rate limiter (rate_limit.rs) keys on the authenticated DID and early-returns when none is present, so it does nothing for an unauthenticated caller.Impact
An anonymous caller can repeatedly force outbound fan-out and flood the sync queue (amplification/abuse). Combined with the outbound-redirect behavior and any poisoned peer row, it also becomes an unauthenticated trigger for the SSRF path.
Suggested fix
Require a valid signature on
sync/triggereven whenrequire_signed_peer_writesis false. (A "known-peer" check doesn't fit here: the request carries no peer identity and pulls from all peers.) Rate-limiting only helps once auth is enforced, since the limiter keys on the authenticated DID, so the two changes are coupled rather than independent. The sole caller,gl sync trigger, already signs when an identity is present, so requiring a signature is low-collateral.notify_syncis acceptable as-is since it gates on a known-peer DID.