From b6f78f4292ba8063232e221ecad7b07e9908ea07 Mon Sep 17 00:00:00 2001 From: Alessandro De Blasis Date: Mon, 17 Aug 2026 18:19:58 +0300 Subject: [PATCH 1/2] =?UTF-8?q?fix(adapters):=20JSON-null=20required-field?= =?UTF-8?q?=20bypass=20=E2=80=94=20109=20sites=20across=2065=20adapters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../adyen-style/scripts/payment_links.star | 4 ++-- adapters/adyen-style/scripts/payments.star | 2 +- .../apple-music-style/scripts/library.star | 2 +- .../scripts/campaigns.star | 2 +- .../aws-cognito-style/scripts/service.star | 24 +++++++++---------- adapters/bluesky-style/scripts/records.star | 2 +- adapters/bluesky-style/scripts/session.star | 4 ++-- .../braintree-style/scripts/webhooks.star | 4 ++-- adapters/cloudflare-style/scripts/d1.star | 4 ++-- adapters/cloudflare-style/scripts/r2.star | 2 +- adapters/cloudflare-style/scripts/zones.star | 2 +- adapters/discord-style/scripts/oauth.star | 4 ++-- adapters/drive-style/scripts/oauth.star | 4 ++-- adapters/entra-id-style/scripts/oauth.star | 2 +- adapters/firebase-style/scripts/auth.star | 8 +++---- adapters/firebase-style/scripts/fcm.star | 4 ++-- .../firebase-style/scripts/firestore.star | 2 +- .../google-admin-style/scripts/groups.star | 4 ++-- .../google-admin-style/scripts/users.star | 2 +- adapters/google-iam-style/scripts/oauth.star | 2 +- adapters/google-iam-style/scripts/roles.star | 2 +- adapters/google-style/scripts/oauth.star | 4 ++-- adapters/hn-style/scripts/auth.star | 2 +- adapters/hn-style/scripts/submit.star | 2 +- adapters/instagram-style/scripts/publish.star | 4 ++-- adapters/jira-style/scripts/issue.star | 4 ++-- adapters/jira-style/scripts/webhook.star | 2 +- adapters/jumio-style/scripts/scans.star | 2 +- adapters/linkedin-style/scripts/oauth.star | 4 ++-- .../microsoft-graph-style/scripts/drive.star | 2 +- .../scripts/subscriptions.star | 6 ++--- adapters/netsuite-style/scripts/query.star | 2 +- adapters/onfido-style/scripts/applicants.star | 4 ++-- adapters/onfido-style/scripts/checks.star | 2 +- adapters/onfido-style/scripts/documents.star | 4 ++-- adapters/persona-style/scripts/inquiries.star | 4 ++-- adapters/photos-style/scripts/oauth.star | 4 ++-- .../plaid-style/scripts/institutions.star | 2 +- adapters/plaid-style/scripts/item.star | 4 ++-- adapters/plaid-style/scripts/lib.star | 4 ++-- adapters/plaid-style/scripts/sandbox.star | 4 ++-- adapters/printful-style/scripts/orders.star | 2 +- adapters/printful-style/scripts/webhooks.star | 2 +- adapters/psd2-style/scripts/lib.star | 2 +- adapters/psd2-style/scripts/payments.star | 2 +- adapters/qbo-style/scripts/customer.star | 4 ++-- adapters/qbo-style/scripts/invoice.star | 2 +- adapters/qbo-style/scripts/lib.star | 2 +- adapters/reddit-style/scripts/submit.star | 4 ++-- .../revenuecat-style/scripts/receipts.star | 2 +- adapters/salesforce-style/scripts/oauth.star | 8 +++---- .../salesforce-style/scripts/sobjects.star | 2 +- adapters/sendgrid-style/scripts/webhooks.star | 2 +- adapters/shopify-style/scripts/oauth.star | 6 ++--- .../slack-style/scripts/conversations.star | 2 +- adapters/slack-style/scripts/events.star | 2 +- adapters/slack-style/scripts/reactions.star | 2 +- adapters/square-style/scripts/oauth.star | 2 +- .../stripe-style/scripts/subscriptions.star | 2 +- adapters/threads-style/scripts/oauth.star | 2 +- adapters/threads-style/scripts/publish.star | 2 +- .../x-articles-style/scripts/articles.star | 2 +- adapters/x-articles-style/scripts/oauth.star | 4 ++-- adapters/youtube-style/scripts/oauth.star | 4 ++-- adapters/zuora-style/scripts/query.star | 2 +- internal/engine/aws_cognito_style_test.go | 17 +++++++++++++ 66 files changed, 126 insertions(+), 109 deletions(-) diff --git a/adapters/adyen-style/scripts/payment_links.star b/adapters/adyen-style/scripts/payment_links.star index 1d6b1626..bb3bf17b 100644 --- a/adapters/adyen-style/scripts/payment_links.star +++ b/adapters/adyen-style/scripts/payment_links.star @@ -35,7 +35,7 @@ def on_create_payment_link(req): if _amt_value(amount) <= 0: return _adyen_err(422, "710", "amount.value must be greater than zero", "validation") - reference = body.get("reference", "") + reference = body.get("reference") or "" if reference == None: reference = "" if reference == "": @@ -46,7 +46,7 @@ def on_create_payment_link(req): if reusable == None: reusable = False - expires_at = body.get("expiresAt", "") + expires_at = body.get("expiresAt") or "" if expires_at == None: expires_at = "" if expires_at == "": diff --git a/adapters/adyen-style/scripts/payments.star b/adapters/adyen-style/scripts/payments.star index a3ab1313..6b777c7b 100644 --- a/adapters/adyen-style/scripts/payments.star +++ b/adapters/adyen-style/scripts/payments.star @@ -135,7 +135,7 @@ def on_payment_details(req): if body == None: body = {} - token = body.get("paymentData", "") + token = body.get("paymentData") or "" if token == None: token = "" if token == "": diff --git a/adapters/apple-music-style/scripts/library.star b/adapters/apple-music-style/scripts/library.star index 5ff62db4..a5d55e37 100644 --- a/adapters/apple-music-style/scripts/library.star +++ b/adapters/apple-music-style/scripts/library.star @@ -251,7 +251,7 @@ def on_played(req): t = body.get("type", "songs") if t == None: t = "songs" - ident = body.get("id", "") + ident = body.get("id") or "" if ident == None: ident = "" if ident == "": diff --git a/adapters/apple-searchads-style/scripts/campaigns.star b/adapters/apple-searchads-style/scripts/campaigns.star index e97362d5..355e1e9d 100644 --- a/adapters/apple-searchads-style/scripts/campaigns.star +++ b/adapters/apple-searchads-style/scripts/campaigns.star @@ -47,7 +47,7 @@ def on_create_campaign(req): if body == None: body = {} - name = body.get("name", "") + name = body.get("name") or "" if name == "": return respond(400, _err("Campaign name is required")) diff --git a/adapters/aws-cognito-style/scripts/service.star b/adapters/aws-cognito-style/scripts/service.star index 4de9fdb3..33f55070 100644 --- a/adapters/aws-cognito-style/scripts/service.star +++ b/adapters/aws-cognito-style/scripts/service.star @@ -72,8 +72,8 @@ def on_service_api(req): # {ClientId, Username, Password, UserAttributes: [{Name, Value}]} def _do_signup(req): body = _json_body(req) - username = body.get("Username", "") - password = body.get("Password", "") + username = body.get("Username") or "" + password = body.get("Password") or "" if username == "" or password == "": return _cognito_err("InvalidParameterException", @@ -125,7 +125,7 @@ def _do_signup(req): # {ClientId, Username, ConfirmationCode} def _do_confirm_signup(req): body = _json_body(req) - username = body.get("Username", "") + username = body.get("Username") or "" if username == "": return _cognito_err("InvalidParameterException", "Username is required") @@ -180,7 +180,7 @@ def _do_respond_to_challenge(req): return _cognito_err("InvalidParameterException", "ChallengeName " + challenge_name + " is not supported") - session_id = body.get("Session", "") + session_id = body.get("Session") or "" if session_id == "": return _cognito_err("InvalidParameterException", "Session is required") @@ -216,7 +216,7 @@ def _do_respond_to_challenge(req): user["status"] = "CONFIRMED" uc.update(user["id"], user) - client_id = body.get("ClientId", "") + client_id = body.get("ClientId") or "" if client_id == "": client_id = "mock-client-id" return respond(200, _auth_result(user, client_id)) @@ -227,7 +227,7 @@ def _do_respond_to_challenge(req): # {ClientId, Username} def _do_forgot_password(req): body = _json_body(req) - username = body.get("Username", "") + username = body.get("Username") or "" if username == "": return _cognito_err("InvalidParameterException", "Username is required") @@ -258,7 +258,7 @@ def _do_forgot_password(req): # {ClientId, Username, ConfirmationCode, Password} def _do_confirm_forgot_password(req): body = _json_body(req) - username = body.get("Username", "") + username = body.get("Username") or "" if username == "": return _cognito_err("InvalidParameterException", "Username is required") @@ -305,7 +305,7 @@ def _do_confirm_forgot_password(req): # {AccessToken} def _do_global_sign_out(req): body = _json_body(req) - access_token = body.get("AccessToken", "") + access_token = body.get("AccessToken") or "" if access_token == "": return _cognito_err("NotAuthorizedException", @@ -340,7 +340,7 @@ def _do_admin_user_global_sign_out(req): # GlobalSignOut (NotAuthorizedException). def _do_get_user(req): body = _json_body(req) - access_token = body.get("AccessToken", "") + access_token = body.get("AccessToken") or "" if access_token == "": return _cognito_err("NotAuthorizedException", @@ -393,12 +393,12 @@ def _do_list_users(req): # {UserPoolId, Username, UserAttributes, [TemporaryPassword]} def _do_admin_create_user(req): body = _json_body(req) - username = body.get("Username", "") + username = body.get("Username") or "" if username == "": return _cognito_err("InvalidParameterException", "Username is required") - temp_password = body.get("TemporaryPassword", "") + temp_password = body.get("TemporaryPassword") or "" if temp_password == "": temp_password = "TempPass" + "1A!" @@ -480,7 +480,7 @@ def _initiate_auth_flow(body, is_admin): auth_params = body.get("AuthParameters", {}) if type(auth_params) != "dict": auth_params = {} - client_id = body.get("ClientId", "") + client_id = body.get("ClientId") or "" if client_id == "": client_id = "mock-client-id" diff --git a/adapters/bluesky-style/scripts/records.star b/adapters/bluesky-style/scripts/records.star index a75c4667..ac6c8a45 100644 --- a/adapters/bluesky-style/scripts/records.star +++ b/adapters/bluesky-style/scripts/records.star @@ -35,7 +35,7 @@ def on_create_record(req): "message": "repo must match the authenticated session", }) - collection = body.get("collection", "") + collection = body.get("collection") or "" if collection == "": return respond(400, { "error": "InvalidRequest", diff --git a/adapters/bluesky-style/scripts/session.star b/adapters/bluesky-style/scripts/session.star index 257afe50..f1d9087d 100644 --- a/adapters/bluesky-style/scripts/session.star +++ b/adapters/bluesky-style/scripts/session.star @@ -17,8 +17,8 @@ def on_create_session(req): body = req["body"] if body == None: body = {} - identifier = body.get("identifier", "") - password = body.get("password", "") + identifier = body.get("identifier") or "" + password = body.get("password") or "" if identifier == "" or password == "": return respond(400, { diff --git a/adapters/braintree-style/scripts/webhooks.star b/adapters/braintree-style/scripts/webhooks.star index 49b159b9..b86b36f0 100644 --- a/adapters/braintree-style/scripts/webhooks.star +++ b/adapters/braintree-style/scripts/webhooks.star @@ -67,8 +67,8 @@ def _register_webhook(req, body): # notification (the simulator accepts any non-empty bt_signature/bt_payload — # real verification is done with the merchant keypair, see lib.star). def _verify_inbound(body): - bt_sig = body.get("bt_signature", "") - bt_payload = body.get("bt_payload", "") + bt_sig = body.get("bt_signature") or "" + bt_payload = body.get("bt_payload") or "" if bt_sig == None: bt_sig = "" diff --git a/adapters/cloudflare-style/scripts/d1.star b/adapters/cloudflare-style/scripts/d1.star index fff9dc8a..ec5c23cc 100644 --- a/adapters/cloudflare-style/scripts/d1.star +++ b/adapters/cloudflare-style/scripts/d1.star @@ -57,7 +57,7 @@ def on_create_database(req): if body == None: return _cf_err(400, _D1_ERR, "Invalid request body.") - name = body.get("name", "") + name = body.get("name") or "" if name == None: name = "" if name == "": @@ -143,7 +143,7 @@ def on_query_database(req): if body == None: return _cf_err(400, _D1_ERR, "Missing request body.") - sql = body.get("sql", "") + sql = body.get("sql") or "" if sql == None: sql = "" if sql == "": diff --git a/adapters/cloudflare-style/scripts/r2.star b/adapters/cloudflare-style/scripts/r2.star index a41feacb..a9cdcd09 100644 --- a/adapters/cloudflare-style/scripts/r2.star +++ b/adapters/cloudflare-style/scripts/r2.star @@ -45,7 +45,7 @@ def on_create_bucket(req): if body == None: return _cf_err(400, 10004, "Invalid request body.") - name = body.get("name", "") + name = body.get("name") or "" if name == None: name = "" if name == "": diff --git a/adapters/cloudflare-style/scripts/zones.star b/adapters/cloudflare-style/scripts/zones.star index 848dcb07..fcfff966 100644 --- a/adapters/cloudflare-style/scripts/zones.star +++ b/adapters/cloudflare-style/scripts/zones.star @@ -47,7 +47,7 @@ def on_create_zone(req): if body == None: return _cf_err(400, 1003, "Invalid or missing zone.") - name = body.get("name", "") + name = body.get("name") or "" if name == None: name = "" if name == "": diff --git a/adapters/discord-style/scripts/oauth.star b/adapters/discord-style/scripts/oauth.star index 1ab8e4c7..04dcb495 100644 --- a/adapters/discord-style/scripts/oauth.star +++ b/adapters/discord-style/scripts/oauth.star @@ -103,8 +103,8 @@ def on_token(req): if grant_type == "refresh_token": presented = body.get("refresh_token", "") - client_id = body.get("client_id", "") - client_secret = body.get("client_secret", "") + client_id = body.get("client_id") or "" + client_secret = body.get("client_secret") or "" if client_id == "" or client_secret == "": return respond(400, { diff --git a/adapters/drive-style/scripts/oauth.star b/adapters/drive-style/scripts/oauth.star index b5ca3279..394f265e 100644 --- a/adapters/drive-style/scripts/oauth.star +++ b/adapters/drive-style/scripts/oauth.star @@ -126,8 +126,8 @@ def on_token(req): if grant_type == "refresh_token": presented = body.get("refresh_token", "") - client_id = body.get("client_id", "") - client_secret = body.get("client_secret", "") + client_id = body.get("client_id") or "" + client_secret = body.get("client_secret") or "" if client_id == "" or client_secret == "": return respond(400, {"error": "invalid_client", "error_description": "missing client creds"}) diff --git a/adapters/entra-id-style/scripts/oauth.star b/adapters/entra-id-style/scripts/oauth.star index fe444703..eb4e0b6a 100644 --- a/adapters/entra-id-style/scripts/oauth.star +++ b/adapters/entra-id-style/scripts/oauth.star @@ -128,7 +128,7 @@ def on_token(req): # --- refresh_token grant --- if grant_type == "refresh_token": presented = body.get("refresh_token", "") - client_id = body.get("client_id", "") + client_id = body.get("client_id") or "" client_secret = body.get("client_secret", "") if client_id == "": diff --git a/adapters/firebase-style/scripts/auth.star b/adapters/firebase-style/scripts/auth.star index 2d8fa979..c252b033 100644 --- a/adapters/firebase-style/scripts/auth.star +++ b/adapters/firebase-style/scripts/auth.star @@ -80,7 +80,7 @@ def on_securetoken(req): if grant_type != "refresh_token": return _err(400, 400, "Only grant_type=refresh_token is supported (INVALID_GRANT_TYPE).", "INVALID_ARGUMENT") - presented = body.get("refresh_token", "") + presented = body.get("refresh_token") or "" if presented == "": return _err(400, 400, "refresh_token is required (MISSING_REFRESH_TOKEN).", "INVALID_ARGUMENT") @@ -134,8 +134,8 @@ def on_relyingparty(req): # _do_sign_in validates email/password against stored users and issues tokens. def _do_sign_in(body, version): - email = body.get("email", "") - password = body.get("password", "") + email = body.get("email") or "" + password = body.get("password") or "" if email == "" or password == "": return _err(400, 400, "MISSING_EMAIL_OR_PASSWORD", "INVALID_ARGUMENT") @@ -157,7 +157,7 @@ def _do_sign_in(body, version): # _do_sign_up creates a new user and issues tokens. def _do_sign_up(body, version): - email = body.get("email", "") + email = body.get("email") or "" password = body.get("password", "") display_name = body.get("displayName", "") diff --git a/adapters/firebase-style/scripts/fcm.star b/adapters/firebase-style/scripts/fcm.star index 2bee89d7..fbe4213d 100644 --- a/adapters/firebase-style/scripts/fcm.star +++ b/adapters/firebase-style/scripts/fcm.star @@ -104,7 +104,7 @@ def on_subscribe(req): tokens = body.get("tokens", None) if tokens == None: - t = body.get("token", "") + t = body.get("token") or "" if t == "": return _err(400, 400, "token or tokens is required (MISSING_TOKEN).", "INVALID_ARGUMENT") tokens = [t] @@ -130,7 +130,7 @@ def on_unsubscribe(req): tokens = body.get("tokens", None) if tokens == None: - t = body.get("token", "") + t = body.get("token") or "" if t == "": return _err(400, 400, "token or tokens is required (MISSING_TOKEN).", "INVALID_ARGUMENT") tokens = [t] diff --git a/adapters/firebase-style/scripts/firestore.star b/adapters/firebase-style/scripts/firestore.star index 84c4d596..7e7a6b0e 100644 --- a/adapters/firebase-style/scripts/firestore.star +++ b/adapters/firebase-style/scripts/firestore.star @@ -101,7 +101,7 @@ def _create_path(req, project, path): if doc_id == None: doc_id = "" if doc_id == "": - doc_id = body.get("documentId", "") + doc_id = body.get("documentId") or "" if doc_id == None: doc_id = "" diff --git a/adapters/google-admin-style/scripts/groups.star b/adapters/google-admin-style/scripts/groups.star index 199ccad4..d96fdac6 100644 --- a/adapters/google-admin-style/scripts/groups.star +++ b/adapters/google-admin-style/scripts/groups.star @@ -49,7 +49,7 @@ def on_create_group(req): if body == None: body = {} - email = body.get("email", "") + email = body.get("email") or "" if email == "": return respond(400, { "error": { @@ -181,7 +181,7 @@ def on_add_member(req): if body == None: body = {} - email = body.get("email", "") + email = body.get("email") or "" if email == "": return respond(400, _invalid("email is required")) diff --git a/adapters/google-admin-style/scripts/users.star b/adapters/google-admin-style/scripts/users.star index 2dacf919..02bfe502 100644 --- a/adapters/google-admin-style/scripts/users.star +++ b/adapters/google-admin-style/scripts/users.star @@ -54,7 +54,7 @@ def on_create_user(req): seq = store_kv_incr("gadmin", "user_seq") uid = "10" + _pad10(seq) - email = body.get("primaryEmail", "") + email = body.get("primaryEmail") or "" if email == "": email = "user" + str(seq) + "@mock-domain.com" diff --git a/adapters/google-iam-style/scripts/oauth.star b/adapters/google-iam-style/scripts/oauth.star index dd2603b3..e41a811d 100644 --- a/adapters/google-iam-style/scripts/oauth.star +++ b/adapters/google-iam-style/scripts/oauth.star @@ -28,7 +28,7 @@ def on_jwt_exchange(req): "error_description": "Only jwt-bearer grant is supported.", }) - assertion = body.get("assertion", "") + assertion = body.get("assertion") or "" if assertion == "": return respond(400, { "error": "invalid_grant", diff --git a/adapters/google-iam-style/scripts/roles.star b/adapters/google-iam-style/scripts/roles.star index efc72b1b..8a0bcc76 100644 --- a/adapters/google-iam-style/scripts/roles.star +++ b/adapters/google-iam-style/scripts/roles.star @@ -33,7 +33,7 @@ def on_query_grantable_roles(req): if body == None: body = {} - full_resource_name = body.get("fullResourceName", "") + full_resource_name = body.get("fullResourceName") or "" if full_resource_name == "": return respond(400, { "error": { diff --git a/adapters/google-style/scripts/oauth.star b/adapters/google-style/scripts/oauth.star index 790f790b..7d9a725c 100644 --- a/adapters/google-style/scripts/oauth.star +++ b/adapters/google-style/scripts/oauth.star @@ -109,8 +109,8 @@ def on_token(req): if grant_type == "refresh_token": presented = body.get("refresh_token", "") - client_id = body.get("client_id", "") - client_secret = body.get("client_secret", "") + client_id = body.get("client_id") or "" + client_secret = body.get("client_secret") or "" if client_id == "" or client_secret == "": return respond(400, {"error": "invalid_client", "error_description": "missing client creds"}) diff --git a/adapters/hn-style/scripts/auth.star b/adapters/hn-style/scripts/auth.star index 38d86a7b..dbb01bae 100644 --- a/adapters/hn-style/scripts/auth.star +++ b/adapters/hn-style/scripts/auth.star @@ -13,7 +13,7 @@ def on_login(req): body = req["body"] if body == None: body = {} - acct = body.get("acct", "") + acct = body.get("acct") or "" pw = body.get("pw", "") if acct == "": diff --git a/adapters/hn-style/scripts/submit.star b/adapters/hn-style/scripts/submit.star index 2fbedfb8..85e3ce04 100644 --- a/adapters/hn-style/scripts/submit.star +++ b/adapters/hn-style/scripts/submit.star @@ -26,7 +26,7 @@ def on_submit(req): body = req["body"] if body == None: body = {} - title = body.get("title", "") + title = body.get("title") or "" if title == "": title = "(no title)" url = body.get("url", "") diff --git a/adapters/instagram-style/scripts/publish.star b/adapters/instagram-style/scripts/publish.star index 1f7b7f1b..46972ef4 100644 --- a/adapters/instagram-style/scripts/publish.star +++ b/adapters/instagram-style/scripts/publish.star @@ -35,8 +35,8 @@ def on_create(req): body = req["body"] if body == None: body = {} - image_url = body.get("image_url", "") - video_url = body.get("video_url", "") + image_url = body.get("image_url") or "" + video_url = body.get("video_url") or "" caption = body.get("caption", "") if image_url == "" and video_url == "": diff --git a/adapters/jira-style/scripts/issue.star b/adapters/jira-style/scripts/issue.star index 198c2aa2..7fe929ac 100644 --- a/adapters/jira-style/scripts/issue.star +++ b/adapters/jira-style/scripts/issue.star @@ -284,7 +284,7 @@ def on_add_comment(req): return _not_found() body = _get_body(req) - comment_text = body.get("body", "") + comment_text = body.get("body") or "" if comment_text == "": return _jira_error(400, "Comment body is required", {"comment": "Comment body can not be empty!"}) @@ -340,7 +340,7 @@ def on_update_comment(req): return _not_found() body = _get_body(req) - comment_text = body.get("body", "") + comment_text = body.get("body") or "" if comment_text == "": return _jira_error(400, "Comment body is required", {"comment": "Comment body can not be empty!"}) diff --git a/adapters/jira-style/scripts/webhook.star b/adapters/jira-style/scripts/webhook.star index 47d79314..a63d62c0 100644 --- a/adapters/jira-style/scripts/webhook.star +++ b/adapters/jira-style/scripts/webhook.star @@ -22,7 +22,7 @@ def on_register_webhook(req): body = _get_body(req) - url = body.get("url", "") + url = body.get("url") or "" if url == None: url = "" if url == "": diff --git a/adapters/jumio-style/scripts/scans.star b/adapters/jumio-style/scripts/scans.star index 419304b6..cddf30f8 100644 --- a/adapters/jumio-style/scripts/scans.star +++ b/adapters/jumio-style/scripts/scans.star @@ -31,7 +31,7 @@ def on_create_scan(req): # Failure injection: simulate_fail, or an explicit reject reason (which # implies failure — see README). - reject_reason = body.get("simulate_reject_reason", "") + reject_reason = body.get("simulate_reject_reason") or "" if reject_reason == None: reject_reason = "" fail = body.get("simulate_fail", False) == True or reject_reason != "" diff --git a/adapters/linkedin-style/scripts/oauth.star b/adapters/linkedin-style/scripts/oauth.star index dfb67efc..237aaac4 100644 --- a/adapters/linkedin-style/scripts/oauth.star +++ b/adapters/linkedin-style/scripts/oauth.star @@ -91,8 +91,8 @@ def on_access_token(req): if grant_type == "refresh_token": presented = body.get("refresh_token", "") - client_id = body.get("client_id", "") - client_secret = body.get("client_secret", "") + client_id = body.get("client_id") or "" + client_secret = body.get("client_secret") or "" if client_id == "" or client_secret == "": return respond(400, {"error": "invalid_client", "error_description": "missing client creds"}) diff --git a/adapters/microsoft-graph-style/scripts/drive.star b/adapters/microsoft-graph-style/scripts/drive.star index c6bb7d77..77e85170 100644 --- a/adapters/microsoft-graph-style/scripts/drive.star +++ b/adapters/microsoft-graph-style/scripts/drive.star @@ -195,7 +195,7 @@ def _create_folder(req, parent_id): body = req["body"] if body == None: body = {} - name = body.get("name", "") + name = body.get("name") or "" if name == "" or body.get("folder") == None: return _err("invalidRequest", 400, "A folder item requires 'name' and a 'folder' facet.") diff --git a/adapters/microsoft-graph-style/scripts/subscriptions.star b/adapters/microsoft-graph-style/scripts/subscriptions.star index 90d83575..12077720 100644 --- a/adapters/microsoft-graph-style/scripts/subscriptions.star +++ b/adapters/microsoft-graph-style/scripts/subscriptions.star @@ -33,9 +33,9 @@ def on_create_subscription(req): if body == None: body = {} - notification_url = body.get("notificationUrl", "") - resource = body.get("resource", "") - change_type = body.get("changeType", "") + notification_url = body.get("notificationUrl") or "" + resource = body.get("resource") or "" + change_type = body.get("changeType") or "" if notification_url == "" or resource == "" or change_type == "": return _err("ValidationError", 400, "notificationUrl, resource and changeType are required.") diff --git a/adapters/netsuite-style/scripts/query.star b/adapters/netsuite-style/scripts/query.star index 3046f70e..0f13fcb6 100644 --- a/adapters/netsuite-style/scripts/query.star +++ b/adapters/netsuite-style/scripts/query.star @@ -17,7 +17,7 @@ def on_suiteql(req): return err body = _get_body(req) - q = body.get("q", "") + q = body.get("q") or "" if q == "": return _netsuite_error(400, "Bad Request", "INVALID_REQUEST", "The 'q' field is required in the request body.") diff --git a/adapters/onfido-style/scripts/applicants.star b/adapters/onfido-style/scripts/applicants.star index 61ec1174..947239eb 100644 --- a/adapters/onfido-style/scripts/applicants.star +++ b/adapters/onfido-style/scripts/applicants.star @@ -13,8 +13,8 @@ def on_create_applicant(req): if body == None: body = {} - first_name = body.get("first_name", "") - last_name = body.get("last_name", "") + first_name = body.get("first_name") or "" + last_name = body.get("last_name") or "" dob = body.get("dob", "") if first_name == "" or last_name == "": diff --git a/adapters/onfido-style/scripts/checks.star b/adapters/onfido-style/scripts/checks.star index e8a04de8..36e19a84 100644 --- a/adapters/onfido-style/scripts/checks.star +++ b/adapters/onfido-style/scripts/checks.star @@ -14,7 +14,7 @@ def on_create_check(req): if body == None: body = {} - applicant_id = body.get("applicant_id", "") + applicant_id = body.get("applicant_id") or "" report_names = body.get("report_names", []) if applicant_id == "": diff --git a/adapters/onfido-style/scripts/documents.star b/adapters/onfido-style/scripts/documents.star index 782eaa1f..8361073c 100644 --- a/adapters/onfido-style/scripts/documents.star +++ b/adapters/onfido-style/scripts/documents.star @@ -15,7 +15,7 @@ def on_upload_document(req): if body == None: body = {} - applicant_id = body.get("applicant_id", "") + applicant_id = body.get("applicant_id") or "" if applicant_id == "": return respond(422, _err("validation_error", "applicant_id is required", { "applicant_id": ["can't be blank"], @@ -61,7 +61,7 @@ def on_upload_live_photo(req): if body == None: body = {} - applicant_id = body.get("applicant_id", "") + applicant_id = body.get("applicant_id") or "" if applicant_id == "": return respond(422, _err("validation_error", "applicant_id is required", { "applicant_id": ["can't be blank"], diff --git a/adapters/persona-style/scripts/inquiries.star b/adapters/persona-style/scripts/inquiries.star index e26fa0a7..d3ef8a26 100644 --- a/adapters/persona-style/scripts/inquiries.star +++ b/adapters/persona-style/scripts/inquiries.star @@ -22,8 +22,8 @@ def on_create_inquiry(req): if body == None: body = {} - template_id = body.get("template_id", "") - reference_id = body.get("reference_id", "") + template_id = body.get("template_id") or "" + reference_id = body.get("reference_id") or "" if template_id == "" or reference_id == "": return respond(400, _jsonapi_err(400, "invalid_request", "template_id and reference_id are required")) diff --git a/adapters/photos-style/scripts/oauth.star b/adapters/photos-style/scripts/oauth.star index c083a4c9..31a671b0 100644 --- a/adapters/photos-style/scripts/oauth.star +++ b/adapters/photos-style/scripts/oauth.star @@ -96,8 +96,8 @@ def on_token(req): if grant_type == "refresh_token": presented = body.get("refresh_token", "") - client_id = body.get("client_id", "") - client_secret = body.get("client_secret", "") + client_id = body.get("client_id") or "" + client_secret = body.get("client_secret") or "" if client_id == "" or client_secret == "": return respond(400, {"error": "invalid_client", "error_description": "missing client creds"}) diff --git a/adapters/plaid-style/scripts/institutions.star b/adapters/plaid-style/scripts/institutions.star index f10f0cdb..59eda394 100644 --- a/adapters/plaid-style/scripts/institutions.star +++ b/adapters/plaid-style/scripts/institutions.star @@ -69,7 +69,7 @@ def on_get_institution_by_id(req): if body == None: body = {} - institution_id = body.get("institution_id", "") + institution_id = body.get("institution_id") or "" if institution_id == "": return respond(400, { "display_message": None, diff --git a/adapters/plaid-style/scripts/item.star b/adapters/plaid-style/scripts/item.star index 6cfc8d63..37ff5e16 100644 --- a/adapters/plaid-style/scripts/item.star +++ b/adapters/plaid-style/scripts/item.star @@ -17,7 +17,7 @@ def on_exchange_public_token(req): if body == None: body = {} - public_token = body.get("public_token", "") + public_token = body.get("public_token") or "" if public_token == "": return respond(400, { "display_message": None, @@ -42,7 +42,7 @@ def on_exchange_public_token(req): # started the Link flow, the public_token must belong to that session. # Mismatched pairs are rejected the way Plaid rejects cross-session # artifacts (INVALID_PRODUCT / INVALID_FIELD). - link_token = body.get("link_token", "") + link_token = body.get("link_token") or "" if link_token != "": if pt_doc.get("link_token", "") != link_token: return respond(400, { diff --git a/adapters/plaid-style/scripts/lib.star b/adapters/plaid-style/scripts/lib.star index af0a1020..8091c1c0 100644 --- a/adapters/plaid-style/scripts/lib.star +++ b/adapters/plaid-style/scripts/lib.star @@ -14,8 +14,8 @@ def _check_auth(req): body = req.get("body") if body != None: - cid = body.get("client_id", "") - secret = body.get("secret", "") + cid = body.get("client_id") or "" + secret = body.get("secret") or "" if cid != "" and secret != "": return None diff --git a/adapters/plaid-style/scripts/sandbox.star b/adapters/plaid-style/scripts/sandbox.star index 5b1635f2..cf38415e 100644 --- a/adapters/plaid-style/scripts/sandbox.star +++ b/adapters/plaid-style/scripts/sandbox.star @@ -31,7 +31,7 @@ def on_create_public_token(req): if body == None: body = {} - institution_id = body.get("institution_id", "") + institution_id = body.get("institution_id") or "" ic = store_collection("institutions") inst = ic.get(institution_id) if institution_id == "" or inst == None: @@ -49,7 +49,7 @@ def on_create_public_token(req): # When a link_token is supplied it must be a live Link session; the minted # public_token is then bound to that session for exchange verification. - link_token = body.get("link_token", "") + link_token = body.get("link_token") or "" if link_token != "": lc = store_collection("link_tokens") if lc.get(link_token) == None: diff --git a/adapters/printful-style/scripts/orders.star b/adapters/printful-style/scripts/orders.star index bf0f5827..1b461a56 100644 --- a/adapters/printful-style/scripts/orders.star +++ b/adapters/printful-style/scripts/orders.star @@ -153,7 +153,7 @@ def on_update_order(req): if body == None: body = {} - new_status = body.get("status", "") + new_status = body.get("status") or "" if new_status != "": doc["status"] = new_status diff --git a/adapters/printful-style/scripts/webhooks.star b/adapters/printful-style/scripts/webhooks.star index 1eed9f30..e589b27d 100644 --- a/adapters/printful-style/scripts/webhooks.star +++ b/adapters/printful-style/scripts/webhooks.star @@ -39,7 +39,7 @@ def on_set_webhooks(req): if body == None: body = {} - url = body.get("url", "") + url = body.get("url") or "" if url == "": return respond(400, { "error": {"message": "url is required", "code": 400}, diff --git a/adapters/psd2-style/scripts/lib.star b/adapters/psd2-style/scripts/lib.star index adc1ccb6..9bd85e5f 100644 --- a/adapters/psd2-style/scripts/lib.star +++ b/adapters/psd2-style/scripts/lib.star @@ -362,7 +362,7 @@ def _sca_update(req, kind): method_id = body.get("authenticationMethodId", "") if method_id == None: method_id = "" - otp = body.get("scaAuthenticationData", "") + otp = body.get("scaAuthenticationData") or "" if otp == None: otp = "" diff --git a/adapters/psd2-style/scripts/payments.star b/adapters/psd2-style/scripts/payments.star index 9fbe3063..9c212c01 100644 --- a/adapters/psd2-style/scripts/payments.star +++ b/adapters/psd2-style/scripts/payments.star @@ -75,7 +75,7 @@ def on_create_payment(req): headers = {} consent_id = headers.get("Consent-ID", "") if consent_id == None or consent_id == "": - consent_id = body.get("consentId", "") + consent_id = body.get("consentId") or "" if consent_id == None: consent_id = "" diff --git a/adapters/qbo-style/scripts/customer.star b/adapters/qbo-style/scripts/customer.star index 8b4973ee..f86ef8ab 100644 --- a/adapters/qbo-style/scripts/customer.star +++ b/adapters/qbo-style/scripts/customer.star @@ -28,7 +28,7 @@ def on_create_customer(req): c = store_collection("customers") # UPDATE path: POST with an Id addresses an existing customer. - upd_id = body.get("Id", "") + upd_id = body.get("Id") or "" if upd_id != "": doc = c.get(upd_id) if doc == None: @@ -41,7 +41,7 @@ def on_create_customer(req): c.update(upd_id, doc) return respond(200, {"Customer": doc, "time": _now()}) - display_name = body.get("DisplayName", "") + display_name = body.get("DisplayName") or "" if display_name == "": return _fault(400, "610", "Required parameter missing", "DisplayName is required") diff --git a/adapters/qbo-style/scripts/invoice.star b/adapters/qbo-style/scripts/invoice.star index 141eaf08..bbe25cf4 100644 --- a/adapters/qbo-style/scripts/invoice.star +++ b/adapters/qbo-style/scripts/invoice.star @@ -72,7 +72,7 @@ def on_create_invoice(req): # API's void. An unknown Id is the usual 620 Object Not Found fault; a missing # Id is a 610 parameter fault. def _void_invoice(body): - inv_id = body.get("Id", "") + inv_id = body.get("Id") or "" if inv_id == "": return _fault(400, "610", "Required parameter missing", "Id is required to void an invoice") diff --git a/adapters/qbo-style/scripts/lib.star b/adapters/qbo-style/scripts/lib.star index e1d490ef..d5145a06 100644 --- a/adapters/qbo-style/scripts/lib.star +++ b/adapters/qbo-style/scripts/lib.star @@ -106,7 +106,7 @@ def _get_query(req): # POST: body field body = req.get("body") if body != None: - val = body.get("query", "") + val = body.get("query") or "" if val != "": return val return "" diff --git a/adapters/reddit-style/scripts/submit.star b/adapters/reddit-style/scripts/submit.star index 2d8f907f..b9379b37 100644 --- a/adapters/reddit-style/scripts/submit.star +++ b/adapters/reddit-style/scripts/submit.star @@ -46,8 +46,8 @@ def on_submit(req): body = req["body"] if body == None: body = {} - sr = body.get("sr", "") - title = body.get("title", "") + sr = body.get("sr") or "" + title = body.get("title") or "" if sr == "": return respond(200, {"json": {"errors": [["SUBREDDIT_REQUIRED", "a subreddit is required", "sr"]], "data": {}}}) diff --git a/adapters/revenuecat-style/scripts/receipts.star b/adapters/revenuecat-style/scripts/receipts.star index d8571fc6..2c9c1d9a 100644 --- a/adapters/revenuecat-style/scripts/receipts.star +++ b/adapters/revenuecat-style/scripts/receipts.star @@ -92,7 +92,7 @@ def on_post_receipt(req): # real API's mechanism, checked in common casings) or the body's platform # field. Returns "" when absent. def _platform(req, body): - p = body.get("platform", "") + p = body.get("platform") or "" if p == None: p = "" if p != "": diff --git a/adapters/salesforce-style/scripts/oauth.star b/adapters/salesforce-style/scripts/oauth.star index ed997ce9..5b3ef136 100644 --- a/adapters/salesforce-style/scripts/oauth.star +++ b/adapters/salesforce-style/scripts/oauth.star @@ -39,20 +39,20 @@ def on_token(req): return _issue_token(username, _claim_str(claims.get("iss", None)), None, False) if grant_type == "password": - username = body.get("username", "") - password = body.get("password", "") + username = body.get("username") or "" + password = body.get("password") or "" if username == "" or password == "" or client_id == "": return _oauth_error("invalid_request", "missing required parameters") return _issue_token(username, client_id) if grant_type == "authorization_code": - code = body.get("code", "") + code = body.get("code") or "" if code == "": return _oauth_error("invalid_grant", "invalid or expired code") return _issue_token("user@mock.org", client_id) if grant_type == "refresh_token": - refresh_token = body.get("refresh_token", "") + refresh_token = body.get("refresh_token") or "" username = store_kv_get("salesforce", "refresh_" + refresh_token) if refresh_token == "" or username == None: return _oauth_error("invalid_grant", "invalid refresh_token") diff --git a/adapters/salesforce-style/scripts/sobjects.star b/adapters/salesforce-style/scripts/sobjects.star index 59edf8ac..c144d9c0 100644 --- a/adapters/salesforce-style/scripts/sobjects.star +++ b/adapters/salesforce-style/scripts/sobjects.star @@ -139,7 +139,7 @@ def on_create(req): return _sf_error(404, "The requested resource does not exist", "NOT_FOUND") body = _get_body(req) - name = body.get("Name", "") + name = body.get("Name") or "" if name == "": return _sf_error(400, "Required field missing: [Name]", "REQUIRED_FIELD_MISSING") diff --git a/adapters/sendgrid-style/scripts/webhooks.star b/adapters/sendgrid-style/scripts/webhooks.star index 246244e9..2a4ff393 100644 --- a/adapters/sendgrid-style/scripts/webhooks.star +++ b/adapters/sendgrid-style/scripts/webhooks.star @@ -27,7 +27,7 @@ def on_update_settings(req): enabled = body.get("enabled", False) if enabled == None: enabled = False - url = body.get("url", "") + url = body.get("url") or "" if url == None: url = "" diff --git a/adapters/shopify-style/scripts/oauth.star b/adapters/shopify-style/scripts/oauth.star index 3522dbf5..7eacfab6 100644 --- a/adapters/shopify-style/scripts/oauth.star +++ b/adapters/shopify-style/scripts/oauth.star @@ -58,9 +58,9 @@ def on_access_token(req): body = req["body"] if body == None: body = {} - code = body.get("code", "") - client_id = body.get("client_id", "") - client_secret = body.get("client_secret", "") + code = body.get("code") or "" + client_id = body.get("client_id") or "" + client_secret = body.get("client_secret") or "" if code == "" or client_id == "" or client_secret == "": return respond(400, {"error": "invalid_request"}) diff --git a/adapters/slack-style/scripts/conversations.star b/adapters/slack-style/scripts/conversations.star index 7356efde..712de914 100644 --- a/adapters/slack-style/scripts/conversations.star +++ b/adapters/slack-style/scripts/conversations.star @@ -22,7 +22,7 @@ def on_create_conversation(req): if body == None: body = {} - name = body.get("name", "") + name = body.get("name") or "" if name == None: name = "" diff --git a/adapters/slack-style/scripts/events.star b/adapters/slack-style/scripts/events.star index c52ba868..7fff9b5f 100644 --- a/adapters/slack-style/scripts/events.star +++ b/adapters/slack-style/scripts/events.star @@ -28,7 +28,7 @@ def on_set_events_url(req): if body == None: body = {} - url = body.get("url", "") + url = body.get("url") or "" if url == None: url = "" if url == "": diff --git a/adapters/slack-style/scripts/reactions.star b/adapters/slack-style/scripts/reactions.star index 633bfc2b..964317c5 100644 --- a/adapters/slack-style/scripts/reactions.star +++ b/adapters/slack-style/scripts/reactions.star @@ -18,7 +18,7 @@ def on_add_reaction(req): channel = body.get("channel", "") if channel == None: channel = "" - timestamp = body.get("timestamp", "") + timestamp = body.get("timestamp") or "" if timestamp == None: timestamp = "" name = body.get("name", "") diff --git a/adapters/square-style/scripts/oauth.star b/adapters/square-style/scripts/oauth.star index d7b79b4e..bb64b69a 100644 --- a/adapters/square-style/scripts/oauth.star +++ b/adapters/square-style/scripts/oauth.star @@ -10,7 +10,7 @@ def on_token(req): body = {} # Square expects form-encoded body for OAuth. - grant_type = body.get("grant_type", "") + grant_type = body.get("grant_type") or "" if grant_type == None: grant_type = "" diff --git a/adapters/stripe-style/scripts/subscriptions.star b/adapters/stripe-style/scripts/subscriptions.star index b25e692e..62d472db 100644 --- a/adapters/stripe-style/scripts/subscriptions.star +++ b/adapters/stripe-style/scripts/subscriptions.star @@ -883,7 +883,7 @@ def on_update_subscription(req): pm_set = False if body.get("default_payment_method", None) != None: - pm = body.get("default_payment_method", "") + pm = body.get("default_payment_method") or "" if pm != "" and not _sub_pm_exists(pm): return _not_found("payment_method", pm) if pm == "": diff --git a/adapters/threads-style/scripts/oauth.star b/adapters/threads-style/scripts/oauth.star index 9f9dddfd..0f57a554 100644 --- a/adapters/threads-style/scripts/oauth.star +++ b/adapters/threads-style/scripts/oauth.star @@ -48,7 +48,7 @@ def on_access_token(req): grant_type = body.get("grant_type", "") if grant_type == "refresh_token": - rt = body.get("refresh_token", "") + rt = body.get("refresh_token") or "" uid = store_kv_get("threads", "refresh_" + rt) if rt == "" or uid == None: return respond(400, {"error": "invalid_grant", "error_description": "invalid refresh_token"}) diff --git a/adapters/threads-style/scripts/publish.star b/adapters/threads-style/scripts/publish.star index 1b0fa1a5..8493b7a2 100644 --- a/adapters/threads-style/scripts/publish.star +++ b/adapters/threads-style/scripts/publish.star @@ -33,7 +33,7 @@ def on_create(req): if body == None: body = {} media_type = body.get("media_type", "") - text = body.get("text", "") + text = body.get("text") or "" if media_type != "TEXT" or text == "": return respond(400, {"error": {"message": "media_type must be TEXT and text is required", "code": 100}}) diff --git a/adapters/x-articles-style/scripts/articles.star b/adapters/x-articles-style/scripts/articles.star index 70009316..88eba30d 100644 --- a/adapters/x-articles-style/scripts/articles.star +++ b/adapters/x-articles-style/scripts/articles.star @@ -32,7 +32,7 @@ def on_draft(req): if body == None: body = {} - title = body.get("title", "") + title = body.get("title") or "" if title == None: title = "" title = title.strip() diff --git a/adapters/x-articles-style/scripts/oauth.star b/adapters/x-articles-style/scripts/oauth.star index f0cd54c8..403b8c6c 100644 --- a/adapters/x-articles-style/scripts/oauth.star +++ b/adapters/x-articles-style/scripts/oauth.star @@ -93,7 +93,7 @@ def on_token(req): grant_type = body.get("grant_type", "") if grant_type == "refresh_token": - rt = body.get("refresh_token", "") + rt = body.get("refresh_token") or "" if rt == "" or store_kv_get("xarticles", "refresh_" + rt) == None: return respond(400, {"error": "invalid_grant"}) # Rotate: invalidate the presented refresh, issue a fresh pair. @@ -127,7 +127,7 @@ def on_token(req): return respond(400, {"error": "invalid_grant", "detail": "redirect_uri mismatch"}) # PKCE (relaxed — see module docstring): code_verifier must be present. - verifier = body.get("code_verifier", "") + verifier = body.get("code_verifier") or "" if verifier == "": return respond(400, {"error": "invalid_grant", "detail": "PKCE verifier mismatch"}) diff --git a/adapters/youtube-style/scripts/oauth.star b/adapters/youtube-style/scripts/oauth.star index 87052469..2a67a228 100644 --- a/adapters/youtube-style/scripts/oauth.star +++ b/adapters/youtube-style/scripts/oauth.star @@ -96,8 +96,8 @@ def on_token(req): if grant_type == "refresh_token": presented = body.get("refresh_token", "") - client_id = body.get("client_id", "") - client_secret = body.get("client_secret", "") + client_id = body.get("client_id") or "" + client_secret = body.get("client_secret") or "" if client_id == "" or client_secret == "": return respond(400, {"error": "invalid_client", "error_description": "missing client creds"}) diff --git a/adapters/zuora-style/scripts/query.star b/adapters/zuora-style/scripts/query.star index d97f9e2f..3f1cb3e0 100644 --- a/adapters/zuora-style/scripts/query.star +++ b/adapters/zuora-style/scripts/query.star @@ -15,7 +15,7 @@ def on_query(req): return err body = _get_body(req) - query = body.get("queryString", "") + query = body.get("queryString") or "" if query == None: query = "" diff --git a/internal/engine/aws_cognito_style_test.go b/internal/engine/aws_cognito_style_test.go index 46455adc..046401ee 100644 --- a/internal/engine/aws_cognito_style_test.go +++ b/internal/engine/aws_cognito_style_test.go @@ -260,6 +260,23 @@ func TestAWSCognitoStyleAdapter(t *testing.T) { t.Fatalf("UserConfirmed = %v, want bool", signUpResp["UserConfirmed"]) } + // A JSON-null required field is a missing field: Starlark decodes + // null to None and None == "" is False, so a plain emptiness guard + // used to pass and store null. Real Cognito rejects it. + body, status = cognitoPostTarget(t, base+"/", + "AWSCognitoIdentityProviderService.SignUp", + map[string]any{ + "ClientId": clientID, + "Username": nil, + "Password": signUpPassword, + }) + if status != 400 { + t.Fatalf("SignUp with null Username -> status %d, want 400; body %s", status, body) + } + if !strings.Contains(body, "InvalidParameterException") { + t.Fatalf("SignUp with null Username -> body %s, want InvalidParameterException", body) + } + // ===== Service API: ConfirmSignUp ===== body, status = cognitoPostTarget(t, base+"/", From c1da9ea6ac4c6b3bd56527f4dda8bfde06ef0963 Mon Sep 17 00:00:00 2001 From: Alessandro De Blasis Date: Mon, 17 Aug 2026 18:32:35 +0300 Subject: [PATCH 2/2] fix(review): the inline-guard shape the pairing rule missed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- adapters/powerplatform-style/scripts/dataverse.star | 2 +- adapters/salesforce-style/scripts/composite.star | 2 +- adapters/salesforce-style/scripts/sobjects.star | 2 +- adapters/square-style/scripts/refunds.star | 2 +- adapters/stripe-style/scripts/subscriptions.star | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/adapters/powerplatform-style/scripts/dataverse.star b/adapters/powerplatform-style/scripts/dataverse.star index 90d218cb..58ad7e99 100644 --- a/adapters/powerplatform-style/scripts/dataverse.star +++ b/adapters/powerplatform-style/scripts/dataverse.star @@ -204,7 +204,7 @@ def on_create_account(req): body = req["body"] if body == None: body = {} - if body.get("accountid", "") == "": + if (body.get("accountid") or "") == "": body["accountid"] = "acc-" + str(store_kv_incr("pp", "account_seq")) body["id"] = body["accountid"] _accounts().insert(body) diff --git a/adapters/salesforce-style/scripts/composite.star b/adapters/salesforce-style/scripts/composite.star index db5d1945..2a0916eb 100644 --- a/adapters/salesforce-style/scripts/composite.star +++ b/adapters/salesforce-style/scripts/composite.star @@ -223,7 +223,7 @@ def _process_sub_request(sub_req, prior): if method == "POST": # Same required-field rule as the direct create endpoint. - if sub_body.get("Name", "") == "": + if (sub_body.get("Name") or "") == "": return _sub_response(ref_id, 400, [{ "message": "Required field missing: [Name]", "errorCode": "REQUIRED_FIELD_MISSING", diff --git a/adapters/salesforce-style/scripts/sobjects.star b/adapters/salesforce-style/scripts/sobjects.star index c144d9c0..f1b81de3 100644 --- a/adapters/salesforce-style/scripts/sobjects.star +++ b/adapters/salesforce-style/scripts/sobjects.star @@ -313,7 +313,7 @@ def on_upsert(req): }) # No match -> insert. Same required-field rule as plain create. - if body.get("Name", "") == "": + if (body.get("Name") or "") == "": return _sf_error(400, "Required field missing: [Name]", "REQUIRED_FIELD_MISSING") record_id = _next_id(obj_type) diff --git a/adapters/square-style/scripts/refunds.star b/adapters/square-style/scripts/refunds.star index 664e842f..c817bf50 100644 --- a/adapters/square-style/scripts/refunds.star +++ b/adapters/square-style/scripts/refunds.star @@ -26,7 +26,7 @@ def on_create_refund(req): if body == None: body = {} - payment_id = body.get("payment_id", "") + payment_id = body.get("payment_id") or "" amount_money = body.get("amount_money", None) location_id = body.get("location_id", "") reason = body.get("reason", "") diff --git a/adapters/stripe-style/scripts/subscriptions.star b/adapters/stripe-style/scripts/subscriptions.star index 62d472db..0710853b 100644 --- a/adapters/stripe-style/scripts/subscriptions.star +++ b/adapters/stripe-style/scripts/subscriptions.star @@ -871,7 +871,7 @@ def on_update_subscription(req): doc["default_tax_rates"] = tax_rates changed = True if body.get("coupon", None) != None or body.get("promotion_code", None) != None: - if body.get("coupon", "") == "" and body.get("promotion_code", "") == "": + if (body.get("coupon") or "") == "" and (body.get("promotion_code") or "") == "": doc["discount"] = None changed = True else: