Skip to content

Commit ff6720f

Browse files
committed
fix(metadata-protocol): hand the credentialsRef judge the write-address read only
The save door binds its repository under the one spelling the stored-row write census reads, and the judge receives only `headAt`, so it can read the save's own row and write nothing. Claude-Session: https://claude.ai/code/session_016WV4cx2wNeyC4kD8R4P7Ns Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0195302 commit ff6720f

2 files changed

Lines changed: 6 additions & 4 deletions

File tree

‎packages/metadata-protocol/src/protocol-datasource-credentials-ref.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ function namedCredentialsRef(body: unknown): unknown {
5959
* See the module header for the rule, the row it reads and who passes.
6060
*/
6161
export async function datasourceCredentialsRefSaveRefusal(args: {
62-
repo: SysMetadataRepository;
62+
/** The repository's write-address read — the one store access this judge makes; it writes nothing. */
63+
headAt: SysMetadataRepository['headAt'];
6364
/** Singular type name. */
6465
type: string;
6566
name: string;
@@ -75,10 +76,10 @@ export async function datasourceCredentialsRefSaveRefusal(args: {
7576
const ref = { type: args.type, name: args.name, org: 'env' } as Parameters<SysMetadataRepository['headAt']>[0];
7677
const state: 'active' | 'draft' = args.mode === 'draft' ? 'draft' : 'active';
7778
const member = itemDiscriminator(args.type, args.item);
78-
const target = await args.repo.headAt(ref, { state, packageId: args.packageId ?? null, member });
79+
const target = await args.headAt(ref, { state, packageId: args.packageId ?? null, member });
7980
let head = target.head;
8081
if (!head && state === 'draft') {
81-
head = (await args.repo.headAt(ref, { state: 'active', packageId: target.packageId, member })).head;
82+
head = (await args.headAt(ref, { state: 'active', packageId: target.packageId, member })).head;
8283
}
8384
const own = namedCredentialsRef(head?.body);
8485
if (typeof own === 'string' && own === named) return undefined;

‎packages/metadata-protocol/src/protocol.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21287,8 +21287,9 @@ export class ObjectStackProtocolImplementation implements
2128721287
// [#27762] A `datasource` body may name, in `external.credentialsRef`, only the reference its own stored row
2128821288
// holds: refused 400 before the authoring gate and every write. See {@link datasourceCredentialsRefSaveRefusal}.
2128921289
{
21290+
const repo = this.getOverlayRepo();
2129021291
const credentialsRefRefusal = await datasourceCredentialsRefSaveRefusal({
21291-
repo: this.getOverlayRepo(), type: singularType, name: request.name, item: request.item,
21292+
headAt: (...a) => repo.headAt(...a), type: singularType, name: request.name, item: request.item,
2129221293
mode, packageId: request.packageId, source: request.source,
2129321294
});
2129421295
if (credentialsRefRefusal) throw credentialsRefRefusal;

0 commit comments

Comments
 (0)