Reject NUL and control bytes in path values before slug and name lookups

A percent-encoded NUL in a path segment survives ServeMux into r.PathValue, and nothing between the router and the database checks the value's shape.

On the management API, resolveSlug (internal/managementapi/resolve.go) passes r.PathValue(slugPathValue) straight to NamespaceFinder.FindBySlug, and NamespaceStore.FindBySlug (internal/datastore/namespaces.go) interpolates it with pg.String(slug). RepositoryStore.FindByName takes the repository-name path value the same way.

LabKit's pool runs pgx in simple-protocol mode, so the value arrives as interpolated query text. A NUL truncates the wire query and PostgreSQL fails the whole statement, which resolveSlug maps to logAndWriteInternalError: a logged 500 on what is malformed client input.

Verified two ways. A standalone ServeMux probe confirms the value survives routing: GET /api/v1/a%00b/repositories matches the {slug} pattern, returns 200, and the handler reads a three-byte slug with the NUL intact. Reading the chain confirms no guard stands between that value and pg.String.

Same class as the cursor-boundary defect, but on the path rather than the query string, so reaching it needs no forged cursor. The container-list half is closed at decode in fix(managementapi): reject malformed container ... (!1229 - merged) • Hayley Swimelar • 19.3.

Proposal: check the shape of the slug and repository-name path values before the lookup. Which status to answer is the open question. An unknown slug answers 404 today for existence-hiding, which argues for answering a malformed one the same way rather than 400. Settle it against S17's Error Cases rows with the spec author before implementing.

Raised in review of fix(managementapi): reject malformed container ... (!1229 - merged) • Hayley Swimelar • 19.3.