Skip to content

fix(api): validate tenant id at route boundary#3

Merged
tzone85 merged 1 commit into
mainfrom
harden/api-tenant-id-validation
Jun 11, 2026
Merged

fix(api): validate tenant id at route boundary#3
tzone85 merged 1 commit into
mainfrom
harden/api-tenant-id-validation

Conversation

@tzone85

@tzone85 tzone85 commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • handleTenantRoute now runs tenant.New(base, id) on the URL-supplied id BEFORE the auth check or any filesystem access. Malformed ids return 400 Bad Request immediately.
  • Defense-in-depth fix. Go's mux normalises .. so true path traversal was already blocked, but every other shape of invalid id (uppercase, leading dash, dot, underscore, space, control char, oversized) flowed past the gate and hit the FS before being rejected.
  • Same validID regex as the on-disk tenant constructor — single source of truth, no second validation layer to drift out of sync.

TDD

RED first: TestAPI_TenantRoute_RejectsMalformedID table test for 6 invalid-id shapes (uppercase, leading dash, dot, underscore, space, 64-byte control-char string). Without the fix all six returned 401 (auth ran first). With the fix all six return 400.

Test plan

  • go test -race ./... — all packages green
  • bash scripts/cover_check.sh — PASS, api 84.9% (target 83), global 88.0% (target 87)
  • golangci-lint run — 0 issues
  • govulncheck ./... — 0 vulnerabilities

Before this change, /v1/tenants/{id}/... handed the raw id from the
URL straight to RequireIdentity and then into filepath.Join. The mux
already normalises `..` so true path traversal was blocked, but every
other malformed id (uppercase, leading dash, dot, control char,
oversized, …) flowed past auth and hit the filesystem before being
rejected — an unnecessary attack surface.

Now handleTenantRoute runs tenant.New(base, id) before auth. Any id
that fails the validID regex returns 400 immediately, no filesystem
access, no auth probe.

Same regex as the on-disk tenant constructor, so the contract is
"the only ids that reach handlers are the ones the on-disk store
would accept" — no second validation layer to drift out of sync.

Coverage gate: api 84.9% (target 83), global 88.0% (target 87).
@tzone85 tzone85 merged commit 419d98e into main Jun 11, 2026
2 checks passed
@tzone85 tzone85 deleted the harden/api-tenant-id-validation branch June 11, 2026 09:19
tzone85 added a commit that referenced this pull request Jun 11, 2026
Adds an Unreleased section to CHANGELOG.md covering the five
sequential PRs that landed today:
- #2 chore(security): bump x/sys for GO-2026-5024
- #3 fix(api): validate tenant id at route boundary
- #4 fix(ingest): reject option-shaped --base-ref
- #5 fix(mempalace): validate Kind/Tenant/Key against path traversal
- #6 chore(supply-chain): pin govulncheck + base images by digest

Updates README Status to mention the hardening pass and replaces a
stale `§9` design-spec cross-reference with the section's actual
name (per the project's doc-style rule against the § symbol).

No code changes, no behaviour changes — pure documentation refresh
for the audit story.
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