Skip to content

fix(TWO-25365): stop sending the merchant storefront URL as the buyer company website - #330

Merged
dgjlindsay merged 6 commits into
stagingfrom
doug/TWO-25365-luma-drop-website
Aug 6, 2026
Merged

fix(TWO-25365): stop sending the merchant storefront URL as the buyer company website#330
dgjlindsay merged 6 commits into
stagingfrom
doug/TWO-25365-luma-drop-website

Conversation

@dgjlindsay

@dgjlindsay dgjlindsay commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

TWO-25365 (part 1 of 4 — Luma)

Luma's order-intent request composed buyer.company.website from window.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.website appears nowhere else, and after this change BASE_URL appears nowhere in the module's JS at all (the TWO_*_BASE_URL names 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 the website key from buyer.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 $.ajax double, and asserts on the JSON that would go on the wire — with window.BASE_URL deliberately 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).

dgjlindsay and others added 6 commits August 6, 2026 09:56
… 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>
@dgjlindsay

Copy link
Copy Markdown
Contributor Author

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: becd017 is a one-character fix to a character class, and it has not had an independent review round — the round-6 reviewer did not return. It was verified by execution instead (node, against the real file: the strip alters exactly one line, the known trailing comment, and a template literal containing // now survives it with the token check still seeing the global). That is evidence, but it is not a review round, and this comment does not claim otherwise.

What the rounds found

Round 1 — two reviewers (correctness/blast-radius, test-vacuity):

Finding Resolution
The source-text guard matched the raw file, so a comment explaining this very fix — which necessarily names the global — turned the spec red. Failing on documentation, not on code. Comments stripped before matching.
The leak guard was scoped to buyer; a mutation moving the URL to a top-level key beside merchant_id, read via a computed global, passed. Guard widened to the whole request body.
expect('website' in body.buyer.company).toBe(false) could never fail independently of the exact-shape assertion above it. Removed rather than left reading as coverage.
A header comment asserted what the API does with the field — not verifiable from this repo. Replaced with a pointer to the ticket.

Round 2 — mutated the round-1 guards, five passing reintroductions plus one false red:

Finding Resolution
The body guard matched the storefront URL exactly. The base URL always ends in a slash, so normalising it is the obvious move — trailing-slash strip, scheme strip, percent-encode and upper-case all passed. Also guards the bare host, case-insensitively.
The key regex saw the object-literal form only, so …company.website = … after the literal, on a country branch the fixture never bills to, passed both tests. Matches assignment too, and the web_site spelling.
The comment strip was general, which weakens the check rather than breaking it: // inside a string eats the rest of that line, and one '/*' constant opens a fake comment swallowing hundreds of lines. Narrowed to the forms this file actually uses.
/BASE_URL/ also reds on unrelated local-dev env-var names. Narrowed (then corrected again in round 3 — see below).

Round 3 — the one substantive gap, plus three narrowing mistakes:

Finding Resolution
The spec guarded one route to the merchant URL and there are two. This renderer also injects Magento's URL builder, and the shared harness double returns its argument unchanged — so building the storefront root through it yielded '', invisible to every wire assertion, once under the removed field's exact name. Builder mocked to resolve to the storefront root, as the browser does. The existing body guard then covers both routes with no new assertion.
Narrowing to window.BASE_URL let const { BASE_URL } = window through — and the justification for narrowing was factually wrong. Back to the bare token.
\b before web fails after an underscore, so company_website: and merchant_website: escaped. Anchor dropped.
Rationale prose had grown longer than the apparatus it explained; the second test's name had been stale for two rounds. Trimmed and renamed.

Round 4 — one apparatus defect, one cut:

Finding Resolution
Two rounds of comment-stripping still left the false-red channel open: trailing comments after code were never stripped, so // no BASE_URL here appended to a line of code — the most natural way to document this at the call site — reddened the check. Trailing form stripped, with a quote exclusion so it declines to strip rather than risk eating a URL in a string.
The window[...] ban outlawed a general JS construct across 2000 lines to cover a case strictly narrower than others the spec already accepts as out of reach. Deleted, and the residual gaps written down rather than implied.

Round 5 — one latent consequence of round 4's own fix: the exclusion class covered ' and " but not backtick, and this file composes URLs in template literals, so a template literal containing whitespace-// on a line also reading the global would have had its real code deleted. Fixed in becd017.

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

  • Zero remaining BASE_URL tokens of any kind in the module's JS, and zero producers or consumers of a company website field. This module's PHP order-create path composes the company object without it.
  • No doc, README, AGENTS.md or e2e spec describes the field — nothing stale left behind.
  • The trailing-comma removal is syntactically correct, and the sibling spec that regex-scrapes this same call site still passes.
  • Leak-gated before each of the five pushes: diff, commit bodies, branch name, PR title and body, all against a three-dot diff.

Mutation batteries

Thirty-two mutations across the rounds. Currently caught: straight revert · website: '' · computed key + computed global · relocation to buyer.representative · relocation to a top-level store_url · trailing-slash strip · scheme strip · percent-encode · upper-case · web_site · company_website · merchant_website · post-literal assignment on an unexercised country branch · destructured BASE_URL · both URL-builder shapes · window.location.origin · a global read on a line whose string contains // · a global read hidden behind a '/*' constant.

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 (homepage, say), or outside the body altogether (a request header). Both need a key-name oracle this spec cannot have.

Full suite green throughout: 30 suites, 385 tests.

Reviewed by Claude.

@dgjlindsay
dgjlindsay merged commit 553122d into staging Aug 6, 2026
27 checks passed
@dgjlindsay
dgjlindsay deleted the doug/TWO-25365-luma-drop-website branch August 6, 2026 10:49
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