You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The other ~40 [Security] Access denied sentences are still English-only developer copy shown verbatim to end users (same class as #7414, one template fixed) #7451
Found while implementing #7414 (PR #7449). That card measured and fixed one template — the object-CRUD grant denial in packages/plugins/plugin-security/src/security-plugin.ts — because that is the one the reporter hit. The same defect is present across the rest of the family, and a 40-template sweep is a different review from a one-template copy fix, so it is filed rather than smuggled into that PR.
The defect class
Every one of these composes an English developer sentence into Error.message, and Error.message is the body's human-readable string on both transports (mapDataError's body.error, the dispatcher's error.message), which Console renders verbatim in a toast. So each is English-only in an otherwise localized UI, and most name API object names, position names, permission-set names or ADR clause numbers.
Census at 2ef18070e (grep -rc "\[Security\] Access denied" --include=*.ts packages/), producers only — the counts below exclude the matcher/doc occurrences in plugin-security/src/errors.ts (1), rest/src/rest-server.ts (3) and runtime/src/security/resolve-execution-context.ts (1), which are startsWith fallbacks and comments rather than thrown copy:
File
Producer templates
plugins/plugin-security/src/security-plugin.ts
19 total, 18 remaining (1 converted by PR #7449; that file's 20th hit is the new developerMessage, which is correctly developer-facing)
The delegated-admin and package-managed-metadata sentences are admin-surface copy; those are arguably correctly developer-facing, which is part of what makes this a triage question rather than a mechanical sweep.
Why it is not just "apply the same patch 40 times"
PR #7449 established the shape: render the user's half through the shared operation-message catalog (packages/spec/src/system/operation-message.ts, addressed under the errors. key prefix), keep the previous sentence byte for byte as developerMessage, and log it rather than ship it (the 403 body carries no structured API names, so shipping it would add a disclosure — see #7450). Applying that needs three decisions per site that a sweep cannot make mechanically:
Audience. A delegated-admin refusal is read by an administrator using an admin console; an RLS row denial is read by a salesperson. Only the second needs the catalog treatment. Localizing admin-surface copy that cites ADR clauses would lose the citation without helping anyone.
How many sentences. The catalog's rule is one wire code, several message keys. Does the whole family share errors.permission_denied, or do the row-level and capability denials get their own keys? A user blocked by row-level security is in a genuinely different situation from one whose profile lacks the object bit.
Triage into a small number of grouped cards along the audience split (end-user gates vs. admin-surface gates), rather than one card per template. The mechanism, the tests to copy and the disclosure reasoning are all in PR #7449.
Found while implementing #7414 (PR #7449). That card measured and fixed one template — the object-CRUD grant denial in
packages/plugins/plugin-security/src/security-plugin.ts— because that is the one the reporter hit. The same defect is present across the rest of the family, and a 40-template sweep is a different review from a one-template copy fix, so it is filed rather than smuggled into that PR.The defect class
Every one of these composes an English developer sentence into
Error.message, andError.messageis the body's human-readable string on both transports (mapDataError'sbody.error, the dispatcher'serror.message), which Console renders verbatim in a toast. So each is English-only in an otherwise localized UI, and most name API object names, position names, permission-set names or ADR clause numbers.Census at
2ef18070e(grep -rc "\[Security\] Access denied" --include=*.ts packages/), producers only — the counts below exclude the matcher/doc occurrences inplugin-security/src/errors.ts(1),rest/src/rest-server.ts(3) andruntime/src/security/resolve-execution-context.ts(1), which arestartsWithfallbacks and comments rather than thrown copy:plugins/plugin-security/src/security-plugin.tsdeveloperMessage, which is correctly developer-facing)plugins/plugin-security/src/delegated-admin-gate.tsplugins/plugin-security/src/suggested-audience-bindings.tsplugins/plugin-auth/src/invitation-role-cap.tsplugins/plugin-security/src/permission-set-projection.tsplugins/plugin-security/src/system-write-guard.tsplugins/plugin-security/src/predicate-guard.tsThe ones an ordinary business user is most likely to meet are the row-level write denial
and the capability AND-gate
The delegated-admin and package-managed-metadata sentences are admin-surface copy; those are arguably correctly developer-facing, which is part of what makes this a triage question rather than a mechanical sweep.
Why it is not just "apply the same patch 40 times"
PR #7449 established the shape: render the user's half through the shared operation-message catalog (
packages/spec/src/system/operation-message.ts, addressed under theerrors.key prefix), keep the previous sentence byte for byte asdeveloperMessage, and log it rather than ship it (the 403 body carries no structured API names, so shipping it would add a disclosure — see #7450). Applying that needs three decisions per site that a sweep cannot make mechanically:errors.permission_denied, or do the row-level and capability denials get their own keys? A user blocked by row-level security is in a genuinely different situation from one whose profile lacks the object bit.Suggested route
Triage into a small number of grouped cards along the audience split (end-user gates vs. admin-surface gates), rather than one card per template. The mechanism, the tests to copy and the disclosure reasoning are all in PR #7449.
Blocked-by: #7449
Generated by Claude Code