Skip to content

fix(security): tenant ID validation + self-instrumentation guard#64

Merged
aksOps merged 2 commits into
mainfrom
fix/input-safety-tenant-validation
Apr 28, 2026
Merged

fix(security): tenant ID validation + self-instrumentation guard#64
aksOps merged 2 commits into
mainfrom
fix/input-safety-tenant-validation

Conversation

@aksOps

@aksOps aksOps commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Summary

PR B of 6 from the production-readiness brainstorm.

  1. Tenant ID validation — `storage.SanitizeTenantID` rejects empty / >128-char / control-character values, applied at every transport boundary:

    • HTTP `X-Tenant-ID` header
    • gRPC `x-tenant-id` metadata
    • OTLP resource attribute `tenant.id` (trusted-resource path)

    Closes the log-injection / VARCHAR-truncation vector identified by codex round 1.

  2. Self-instrumentation feedback loop guard — when `OTEL_EXPORTER_OTLP_ENDPOINT` resolves to a loopback host, `Config.GuardSelfInstrumentation` auto-prepends the own service name (`config.SelfServiceName`) to `IngestExcludedServices` so SDK-emitted spans don't re-enter Export. Closes codex round 2 finding F.

Both fixes are non-breaking — rejection only catches inputs that were never valid, and the guard only fires on loopback endpoints.

Test plan

  • `go test -count=1 ./...` → 438 passed (was 416, +22 new)
  • MCP tests pass in isolation under race; full-suite race flakes are pre-existing 30s-timeout pressure unrelated to this PR
  • Sanitizer covers: control chars (\n, \r, \0, \t, ESC), over-length, empty, whitespace-only, Unicode letters allowed, trim handling
  • Guard covers: empty endpoint no-op, localhost auto-add, prepend to existing list, idempotent on re-run, remote endpoint no-op, nil-safe

🤖 Generated with Claude Code

aksOps and others added 2 commits April 28, 2026 07:25
Closes two production-readiness gaps from the brainstorm:

1. Tenant ID validation. SanitizeTenantID rejects empty / over-128-char
   / control-character values, applied at every transport boundary:
   - HTTP X-Tenant-ID header (api/tenant_middleware.go)
   - gRPC x-tenant-id metadata (ingest/otlp.go)
   - OTLP resource attribute tenant.id (ingest/otlp.go, trusted-resource path)

   Without this, a tenant ID of "foo\nbar" would inject a newline into
   slog structured fields, and 8KB tenant IDs would silently truncate at
   the GORM VARCHAR(64) layer.

2. Self-instrumentation feedback loop guard. When OTEL_EXPORTER_OTLP_ENDPOINT
   resolves to a loopback host (the binary's own gRPC port), the OTel SDK
   would otherwise emit a span on every Export call, re-entering Export and
   amplifying without bound. Config.GuardSelfInstrumentation auto-prepends
   the own service name to IngestExcludedServices and warns the operator.

Both fixes are non-breaking — sanitization rejects only inputs that were
never valid, and the guard only fires on loopback endpoints.

Tests: 26 new test cases across config / storage / api packages covering
the happy path, every rejection branch, and idempotency on re-runs.
go test ./... → 438 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@aksOps aksOps merged commit 5e074f0 into main Apr 28, 2026
17 checks passed
@aksOps aksOps deleted the fix/input-safety-tenant-validation branch April 28, 2026 07:42
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