Skip to content

fix: list repos for a q or prefix of any length, and refuse NUL and invalid UTF-8 - #783

Merged
ThomasK33 merged 5 commits into
mainfrom
list-repos-long-query
Nov 9, 2026
Merged

ThomasK33 merged 5 commits into
mainfrom
list-repos-long-query

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

Why

GET /api/v1/repos?q=<200 KiB> answers 500 on main (found by #780's verifier). The cause is SQLite: it refuses a LIKE pattern over 50,000 bytes (SQLITE_MAX_LIKE_PATTERN_LENGTH, "LIKE or GLOB pattern too complex"). The local backend matched q and prefix with lower(url) LIKE lower(?), and the escaping doubles each %, _ and \, so a q or prefix of 50,000 bytes, or of 25,000 percent signs, answered 500. PostgreSQL answers 200 there.

The same filters had a second gap. PostgreSQL refuses a NUL byte and invalid UTF-8 in a text parameter (SQLSTATE 22021), so a q or prefix with either answered 500 there. SQLite read a q only up to a NUL byte: q=a%00b listed acme/alpha.

What changes

  • SQLite's ListRepos tests the prefix with substr(lower(url), 1, length(?)) = lower(?) and q with instr(substr(lower(url), length(<prefix>) + 1), lower(?)) > 0, as the S3-WAL backend does in Go. The filters have no length limit there and need no escaping. PostgreSQL keeps ILIKE.
  • GET /repos answers 400 for a q or prefix with a NUL byte or invalid UTF-8: "invalid q (want UTF-8 text without NUL)". meta.ListParams states that contract, and every backend asserts it (assertListFilters).
  • docs/reference/http-api.md says so.

Verification

  • TestListReposLongFilters (meta, on PostgreSQL and SQLite, and on an in-memory S3-WAL store through TestS3WALMetaConformance): a q and a prefix of 200 KiB list nothing. A q of 30,000 percent signs lists the one URL that holds them, and one more percent sign lists nothing. A prefix of acme/ and 30,000 percent signs with q=END lists that URL, and q=% inside that prefix lists nothing. On main every SQLite case fails with "LIKE or GLOB pattern too complex", and PostgreSQL passes. S3-WAL passes at this head.
  • TestListFilterValues (api, PostgreSQL and SQLite): a q of 200 KiB, a prefix of 200 KiB and a q of 25,001 percent signs answer 200 with an empty last page. A q or prefix with a NUL byte or invalid UTF-8 answers 400. On main, SQLite answers 500 for the three long values and 200 for the four NUL and UTF-8 cases, and PostgreSQL answers 500 for the four NUL and UTF-8 cases.
  • TestListReposPrefixFilter gains acme/pierre/inner, which holds the prefix pierre/ but not at its start. It runs on PostgreSQL, SQLite and S3-WAL (TestS3WALMetaConformance already listed it).
  • TestListReposQFilter gains q=GAMMA, which must list github.com/acme/gamma: a q in another case than the URL.
  • Mutant checks (each fails a test): no 400 in the handler (the store's assertion makes it a 500), q matched from the prefix's last character, q matched over the whole URL, the prefix matched anywhere, and a case-sensitive q. In the S3-WAL matcher, a q matched over the whole URL fails ListReposPrefixFilter and ListReposLongFilters. A case-sensitive q fails both TestListReposQFilter and TestListReposLongFilters on each backend: q not lowercased on SQLite and S3-WAL, and LIKE for ILIKE on PostgreSQL.
  • At a5cf389, with -race: build, vet, golangci-lint (0 issues), the doc-link check and both --fingerprints-only checks pass. internal/meta passes. Its shared ListRepos tests run on PostgreSQL and SQLite (runBackends), and TestS3WALMetaConformance runs them on an in-memory S3-WAL store. internal/api passes with and without -tags=openstorage_viewcheck. internal/api runs each integration test on PostgreSQL and SQLite only, so no lane runs GET /repos on S3-WAL. The handler refuses a NUL byte or invalid UTF-8 before it calls the store, so that 400 does not depend on the backend.
  • The first tagged internal/api run failed TestReadCoreFilesMetadataOrder/pg/v1 with 503 "request exceeded the server's git time budget". That run took 792 s, against 402 s for the passing tagged rerun, and the host load average read 56 to 71 right after it. This round changes no internal/api code.
  • At 335fd9d (adds the q=GAMMA row, and merges origin/main 4a505d0, which changes no file of this PR), with -race: build, vet, golangci-lint (0 issues), the doc-link check and both --fingerprints-only checks pass. internal/meta passes on PostgreSQL, SQLite and in-memory S3-WAL, and internal/api passes with -tags=openstorage_viewcheck on PostgreSQL and SQLite.

Size

141 changed lines: 51 of production Go, 88 of tests and 2 of docs.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh

SQLite refuses a LIKE pattern over 50,000 bytes
(SQLITE_MAX_LIKE_PATTERN_LENGTH), so GET /repos answered 500 for a q or
prefix of 50,000 bytes or 25,000 percent signs on the local backend.
SQLite's ListRepos now tests the start of lower(url) with substr and q
with instr, as the S3-WAL backend does in Go, so the filters have no
length limit and need no escaping.

PostgreSQL refuses a NUL byte or invalid UTF-8 in a text parameter, and
answered 500 for one in q or prefix, while SQLite matched a q with a NUL
byte as its text before the NUL. GET /repos now answers 400 for either
in q or prefix, and every backend asserts the filters are UTF-8 without
NUL.

Change-Id: Iace746814c0c2af656a1f75c4e747f46467cb0e1
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I37772e9c3af38d3e2e396d3ffe3d0eb7fff7a2de
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-09T09:03:25.089006Z 335fd9d Manual request
🔒 Security Review ✅ Completed 2026-11-09T09:07:51.717114Z 335fd9d Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: d34a0b0995

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: d34a0b0995

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

TestS3WALMetaConformance now runs testListReposLongFilters, so the
S3-WAL backend's Go matcher gets the same long q and prefix cases as
PostgreSQL and SQLite. testListReposPrefixFilter was already in the list.

Change-Id: I80831bff27a7bfa9ea9843fbffa103a86b45d6f1
Signed-off-by: Thomas Kosiewski <tk@coder.com>
q=GAMMA must list github.com/acme/gamma on every backend. A q that is
matched case-sensitively now fails TestListReposQFilter too, not only
TestListReposLongFilters.

Change-Id: I30e8bf5ce4e803d8e8c4d11189793ebc2f9d709f
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 335fd9d1a1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 335fd9d1a1

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Nov 9, 2026
Merged via the queue into main with commit e6be273 Nov 9, 2026
10 of 11 checks passed
@ThomasK33
ThomasK33 deleted the list-repos-long-query branch November 9, 2026 09:47
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.

1 participant