Skip to content

fix: mask market and catalog errors to a closed set, and keep originals out of the gateway's - #28

Merged
Hebilicious merged 1 commit into
audit/repo-hygienefrom
audit/error-masking
Oct 14, 2026
Merged

Hebilicious merged 1 commit into
audit/repo-hygienefrom
audit/error-masking

Conversation

@Hebilicious

@Hebilicious Hebilicious commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Closes #12

What changed

  • Market subgraph (packages/market-subgraph/src/index.ts): maskedErrors now uses a mask that lets out only errors made by marketError:

    • BAD_USER_INPUT;
    • QUERY_BUDGET_EXCEEDED;
    • UPSTREAM_UNAVAILABLE, with the fixed message "The Central Market could not be read.". Every failed Pearl Abyss read gets this code in this layer.

    Item ID and enhancement-level argument errors keep their messages as BAD_USER_INPUT. Anything else becomes INTERNAL_SERVER_ERROR with one fixed message.

  • Catalog subgraph: the same kind of closed set. An item ID that is not a positive whole number in item(id:), items(ids:) or an entity reference is BAD_USER_INPUT; anything else is INTERNAL_SERVER_ERROR. createCatalogSubgraph takes an optional itemNameSearch, which a test uses to make a search fail.

  • Gateway: maskedErrors: { isDev: false }, so the original error and its stack stay out even when NODE_ENV=development.

  • Tests: new market-error-semantics feature, with the scenarios this layer needs, and new catalog-error-semantics feature. Both are added to the test tsconfigs.

  • docs/api.md lists the codes.

  • The catalog identity is regenerated.

In this layer, a throttled Pearl Abyss read is masked to UPSTREAM_UNAVAILABLE. audit/throttle-and-budget-errors gives it its own RATE_LIMITED code.

How it was reproduced

I ran this layer's two features against the parent layer (audit/repo-hygiene):

Scenario On the parent
market-client-input-errors fails: the error is {"message":"Item ID must be a positive safe integer", …, "code":"DOWNSTREAM_SERVICE_ERROR"}, not BAD_USER_INPUT
market-internal-errors-masked fails: the NA error is ["DOWNSTREAM_SERVICE_ERROR", …, "Pearl Abyss response is not ASCII text"]. That is the leak: internal transport text reaches the client
gateway-errors-masked-in-development fails: The input was expected to not match … /internal plugin detail 7f3a/u. The plugin's internal message reaches the client
catalog-client-input-errors fails: {"message":"Item ID must be a positive safe integer", …} has no BAD_USER_INPUT code
catalog-internal-errors-masked fails: there are no errors at all (actual: undefined)

Not reproduced

  • catalog-internal-errors-masked fails on the parent only because the parent has no itemNameSearch seam, so the failing search cannot be injected. That the catalog's internal text leaked was confirmed by reading the code (maskedErrors: false), not by a failing assertion on its text.
  • The audit's live demo request (catalog(category: {main: 0}) returning "Unsupported Pearl Abyss Central Market category") was not repeated, because there is no live traffic. audit/summary-and-catalog-limits covers that message locally.

Refs #21 (security audit tracking issue). Part of stack #42; review bottom-up, each layer shows only its own diff.

@changeset-bot

changeset-bot Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: be0f9fa

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@Hebilicious
Hebilicious added this pull request to stack #37 October 13, 2026 20:10
@Hebilicious
Hebilicious removed this pull request from stack #37 October 14, 2026 06:05
@Hebilicious
Hebilicious added this pull request to stack #42 October 14, 2026 06:05
@Hebilicious Hebilicious changed the title Mask market and catalog errors to a closed set, and keep the gateway's masked errors free of the original fix: mask market and catalog errors to a closed set, and keep originals out of the gateway's Oct 14, 2026
…ls out of the gateway's

The market and catalog Yogas ran with maskedErrors: false, and the gateway
passes a subgraph's errors through as the subgraph wrote them, so internal
text such as "Huffman file header is invalid" or "Pearl Abyss positional row
has an unknown shape" reached callers.

- The market subgraph lets out only its own closed set: BAD_USER_INPUT,
  QUERY_BUDGET_EXCEEDED and UPSTREAM_UNAVAILABLE, each with a fixed message.
  A failed Pearl Abyss read is UPSTREAM_UNAVAILABLE without its reason.
  Client argument errors (item IDs, enhancement levels) keep their message
  as BAD_USER_INPUT. Anything else is INTERNAL_SERVER_ERROR with one fixed
  message.
- The catalog subgraph does the same: an item ID that is not a positive
  whole number in item(id:), items(ids:) and entity references is
  BAD_USER_INPUT, and anything else is INTERNAL_SERVER_ERROR.
  createCatalogSubgraph takes an optional itemNameSearch, which the
  scenario uses to make a search fail.
- The gateway masks its own errors with isDev: false, so the original error
  and its stack stay out even where NODE_ENV says development.

market-error-semantics (market-client-input-errors,
market-internal-errors-masked, gateway-errors-masked-in-development) and
catalog-error-semantics prove it end to end. docs/api.md lists the codes.
The catalog identity is regenerated, since the subgraph's code is part of it.

Refs #12
@Hebilicious
Hebilicious merged commit 3dd2aee into main Oct 14, 2026
5 of 6 checks passed
@Hebilicious
Hebilicious deleted the audit/error-masking branch October 14, 2026 08:56
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.

Internal Pearl Abyss error text reaches API clients

1 participant