Skip to content

fix: reject out-of-range repository IDs instead of silently clamping - #1171

Open
vliggio wants to merge 2 commits into
sourcegraph:mainfrom
vliggio:fix/reject-out-of-range-repoid
Open

vliggio wants to merge 2 commits into
sourcegraph:mainfrom
vliggio:fix/reject-out-of-range-repoid

Conversation

@vliggio

@vliggio vliggio commented Oct 5, 2026

Copy link
Copy Markdown

Closes #1170

Problem

zoekt.repoid is parsed with strconv.ParseUint(v, 10, 32) and the error is discarded, both at index time (gitindex) and at metadata read time (Repository.UnmarshalJSON). Go returns the maximum value on overflow, so any ID above 4294967295 is silently stored as 4294967295 and collides with every other oversized repository in ReposMap, compound shard lookups, tombstones and RepoIDs filters. A malformed value is silently stored as 0.

Changes

  • api.go: add ParseRepoID, which rejects malformed and out-of-range values with a descriptive error. Repository.UnmarshalJSON now uses it and returns the error. An empty repoid is treated as absent, as before.
  • gitindex/index.go: setTemplatesFromRepoConfig uses ParseRepoID and returns the error, so zoekt-git-index fails the build instead of writing a shard with a colliding ID.
  • cmd/zoekt-git-clone/main.go: -repoid values above the uint32 maximum are rejected up front instead of being written to git config and clamped later.
  • cmd/zoekt-sourcegraph-indexserver: the id and repo HTTP parameters and the debug index argument use ParseRepoID instead of strconv.Atoi plus a uint32 conversion, which wrapped modulo 2^32. An empty repo parameter still returns 400.

Behaviour change

A shard whose stored repoid cannot be parsed now fails ReadMetadata. The shard loader already logs and skips shards that fail to load, so such a shard is dropped from search with an [ERROR] reloading: log line instead of being served under a colliding ID. These shards were already corrupt, since their ID collided with every other oversized repository, but reviewers should be aware this makes the corruption visible.

Test plan

  • TestParseRepoID: valid, zero, max uint32, empty, out of range, negative, malformed.
  • TestRepositoryUnmarshalJSON_RepoID: out-of-range and malformed repoid return an error; empty repoid leaves the JSON ID untouched.
  • TestSetTemplates_RepoID: end-to-end through git config zoekt.repoid for valid, max, out-of-range and malformed values.
  • go test ./... -short passes locally.

🤖 Generated with Claude Code

vliggio and others added 2 commits October 5, 2026 10:17
zoekt.repoid was parsed with strconv.ParseUint(v, 10, 32) and the error
discarded, both at index time and when reading shard metadata. Go returns
the maximum value on overflow, so every ID above 4294967295 became
4294967295 and collided in ReposMap, compound shard lookups, tombstones
and RepoIDs filters. Malformed values became 0.

Add zoekt.ParseRepoID and use it at every parse site so an invalid ID
fails the index build, fails shard metadata loading (logged and skipped),
and is rejected by zoekt-git-clone -repoid and the indexserver's HTTP and
debug handlers, which previously wrapped via uint32(Atoi(v)).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
indexGitRepo logs and tolerates setTemplatesFromRepo errors because
template and URL problems only degrade result links. A local smoke test
showed that an out-of-range zoekt.repoid therefore still produced a
shard, with ID 0 and without its raw config, instead of failing.

Wrap ParseRepoID errors in a new zoekt.ErrInvalidRepoID sentinel and
return from indexGitRepo when errors.Is matches it, so zoekt-git-index
exits non-zero and writes no shard. Other config errors are still
logged and tolerated. Add an end-to-end test through IndexGitRepo.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Out-of-range zoekt.repoid is silently clamped to 4294967295 instead of rejected

1 participant