Skip to content

Commit 97d8831

Browse files
committed
test(runtime): re-spell the share-links 403 pin for the localized message (#7414)
`share-links-enforcement-context.test.ts` drives the REAL security gate and asserted `toContain('Access denied')` on the message. That string is now the developer half (logged, not shipped); the message is the catalog's user-facing sentence. Re-spelled against `BUILTIN_OPERATION_MESSAGES.en.permission_denied` so the case keeps its stated purpose — proving an authorization answer reached the client rather than being swallowed into the generic internal-error string — without needing another re-spell on the next copy edit. Found by CI, not by the local sweep: the rule's consumption radius is wider than the edited package. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BM1tNf5U3nEbHKR4fo5qVQ
1 parent d10b998 commit 97d8831

1 file changed

Lines changed: 12 additions & 1 deletion

File tree

packages/runtime/src/domains/share-links-enforcement-context.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import { SHARE_LINK_SERVICE } from '@objectstack/spec/contracts';
5555
import { PermissionDeniedError, SecurityPlugin } from '@objectstack/plugin-security';
5656
import { ShareLinkService } from '@objectstack/plugin-sharing';
5757
import { ApiErrorSchema, BaseResponseSchema, envelopeViolations } from '@objectstack/spec/api';
58+
import { BUILTIN_OPERATION_MESSAGES } from '@objectstack/spec/system';
5859
import { apiErrorResponse } from '../error-envelope.js';
5960
import { handleShareLinksRequest } from './share-links.js';
6061
import { HttpDispatcher } from '../http-dispatcher.js';
@@ -605,7 +606,17 @@ describe('[#6649] a security-middleware refusal keeps its own status through the
605606
// message trips no clause of `looksLikeInternalErrorLeak` anyway). It
606607
// pins the refusal's own reason against a FUTURE widening of that
607608
// heuristic swallowing an authorization answer.
608-
expect(res.body.error.message).toContain('Access denied');
609+
//
610+
// [#7414] Re-spelled, not weakened. This used to read
611+
// `toContain('Access denied')`, which was the CRUD gate's developer
612+
// sentence; that sentence is now `developerMessage` (logged, not
613+
// shipped) and `message` is the user-facing catalog entry rendered in
614+
// `ExecutionContext.locale` — `en` here, since this caller declares no
615+
// locale. Asserted against the catalog constant rather than a literal so
616+
// a future copy edit does not need to re-spell this file, and still
617+
// proves the same thing: an authorization answer reached the client
618+
// instead of being swallowed into the generic internal-error string.
619+
expect(res.body.error.message).toBe(BUILTIN_OPERATION_MESSAGES.en.permission_denied);
609620
}, 30_000);
610621

611622
it('group posture: the same denial, the same envelope — the defect was never posture-specific', async () => {

0 commit comments

Comments
 (0)