Skip to content

feat(validation): proxy-side OpenAPI request validation#11

Open
spiohq wants to merge 13 commits into
mainfrom
feat/input-validation
Open

feat(validation): proxy-side OpenAPI request validation#11
spiohq wants to merge 13 commits into
mainfrom
feat/input-validation

Conversation

@spiohq

@spiohq spiohq commented May 5, 2026

Copy link
Copy Markdown
Owner

What

Adds request validation against the official SP-API OpenAPI specs before forwarding to upstream. Invalid requests (missing required parameters, wrong types, malformed bodies) are rejected at the proxy with a precise 400 error instead of wasting a round-trip and a rate-limit token.

How it works

  • On startup, the proxy downloads the SP-API OpenAPI specs as a ZIP from amzn/selling-partner-api-models (no git binary required, pure Go stdlib)
  • Specs are cached on disk and reloaded daily via the existing scheduler mechanism with an atomic router swap (no downtime)
  • The validation middleware sits after logging in the chain so rejected requests appear in the dashboard like normal upstream 400s, identifiable by the X-SP-Proxy-Validation: rejected response header
  • Endpoints not covered by any loaded spec pass through unchanged (graceful degradation — private betas and new endpoints are never blocked)
  • Validation is opt-in (SP_PROXY_VALIDATION_ENABLED=false by default) and can be bypassed per-request with X-SP-Proxy-Skip-Validation: true

New config

Env var Default Description
SP_PROXY_VALIDATION_ENABLED false Master switch
SP_PROXY_VALIDATION_SPECS_URL GitHub ZIP URL Source for SP-API OpenAPI specs
SP_PROXY_VALIDATION_SPECS_DIR (empty) Filesystem cache path (required when enabled)
SP_PROXY_VALIDATION_REFRESH_INTERVAL 24h How often to re-download specs

Error format

Errors are returned in the standard SP-API envelope so clients see no structural difference, just a more useful message:

{
  "errors": [
    {
      "code": "InvalidInput",
      "message": "missing required query parameter: marketplaceIds",
      "details": "validated by proxy against SP-API OpenAPI spec"
    }
  ]
}

New dependency

github.com/getkin/kin-openapi for OpenAPI 3.0 spec parsing and request validation.

spiohq added 13 commits May 5, 2026 09:58
Adds a standalone AUP compliance reference structured like DPP_COMPLIANCE.md,
covering all AUP §§1-5 with Smart Proxy enforcement details, operator
obligations, audit checklist, and code verification table. Adds cross-links
from DPP_COMPLIANCE.md §7 and the README documentation table.
ThrottleFactor > 1.0 is already enforced by Validate() at startup, so
the checklist entry no longer implies it is operator-verifiable config.
Verification table: TestDefaults_ThrottleFactor -> TestLoad_Defaults.
Adds NewMiddleware and AtomicRouter to the validation package, plus a
fix to humanMessage in errors.go so parameter names are preserved in
error output (use reqErr.Error() instead of reqErr.Err.Error()).
Add VALIDATION.md reference doc, update README with feature section,
configuration table, header tables, architecture diagram, and docs index.
Update SECURITY.md with bypass-header trust guidance and scope update.
- Add TestMiddleware_SkipHeader_NonTrueValues_StillValidates to verify
  that only the exact string "true" bypasses validation; case variants
  ("True", "TRUE", "1", "yes", "on") still go through the validator
- Add test/e2e/validation_test.go with 7 E2E tests covering the full
  proxy stack: invalid request, valid request, skip-header bypass,
  skip-header non-true-values, unknown path passthrough, error envelope
  shape, and nil-router (disabled) passthrough
- Wire WithValidationRouter option into e2e NewTestEnv so validation
  middleware can be enabled without a real spec download in tests
- Add SP_PROXY_VALIDATION_*, SP_PROXY_PROMETHEUS_*, and
  SP_PROXY_RDT_AUTO_MINT sections to deploy/example.env with
  documentation; these vars were implemented but undocumented
ValidateRequest from kin-openapi reads r.Body to completion, leaving it
empty for the reverse proxy. This caused `unexpected end of stream` errors
on the upstream side for any request with a body (POST/PUT/PATCH).

Fix: read body into a buffer before validation and replace r.Body with a
fresh reader both before and after the call so downstream handlers always
see the original bytes.

Also fix setupValidation in main.go to auto-create a temp dir when
SP_PROXY_VALIDATION_SPECS_DIR is unset instead of refusing to start;
matches the documented behaviour in example.env.

Add TestMiddleware_BodyRestoredAfterValidation (unit) and
TestE2E_Validation_RequestBodyForwardedToUpstream (e2e) as regression
tests for the body-drain bug.
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