fix(adapters): JSON-null required-field bypass — 109 sites across 65 adapters - #63
Merged
Conversation
…adapters
Starlark decodes JSON null to None and None == "" is False, so a
plain .get(k, "") + == "" required-field guard PASSES a null field:
{"username": null} was stored and echoed as null where every real API
rejects it (the guard-integrity audit stunt-qi0, the one class the
never-5xx fuzz invariant cannot see — it manifests as a wrong 200).
Fix at the extraction: V = body.get(k, "") -> V = body.get(k) or ""
at the 109 paired extraction+guard sites where the guard lacked its own
None/type check (sites already checking == None or type(V) != "string"
were left alone). Null now lands as "" and the existing required-field
400s fire with each provider's real error.
Pinned by TestAWSCognitoStyleAdapter: SignUp with Username: null ->
400 InvalidParameterException.
Review findings on PR #63: - salesforce upsert-insert (sobjects.star:316) and composite (composite.star:226): inline 'body.get("Name", "") == ""' guards passed {"Name": null} on the upsert path while the plain-create path (fixed in the first commit) rejects it — create and upsert now agree. - powerplatform dataverse create (dataverse.star:207): a null accountid skipped id autogeneration and then crashed building the Location header (string + None) instead of returning 201-with-generated-id. - stripe subscriptions discount guard: same permeable inline shape, contrived-only impact, fixed for consistency. - square refunds (refunds.star): a null payment_id crashed the 404 error path itself ('Payment ' + None) — coerced at extraction.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Closes the guard-integrity audit (
stunt-qi0) — the one class the v0.45.0 fuzz invariant cannot see: a JSON null in a required field produces a wrong 200 with a stored null, not a 500.Starlark decodes JSON
nulltoNone, andNone == ""isFalse— soV = body.get(k, "")+if V == "": return 400passes a{"username": null}body. The null gets stored and echoed where every real API rejects the request.Fix
V = body.get(k, "")→V = body.get(k) or ""— applied only at extraction+guard pairs where the guard (or its 3-line window) lacked its own== None/!= None/type()check. Sites that already null-check (e.g.if x == None or x == "") were measured and left alone. Null now lands as""and each provider's existing required-field 400 fires with its real error shape.109 sites, 65 adapters — OAuth client-id/secret checks, signup/credential fields (Cognito, Firebase), create-required names/urls (Cloudflare zones/D1/R2, Slack, Jira, Shopify, Stripe subscriptions, Onfido, Persona, PSD2 consents, …).
Test
TestAWSCognitoStyleAdapterpins it: SignUp withUsername: null→ 400InvalidParameterException(an auth-free endpoint, so the pin exercises exactly this guard).TestDownEndToEndWithRealBinaryflaked once under post-fuzz machine load; 2x green in isolation.)