fix(mosaic): quarantine unscanned brain state
This commit is contained in:
@@ -196,6 +196,43 @@ describe('provider-backed durable owner resolver', (): void => {
|
||||
expect(requests[0]?.signal).toBeInstanceOf(AbortSignal);
|
||||
});
|
||||
|
||||
it('cancels a chunked provider body as soon as it exceeds the byte ceiling', async (): Promise<void> => {
|
||||
const resolver = await loadResolver('MB-REQ-09 bounded owner response stream');
|
||||
let cancelled = false;
|
||||
const oversized = new ReadableStream<Uint8Array>({
|
||||
start(controller): void {
|
||||
controller.enqueue(new Uint8Array(200_000));
|
||||
controller.enqueue(new Uint8Array(100_000));
|
||||
},
|
||||
cancel(): void {
|
||||
cancelled = true;
|
||||
},
|
||||
});
|
||||
|
||||
const result = await resolver.resolveProviderDurableOwner(
|
||||
{
|
||||
estateRegistrySource: estateRegistry(),
|
||||
ownerPolicySource: ownerPolicy(),
|
||||
host: 'git.example.invalid',
|
||||
requestedOwner: 'user:durable-owner',
|
||||
},
|
||||
{
|
||||
fetch: async (): Promise<Response> =>
|
||||
new Response(oversized, {
|
||||
status: 200,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
}),
|
||||
absentControlName: (): string => 'generated-absent-control',
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
verdict: 'not-measured',
|
||||
reasonCode: 'owner-unexpected-provider-shape',
|
||||
});
|
||||
expect(cancelled).toBe(true);
|
||||
});
|
||||
|
||||
it('requires the GLPI standing remediation queue in the local estate policy', async (): Promise<void> => {
|
||||
const resolver = await loadResolver('MB-REQ-09 standing process policy');
|
||||
const raw = JSON.parse(ownerPolicy()) as { estates: Array<Record<string, unknown>> };
|
||||
|
||||
@@ -81,14 +81,37 @@ async function boundedJson(response: Response): Promise<unknown> {
|
||||
throw new Error('owner-unexpected-content-type');
|
||||
}
|
||||
const declared = response.headers.get('content-length');
|
||||
let declaredSize: number | null = null;
|
||||
if (declared !== null) {
|
||||
const size = Number.parseInt(declared, 10);
|
||||
if (Number.isFinite(size) && size > MAX_BODY_BYTES) {
|
||||
if (!/^\d+$/.test(declared)) throw new Error('owner-unexpected-provider-shape');
|
||||
declaredSize = Number.parseInt(declared, 10);
|
||||
if (!Number.isSafeInteger(declaredSize) || declaredSize > MAX_BODY_BYTES) {
|
||||
throw new Error('owner-unexpected-provider-shape');
|
||||
}
|
||||
}
|
||||
const body = new Uint8Array(await response.arrayBuffer());
|
||||
if (body.byteLength > MAX_BODY_BYTES) throw new Error('owner-unexpected-provider-shape');
|
||||
if (response.body === null) throw new Error('owner-unexpected-provider-shape');
|
||||
const reader = response.body.getReader();
|
||||
const chunks: Uint8Array[] = [];
|
||||
let total = 0;
|
||||
while (true) {
|
||||
const next = await reader.read();
|
||||
if (next.done) break;
|
||||
total += next.value.byteLength;
|
||||
if (total > MAX_BODY_BYTES) {
|
||||
await reader.cancel('owner response exceeds byte ceiling');
|
||||
throw new Error('owner-unexpected-provider-shape');
|
||||
}
|
||||
chunks.push(next.value);
|
||||
}
|
||||
if (declaredSize !== null && declaredSize !== total) {
|
||||
throw new Error('owner-unexpected-provider-shape');
|
||||
}
|
||||
const body = new Uint8Array(total);
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
body.set(chunk, offset);
|
||||
offset += chunk.byteLength;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(body));
|
||||
} catch {
|
||||
|
||||
@@ -51,6 +51,7 @@ interface ProvisionModule {
|
||||
readonly run: CommandRunner;
|
||||
readonly fetch: FetchLike;
|
||||
readonly absentControlName: () => string;
|
||||
readonly approveMigrationContent?: (path: string, content: Uint8Array) => boolean;
|
||||
},
|
||||
): Promise<ProvisionResult>;
|
||||
}
|
||||
@@ -359,6 +360,7 @@ describe('P7 brain provisioning orchestration', (): void => {
|
||||
run: runner,
|
||||
fetch: ownerFetch(),
|
||||
absentControlName: (): string => 'generated-absent-control',
|
||||
approveMigrationContent: (): boolean => true,
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
|
||||
@@ -88,6 +88,7 @@ export async function provisionBrain(
|
||||
readonly run: CommandRunner;
|
||||
readonly fetch: OwnerFetch;
|
||||
readonly absentControlName: () => string;
|
||||
readonly approveMigrationContent?: (path: string, content: Uint8Array) => boolean;
|
||||
},
|
||||
): Promise<ProvisionResult> {
|
||||
const brainNamespace = parseRequestedOwner(input.requestedOwner);
|
||||
@@ -213,6 +214,7 @@ export async function provisionBrain(
|
||||
laneActive: input.laneActive,
|
||||
},
|
||||
(): MigrationOwnerResolution => owner,
|
||||
dependencies.approveMigrationContent,
|
||||
);
|
||||
const migration = migrateBrainState(
|
||||
plan,
|
||||
|
||||
@@ -133,6 +133,7 @@ interface BrainStoreModule {
|
||||
readonly laneActive: boolean;
|
||||
},
|
||||
resolveOwner?: (lane: string) => MigrationOwnerResolution,
|
||||
approveContent?: (path: string, content: Uint8Array) => boolean,
|
||||
): MigrationPlan;
|
||||
migrateBrainState(
|
||||
plan: MigrationPlan,
|
||||
@@ -557,6 +558,7 @@ describe('R7 — migration is non-destructive, append-only, and explicit', (): v
|
||||
const plan = sut.discoverBrainMigration(
|
||||
{ sourceRoot, brainRoot, seat: 'seat-a', lane: 'lane-a', laneActive: true },
|
||||
activeLaneOwner,
|
||||
(): boolean => true,
|
||||
);
|
||||
|
||||
expect(plan.status).toBe('ready');
|
||||
@@ -569,6 +571,36 @@ describe('R7 — migration is non-destructive, append-only, and explicit', (): v
|
||||
);
|
||||
});
|
||||
|
||||
it('retains and reports otherwise-benign legacy content when no approved scanner is available', async (): Promise<void> => {
|
||||
const sut = await loadSut('MB-REQ-06 approved scanner required');
|
||||
const root = tempRoot();
|
||||
const sourceRoot = join(root, 'local-memory');
|
||||
mkdirSync(join(sourceRoot, 'lanes', 'lane-a'), { recursive: true });
|
||||
const source = join(sourceRoot, 'lanes', 'lane-a', 'finding.md');
|
||||
writeFileSync(source, 'ordinary finding\n');
|
||||
|
||||
const plan = sut.discoverBrainMigration(
|
||||
{
|
||||
sourceRoot,
|
||||
brainRoot: join(root, 'brain'),
|
||||
seat: 'seat-a',
|
||||
lane: 'lane-a',
|
||||
laneActive: true,
|
||||
},
|
||||
activeLaneOwner,
|
||||
);
|
||||
|
||||
expect(plan.candidates).toHaveLength(0);
|
||||
expect(plan.reported).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
path: source,
|
||||
reason: expect.stringMatching(/scanner|approval/i),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('ignores a caller-asserted owner string/validated flag and leaves the gate blocking', async (): Promise<void> => {
|
||||
const sut = await loadSut('MB-REQ-09 caller owner assertion cannot satisfy gate');
|
||||
const root = tempRoot();
|
||||
@@ -672,20 +704,26 @@ describe('R7 — migration is non-destructive, append-only, and explicit', (): v
|
||||
mkdirSync(join(laneRoot, path, '..'), { recursive: true });
|
||||
writeFileSync(join(laneRoot, path), 'NESTED-SECRET-MARKER\n');
|
||||
}
|
||||
const embeddedSecret = join(laneRoot, 'benign-looking.md');
|
||||
writeFileSync(
|
||||
embeddedSecret,
|
||||
'Authorization: Bearer NESTED-CONTENT-SECRET-MARKER-1234567890\n',
|
||||
);
|
||||
const embeddedSecrets = new Map([
|
||||
['benign-auth.md', 'Authorization: Bearer NESTED-CONTENT-SECRET-MARKER-1234567890\n'],
|
||||
['benign-token.md', 'token=fixture-value\n'],
|
||||
['benign-aws.md', 'AWS_SECRET_ACCESS_KEY=fixture-value\n'],
|
||||
['benign-header.md', 'X-Api-Key: fixture-value\n'],
|
||||
['benign-jwt.md', 'session=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmaXh0dXJlIn0.signature\n'],
|
||||
]);
|
||||
for (const [name, content] of embeddedSecrets) writeFileSync(join(laneRoot, name), content);
|
||||
|
||||
const plan = sut.discoverBrainMigration(
|
||||
{ sourceRoot, brainRoot, seat: 'seat-a', lane: 'lane-a', laneActive: true },
|
||||
activeLaneOwner,
|
||||
(): boolean => true,
|
||||
);
|
||||
|
||||
expect(plan.status).toBe('ready');
|
||||
expect(plan.candidates).toHaveLength(0);
|
||||
expect(plan.reported).toHaveLength(secretNames.length + nestedSecretPaths.length + 1);
|
||||
expect(plan.reported).toHaveLength(
|
||||
secretNames.length + nestedSecretPaths.length + embeddedSecrets.size,
|
||||
);
|
||||
expect(plan.reported.every((entry) => /secret/i.test(entry.reason))).toBe(true);
|
||||
expect(JSON.stringify(plan)).not.toMatch(/NESTED-(?:CONTENT-)?SECRET-MARKER/);
|
||||
expect(existsSync(brainRoot)).toBe(false);
|
||||
@@ -701,6 +739,7 @@ describe('R7 — migration is non-destructive, append-only, and explicit', (): v
|
||||
const plan = sut.discoverBrainMigration(
|
||||
{ sourceRoot, brainRoot, seat: 'seat-a', lane: 'lane-a', laneActive: true },
|
||||
activeLaneOwner,
|
||||
(): boolean => true,
|
||||
);
|
||||
const candidate = plan.candidates[0];
|
||||
expect(candidate).toBeDefined();
|
||||
@@ -756,6 +795,7 @@ describe('R7 — migration is non-destructive, append-only, and explicit', (): v
|
||||
const plan = sut.discoverBrainMigration(
|
||||
{ sourceRoot, brainRoot, seat: 'seat-a', lane: 'lane-a', laneActive: true },
|
||||
activeLaneOwner,
|
||||
(): boolean => true,
|
||||
);
|
||||
|
||||
const result = sut.migrateBrainState(
|
||||
@@ -796,6 +836,7 @@ describe('R7 — migration is non-destructive, append-only, and explicit', (): v
|
||||
const plan = sut.discoverBrainMigration(
|
||||
{ sourceRoot, brainRoot, seat: 'seat-a', lane: 'lane-a', laneActive: true },
|
||||
activeLaneOwner,
|
||||
(): boolean => true,
|
||||
);
|
||||
let swapped = false;
|
||||
let publishCalls = 0;
|
||||
@@ -838,6 +879,7 @@ describe('R7 — migration is non-destructive, append-only, and explicit', (): v
|
||||
const plan = sut.discoverBrainMigration(
|
||||
{ sourceRoot, brainRoot, seat: 'seat-a', lane: 'lane-a', laneActive: true },
|
||||
activeLaneOwner,
|
||||
(): boolean => true,
|
||||
);
|
||||
let publishCalls = 0;
|
||||
|
||||
@@ -867,6 +909,7 @@ describe('R7 — migration is non-destructive, append-only, and explicit', (): v
|
||||
const plan = sut.discoverBrainMigration(
|
||||
{ sourceRoot, brainRoot, seat: 'seat-a', lane: 'lane-a', laneActive: true },
|
||||
activeLaneOwner,
|
||||
(): boolean => true,
|
||||
);
|
||||
let cleanupHookCalled = false;
|
||||
|
||||
@@ -908,6 +951,7 @@ describe('R7 — migration is non-destructive, append-only, and explicit', (): v
|
||||
const plan = sut.discoverBrainMigration(
|
||||
{ sourceRoot, brainRoot, seat: 'seat-a', lane: 'lane-a', laneActive: true },
|
||||
activeLaneOwner,
|
||||
(): boolean => true,
|
||||
);
|
||||
|
||||
const result = sut.migrateBrainState(
|
||||
|
||||
@@ -614,9 +614,8 @@ function migrationCandidate(
|
||||
kind: 'lane' | 'seat',
|
||||
seat: string,
|
||||
lane: string,
|
||||
source: StableSourceSnapshot,
|
||||
): MigrationCandidate {
|
||||
const source = stableSourceSnapshot(path);
|
||||
if (sourceContainsSecretMaterial(source.content)) throw new Error('migration-secret-content');
|
||||
const key = relative(sourceRoot, path).split(sep).join('/');
|
||||
const digest = createHash('sha256')
|
||||
.update(key)
|
||||
@@ -672,7 +671,8 @@ function sourceContainsSecretMaterial(content: Buffer): boolean {
|
||||
return [
|
||||
/-----BEGIN [^-\r\n]*PRIVATE KEY-----/i,
|
||||
/\bauthorization\s*:\s*(?:bearer|basic)\s+\S+/i,
|
||||
/\b(?:password|passwd|api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret)\s*[:=]\s*\S+/i,
|
||||
/\b(?:[a-z0-9_-]*(?:token|secret|password|passwd)|(?:x-)?api[_-]?key|aws_secret_access_key)\s*[:=]\s*\S+/i,
|
||||
/\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}\b/,
|
||||
/https?:\/\/[^/\s:@]+:[^/\s@]+@/i,
|
||||
/\b[A-Za-z0-9+/_=-]{40,}\b/,
|
||||
].some((pattern): boolean => pattern.test(source));
|
||||
@@ -691,6 +691,7 @@ export function discoverBrainMigration(
|
||||
readonly laneActive: boolean;
|
||||
},
|
||||
resolveOwner?: (lane: string) => MigrationOwnerResolution,
|
||||
approveContent?: (path: string, content: Uint8Array) => boolean,
|
||||
): MigrationPlan {
|
||||
const seat = safeName(input.seat, 'seat');
|
||||
const lane = safeName(input.lane, 'lane');
|
||||
@@ -746,16 +747,24 @@ export function discoverBrainMigration(
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const source = stableSourceSnapshot(path);
|
||||
if (sourceContainsSecretMaterial(source.content)) throw new Error('migration-secret-content');
|
||||
if (approveContent === undefined || !approveContent(path, Buffer.from(source.content))) {
|
||||
throw new Error('migration-content-unapproved');
|
||||
}
|
||||
candidates.push(
|
||||
migrationCandidate(input.sourceRoot, input.brainRoot, path, 'lane', seat, lane),
|
||||
migrationCandidate(input.sourceRoot, input.brainRoot, path, 'lane', seat, lane, source),
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
const code = error instanceof Error ? error.message : '';
|
||||
reported.push({
|
||||
path,
|
||||
reason:
|
||||
error instanceof Error && error.message === 'migration-secret-content'
|
||||
code === 'migration-secret-content'
|
||||
? 'Content may contain secret material; retained and reported.'
|
||||
: 'Lane state was not a regular file; retained and reported.',
|
||||
: code === 'migration-content-unapproved'
|
||||
? 'Approved secret scanner or content approval is unavailable; retained and reported.'
|
||||
: 'Lane state was not a regular file; retained and reported.',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -768,16 +777,24 @@ export function discoverBrainMigration(
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const source = stableSourceSnapshot(path);
|
||||
if (sourceContainsSecretMaterial(source.content)) throw new Error('migration-secret-content');
|
||||
if (approveContent === undefined || !approveContent(path, Buffer.from(source.content))) {
|
||||
throw new Error('migration-content-unapproved');
|
||||
}
|
||||
candidates.push(
|
||||
migrationCandidate(input.sourceRoot, input.brainRoot, path, 'seat', seat, lane),
|
||||
migrationCandidate(input.sourceRoot, input.brainRoot, path, 'seat', seat, lane, source),
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
const code = error instanceof Error ? error.message : '';
|
||||
reported.push({
|
||||
path,
|
||||
reason:
|
||||
error instanceof Error && error.message === 'migration-secret-content'
|
||||
code === 'migration-secret-content'
|
||||
? 'Content may contain secret material; retained and reported.'
|
||||
: 'Seat state was not a regular file; retained and reported.',
|
||||
: code === 'migration-content-unapproved'
|
||||
? 'Approved secret scanner or content approval is unavailable; retained and reported.'
|
||||
: 'Seat state was not a regular file; retained and reported.',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user