fix(TWO-25365): stop sending the merchant storefront URL as the buyer company website - #330
Conversation
… company website Luma's order-intent request set `buyer.company.website` to `window.BASE_URL` — the MERCHANT's own storefront URL, not the buying company's website. Every order intent therefore claimed the merchant's site as the buyer company's, which is wrong data under a field name that says something else. The field is not part of the request Luma is meant to send and nothing downstream reads it (the Hyva payment component never sent it), so it is removed rather than re-sourced from somewhere else. New Test/Js spec composes a real request body with `window.BASE_URL` deliberately set and asserts on the JSON that would go on the wire, so it cannot pass on a missing global; a second assertion pins the global itself as unused so the URL cannot reappear under another key. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…eview escapes Adversarial review round 1 found the spec passing on mutations it was meant to catch, and failing on one it should not: - the source-text guard matched the RAW file, so a comment explaining this very fix (which necessarily names the global) turned it red — failing on documentation rather than on code. Comments are stripped before matching now, same convention as the sibling spec. - the leak guard was scoped to `buyer`, so the merchant URL relocated to a top-level key beside `merchant_id`, read via a computed global, passed both tests. It now guards the whole body. - the `'website' in ...` assertion could never fail independently of the exact-shape assertion above it. Removed rather than left reading as coverage. Also drops a header claim about what the API does with the field — not verifiable from this repo — in favour of pointing at the ticket, and moves the fixture's product-image host off the storefront host so the widened guard trips on the defect rather than on a legitimate URL. Mutation battery: straight revert, computed-key + computed-global, and the relocated top-level `store_url` all fail the spec; a comment naming the global keeps it green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Round 2 mutated the spec's own guards rather than the fix, and got five passing reintroductions plus one false red: - the whole-body guard matched the storefront URL EXACTLY. Magento's base URL always ends in a slash, so normalising it is the most natural thing a reintroduction would do — stripping the trailing slash, dropping the scheme, percent-encoding and upper-casing all passed. Guarded on the bare host as well, case-insensitively; the host token survives every one of those transforms. - the key regex saw the object-literal form only, so a post-literal `…company.website = …` on a country branch the fixture never bills to passed both tests — precisely the case the comment claimed to cover. Now matches assignment too, and the `web_site` spelling. - the comment strip was general, which WEAKENS this check instead of breaking it: `//` inside a string literal deletes the rest of that line, and one `'/*'` string constant opens a fake comment swallowing 400+ real lines, hiding a plain global read in both cases. Narrowed to the two forms this file actually uses — JSDoc blocks and whole-line `//`. - `/BASE_URL/` also reddens on the unrelated local-dev env var names that are live vocabulary elsewhere in this module. Narrowed to the actual global, with the computed `window[...]` form guarded separately (this file has no legitimate computed window access). Ten-mutation battery, all behaving: the four normalisations, the post-literal assignment, the `web_site` spelling and both comment-strip exploits fail the spec; a whole-line comment and a JSDoc block naming the global keep it green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…op over-fitting
Round 3's substantive find: the spec guarded one route to the merchant
URL and there are two. This renderer also injects Magento's URL builder
and calls it elsewhere, and the shared harness double returns its
argument unchanged — so building the storefront root through it yielded
an empty string, invisible to every wire assertion, once under the
removed field's exact name. The builder is now mocked to resolve to the
real storefront root, as the browser does, and the existing body guard
covers both routes with no new assertion.
Three narrowing mistakes from the previous round, each proven by a
passing mutation:
- matching `window.BASE_URL` rather than the bare token let
`const { BASE_URL } = window` through. Back to the token: the
justification for narrowing was wrong anyway, since this file contains
no other name of that shape — the local-dev env vars live in shell and
PHP config, which this check never reads.
- `\b` before `web` fails after an underscore, so `company_website:` and
`merchant_website:` escaped — the likeliest names if the field moves
out of the company object. Anchor dropped.
- the fixture's product-image host needed the reason it is off-storefront
written down, since production images really are storefront-hosted and
a maintainer "fixing" the fixture would red the guard with no defect.
Also trimmed the rationale prose, which had grown longer than the
apparatus it explains, and renamed the second test — it stopped being
about one global two rounds ago.
Ten-mutation battery: both builder-route shapes, the destructured global
on an unexercised branch, the two underscore-prefixed key names, the
straight revert, the computed-and-normalised relocation and the
post-literal assignment all fail. A comment naming the global, and a
legitimate new URL-builder call, both stay green.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…] ban Two rounds of comment-stripping still left the false-red channel open: only whole-line and JSDoc comments were stripped, so appending `// no BASE_URL here` to a line of CODE — the most natural way to document this fix at the call site — reddened the check. That is the exact failure mode the strip exists to prevent. Trailing comments are stripped now, with a quote exclusion so the pattern declines to strip rather than risk eating a URL inside a string. The `window[...]` guard is removed. It outlawed a general JS construct across the whole file to cover one narrow case — a computed global read, on a branch no fixture exercises, under a key name no regex anticipates — and that case is strictly narrower than others this spec already accepts as out of reach. Banning the construct bought less than it cost the next person to touch the file. Also corrected the justification for the bare-token match: the claim that env-var names of that shape were the false-positive risk was wrong. A leading word boundary cannot match `TWO_API_BASE_URL` at all, since the boundary fails after an underscore — the same property the key regex below exploits deliberately. And the residual gaps are now written down rather than implied, so the guards are not read as more than they are. Eight-mutation battery: trailing and whole-line comments naming the global stay green; the straight revert, the underscore key, the builder route, the destructured global, `window.location.origin`, and a global read on a line whose string contains `//` all fail. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The exclusion class covered ' and " but not `, and this file composes
URLs in template literals. A template literal containing whitespace-//
on a line that also read the global would therefore have had its real
code deleted — silently weakening the token check, which is the exact
failure mode the exclusion exists to prevent. Latent rather than live:
no such line exists today.
Verified in node rather than reasoned: with the backtick in the class,
`a // ${window.BASE_URL}` survives the strip and the token check still
sees it; a genuine trailing comment is still removed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adversarial self-review — TWO-25365 (Luma)Five independent review rounds over a one-line production change. They found 14 issues; all fixed. Notably, only one finding was ever about the production change — the other 13 were about the spec written to cover it, most of them found by mutating the spec's own guards rather than the code under test. Status of the last commit, stated precisely: What the rounds foundRound 1 — two reviewers (correctness/blast-radius, test-vacuity):
Round 2 — mutated the round-1 guards, five passing reintroductions plus one false red:
Round 3 — the one substantive gap, plus three narrowing mistakes:
Round 4 — one apparatus defect, one cut:
Round 5 — one latent consequence of round 4's own fix: the exclusion class covered Convergence, since it matters more than the round count here. The findings moved steadily away from the code and into the spec's own apparatus: round 3 was the last one to touch coverage of the actual defect (the second route to the URL), round 4 cut an over-reaching assertion rather than adding one, and round 5's single finding was a latent flaw in round 4's own fix. That is a converging loop, not an oscillating one — but it is also the point at which a spec guarding a one-line deletion has absorbed more review than the deletion, which is why round 4's recommendation to delete an assertion was taken. Verified clean
Mutation batteriesThirty-two mutations across the rounds. Currently caught: straight revert · Deliberately green: a whole-line comment, a trailing comment and a JSDoc block naming the global and the field; a legitimate new URL-builder call. Documentation must not fail this spec. Deliberately not covered, so the guards aren't read as more than they are: the URL reappearing on an unexercised branch under a name no regex can anticipate ( Full suite green throughout: 30 suites, 385 tests. Reviewed by Claude. |
TWO-25365 (part 1 of 4 — Luma)
Luma's order-intent request composed
buyer.company.websitefromwindow.BASE_URL. That global is the merchant's own storefront URL, not the buying company's website — so every order intent sent the merchant's site under a field name that says something else.The field is not part of the request this renderer is meant to send. Grepped this repo for other readers or producers:
company.websiteappears nowhere else, and after this changeBASE_URLappears nowhere in the module's JS at all (theTWO_*_BASE_URLnames elsewhere are unrelated local-dev env vars read by PHP config). So the assignment is removed rather than re-sourced from somewhere else.Per the ticket, this is non-causal for any known bug — a data-correctness defect, not a live failure. Whether the API tolerates or ignores the field is not something this repo can attest to; TWO-25365 carries that reference.
Change
view/frontend/web/js/view/payment/method-renderer/gateway_method.js— drop thewebsitekey frombuyer.company.Test/Js/gateway-method-order-intent-request-body.test.js— new spec.Test
The new spec composes a real request body through
placeOrderIntent()with a recording$.ajaxdouble, and asserts on the JSON that would go on the wire — withwindow.BASE_URLdeliberately set, so it cannot pass on a missing sandbox global rather than on the composition. A second assertion pins the global itself as unused, so the merchant URL cannot reappear under a different key.Mutation-checked across five review rounds — see the review comment below for the batteries and what they cover. Full suite green (
npm run test:js— 30 suites, 385 tests).