Skip to content

fix(mrt-welcome-app): escape reflected user input in echo response (@W-23137861)#537

Draft
aditya-balachander wants to merge 8 commits into
developfrom
W-23137861-fix-reflected-html-injection-welcome-app
Draft

fix(mrt-welcome-app): escape reflected user input in echo response (@W-23137861)#537
aditya-balachander wants to merge 8 commits into
developfrom
W-23137861-fix-reflected-html-injection-welcome-app

Conversation

@aditya-balachander

Copy link
Copy Markdown

Summary

Fixes a reflected HTML injection vulnerability in the mrt-welcome-app diagnostic echo handler (W-23137861).

The welcome-app's echo handler renders incoming request data (path, query, headers) through an EJS template using the unescaped output tag <%- %> at packages/mrt-welcome-app/src/views/layout.html:118:

<pre class="code"><%- JSON.stringify(json_content, null, 2) %></pre>

Because JSON.stringify only escapes " and \, characters like <, >, and & pass through unchanged. When the rendered page is served as text/html, the browser parses any HTML supplied via the URL (e.g. ?value=<h1>sample<a href=https://example.com>link</a></h1>) as live markup. This is a textbook reflected HTML injection / XSS sink.

This PR switches the template to the HTML-escaping tag <%= %>, so reflected request data is rendered as text rather than HTML:

<pre class="code"><%= JSON.stringify(json_content, null, 2) %></pre>

A regression test is added that submits a payload containing <h1> and <a> markup and asserts:

  • The response does not contain live <h1> / <a> tags.
  • The response does contain the HTML-entity-escaped form (&lt;h1&gt;...).
  • The original input still round-trips correctly when the embedded JSON is HTML-decoded and parsed.

Testing

  • Added a regression test in packages/mrt-welcome-app/src/utils/welcome-routes.test.ts covering the XSS payload above.
  • Existing echo tests were updated to HTML-decode the embedded JSON before parsing (since the JSON block is now emitted as escaped HTML entities).
  • Verified the rendered template manually with ejs.renderFile against the malicious payload — the output contains &lt;h1&gt;sample and no live <h1> tags.

Dependencies

  • No net-new third-party dependencies were added
  • If net-new third-party dependencies were added, rationale/discussion is included and 3pl-approved is set by a maintainer

  • Tests pass (pnpm test)
  • Code is formatted (pnpm run format)

github-actions Bot and others added 8 commits June 23, 2026 13:40
* chore: version packages

* chore: trigger CI checks

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Remove reader-facing sausage: the internal API name (SDAPI), client_id
mechanics, 'over stdio' transport detail, and the roadmap note. Fold the
generic Configuration section into Requirements and drop the duplicate
interface list (the table already covers it).
* fix(ci): include changelog in agent-plugins release notes

The agent-plugins release step used a static --notes string, so its
GitHub release (e.g. b2c-agent-plugins@1.4.3) shipped with no real notes.
Extract the version's section from skills/CHANGELOG.md and use
--notes-file, matching the VS Code extension release step. Also make the
release create idempotent on re-runs and clobber re-uploaded artifacts.

* ci: link Agent Skills & Plugins guide in plugins release notes
…ders status gaps (#528)

* docs(skills): document headless order afterPOST, jobs.xml import, and Admin Orders status gaps

Closes documentation gaps surfaced building a headless (SCAPI) order-failure
flow, so the b2c agent skills cover the operational behavior — not just the
signatures — for afterPOST, job definitions, and the Admin Orders status filter.

- b2c-hooks: add an "Order afterPOST: Headless Order Placement" section — the
  hook runs inside a platform transaction (no nested Transaction.wrap, which
  rolls back and surfaces an opaque HTTP 400 ExtensionPoint error), owns the
  CREATED->NEW/FAILED transition (placeOrder/failOrder), and must log its own
  decline reason — plus a canonical example authorizing payment instruments via
  app.payment.processor.* Authorize hooks. Cross-linked from b2c-ordering and
  the OCAPI/SCAPI hooks reference.
- b2c-custom-job-steps: add a jobs.xml reference + SKILL section covering job/
  flow/step structure, the type (jobs.xml) vs @type-id (steptypes.json)
  distinction, step parameters, and the required <triggers> element; importable
  via `b2c job import`. Pointer added from the b2c-job CLI skill.
- b2c-scapi-admin / b2c-scapi-schemas: note the Orders API status filter only
  accepts new|completed|cancelled, status=failed returns HTTP 400, and CREATED/
  FAILED orders are not enumerable server-side; document the OrderMgr workaround
  (new b2c-ordering "Enumerate FAILED Orders" example).
- b2c-scapi-shopper: callout that POST shopper-orders leaves the order in
  CREATED and the afterPOST hook must place/fail it, with links to b2c-hooks/
  b2c-ordering (discoverability for the headless entry point).

All claims verified against the bundled jobs.xsd, the Checkout Orders v1 SCAPI
schema, and the OrderMgr/Status/Collection/Order Script API docs.

Changeset targets @salesforce/b2c-agent-plugins. Documentation-only; no code,
CLI, or SDK behavior changes. ImportCatalog/standard job steps are out of scope
(tracked in W-23128184).

W-23128917

* docs(skills): drop SCAPI Admin Orders status-filter docs (out of scope)

Revert the b2c-scapi-admin / b2c-scapi-schemas status-filter content and the
b2c-ordering "Enumerate FAILED Orders" example. Documenting that the Admin
Orders status filter rejects status=failed (potentially a platform bug) is out
of scope for this work item, which targets the order afterPOST hook behavior
and jobs.xml authoring. b2c-scapi-admin and b2c-scapi-schemas return to their
prior state. The b2c-hooks "stranded in CREATED" note keeps its reporting
motivation but no longer asserts Admin Orders API behavior.

W-23128917
…(@W-23128184) (#530)

* feat(docs): document standard (system) job steps in bundled docs + skills

@W-23128184

Add the full Business Manager standard/system job-step catalog (69 type-ids:
import, export, and processing steps such as ImportCatalog, ExportCatalog,
ExecutePreconfiguredDataReplicationProcess, SearchReindex, ExecutePipeline).
Each step's purpose, execution scope (Organization / Site / both), and
configuration parameters (required, defaults, allowed values) are bundled with
the CLI and surfaced through the EXISTING docs search/read paths — `b2c docs
search`/`b2c docs read` (CLI) and the `docs_search`/`docs_read` MCP tools —
with no new commands or tools. Read the catalog with `b2c docs read job-steps`
or a single step with `b2c docs read <TypeID>`.

Source: the public B2C Commerce Job Step API documentation (the `jobstepapi`
section of the Script API documentation archive obtained via `b2c docs
download`). No private/internal source is used.

SDK:
- New bundled corpus data/job-steps/ (job-steps.json source-of-truth + generated
  per-step markdown + index.json), merged into the docs search index.
- search.ts now loads multiple corpora and resolves each entry's file against
  its own data dir; types.ts adds JOB_STEPS_DATA_DIR.
- build-job-steps-dataset.ts parses the public jobstepapi HTML into job-steps.json
  (script: build:job-steps-dataset); generate-job-steps-docs.ts renders the
  markdown + index (script: generate:job-steps-docs).
- test/docs/job-steps.test.ts covers the combined corpus, Scope rendering, and
  the framework/replication steps.

Skills + docs:
- b2c:b2c-custom-job-steps gains a standard-step catalog (with Scope), IMPEX
  file hand-off, a custom→import→replicate chaining example, and in-flow-vs-CLI
  guidance.
- b2c-cli:b2c-job and b2c-cli:b2c-docs cross-link the catalog and clarify when to
  use an in-flow system step vs. the CLI (e.g. standard catalog import vs.
  `b2c job import`).
- Website docs/cli/jobs.md and docs/cli/docs.md updated to match.

* chore(docs): update bundled Script API docs and XSD schemas to 26.7

Refresh the bundled Script API documentation (data/script-api) and XSD
schemas (data/xsd) from the 26.7 documentation archive, and regenerate the
search indexes. Keeps the embedded docs on the same platform version as the
standard job-step catalog.

- Script API: 527 entries (8 new, incl. dw.customer.consent.* and
  dw.order.LineItemTax / dw.order.hooks.TaxHooks; 30 updated)
- XSD schemas: 57 entries (1 new: eventrouting.xsd; 5 updated)

@W-23128184
…en (#524)

Admin API tokens (system and custom) require both SALESFORCE_COMMERCE_API:<tenant_id>
and API-specific scopes. Document this consistently across the custom-api-development,
scapi-admin, scapi-custom, and config skills; note that b2c auth token accepts multiple
--auth-scope values and does not auto-inject the tenant scope (unlike SCAPI subcommands).
Fix a broken admin token curl example and an invalid --scope flag reference.
…W-23137861)

The welcome-app's diagnostic echo handler renders request data through
an EJS template using the unescaped output tag (<%- %>). Because
JSON.stringify only escapes '"' and '\\', characters such as '<', '>',
and '&' pass through and the browser renders any HTML supplied via the
URL (query string, path, headers) as live markup. Switch the template
tag to the escaping form (<%= %>) and add a regression test that
asserts reflected HTML is escaped.
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.

2 participants