test(mosaic): control brain config owner binding

This commit is contained in:
2026-08-05 14:24:27 -05:00
parent d03ca8083e
commit b038625db5
2 changed files with 17 additions and 2 deletions
@@ -83,11 +83,12 @@ No explicit token ceiling was supplied. Working cap: 55K tokens for implementati
- [x] C1 P5→P7 seam receipt read; no brain implementation is in C1. - [x] C1 P5→P7 seam receipt read; no brain implementation is in C1.
- [x] RED acceptance set committed at `cf11c6c86abae073d8b02b4014cd5447ba67f12a`; author and committer read back as `be-coder-07` and branch reachability was independently verified by `tl-mosaic`. - [x] RED acceptance set committed at `cf11c6c86abae073d8b02b4014cd5447ba67f12a`; author and committer read back as `be-coder-07` and branch reachability was independently verified by `tl-mosaic`.
- [x] Moving-contract REDs observed independently for v1.4 mismatch, R8 prerequisite ordering, owner resolver seam/allowlist, tracked skeleton/no-follow behavior, runtime observation/publication, and provider owner resolution. - [x] Moving-contract REDs observed independently for v1.4 mismatch, R8 prerequisite ordering, owner resolver seam/allowlist, tracked skeleton/no-follow behavior, runtime observation/publication, and provider owner resolution.
- [x] Focused implementation includes secure migration, v1.5 write-differential/subject binding, production Git+API refusal parity, provider-backed durable owner resolution that ignores non-admin `active`, required GLPI standing-process policy, P7 provision orchestration, an internal installer command, and installed `mosaic doctor` wiring. Latest focused result: 87/87 (secure config 3, store 43, runtime 13, owner resolver 16, provision 4, provision command 3, installed doctor 5). - [x] Focused implementation includes secure migration, v1.5 write-differential/subject binding, production Git+API refusal parity, provider-backed durable owner resolution that ignores non-admin `active`, required GLPI standing-process policy, P7 provision orchestration, an internal installer command, and installed `mosaic doctor` wiring. Latest focused result: 88/88 (secure config 4, store 43, runtime 13, owner resolver 16, provision 4, provision command 3, installed doctor 5).
- [x] MC-CRED added the required canonical reverse registry seam `ParsedCredentialEstateRegistry.resolveByHost()` at dependency head `6ca8758f`; current local copies are temporary until dependency integration and the 32-line permissive shim has been removed. - [x] MC-CRED added the required canonical reverse registry seam `ParsedCredentialEstateRegistry.resolveByHost()` at dependency head `6ca8758f`; current local copies are temporary until dependency integration and the 32-line permissive shim has been removed.
- [x] Identity gotcha measured: inline `MOSAIC_GIT_IDENTITY=be-coder-07` controls credential resolution but does not override `user.name`/`user.email` inherited from the linked worktree common-dir config (`coder-mos1`). The first local P7 RED commit was immediately amended before push with command-scoped `GIT_AUTHOR_*` + `GIT_COMMITTER_*`; resulting author and committer both read back as `be-coder-07`. Every subsequent authoring command must carry both identity sets and be verified. - [x] Identity gotcha measured: inline `MOSAIC_GIT_IDENTITY=be-coder-07` controls credential resolution but does not override `user.name`/`user.email` inherited from the linked worktree common-dir config (`coder-mos1`). The first local P7 RED commit was immediately amended before push with command-scoped `GIT_AUTHOR_*` + `GIT_COMMITTER_*`; resulting author and committer both read back as `be-coder-07`. Every subsequent authoring command must carry both identity sets and be verified.
- [x] R6 migration reports filename- or content-secret-shaped files without copying them; arbitrary legacy content requires an approved scanner bound to the exact source snapshot, and production currently retains/reports when no approved scanner is configured. Symlinked `.gitignore`, layout directories, and nested migration destinations fail closed. - [x] R6 migration reports filename- or content-secret-shaped files without copying them; arbitrary legacy content requires an approved scanner bound to the exact source snapshot, and production currently retains/reports when no approved scanner is configured. Symlinked `.gitignore`, layout directories, and nested migration destinations fail closed.
- [x] Provider owner lookup uses manual redirect handling, a five-second abort signal, strict JSON content type/shape, and an incrementally enforced 256 KiB response ceiling. - [x] Provider owner lookup uses manual redirect handling, a five-second abort signal, strict JSON content type/shape, and an incrementally enforced 256 KiB response ceiling.
- [x] Security-critical owner policy/registry reads have direct controls for principal UID ownership, file/ancestor permissions, and descriptor-safe regular-file reads.
- [x] Automatic source deletion is parked per the shared-Git-identity governance ruling; remotely reachable snapshots still leave and report every source. - [x] Automatic source deletion is parked per the shared-Git-identity governance ruling; remotely reachable snapshots still leave and report every source.
- [x] Multi-host push-on-write uses an isolated temporary Git index populated from approved in-memory blobs rather than pathname re-reads, verifies each committed blob ID, the exact changed-path allowlist, and both author/committer trailers before push, retries non-fast-forward races via identity-scoped fetch/rebase rather than LWW, and retains both sources and local copies when publication reachability is unknown. A real-repository abuse test proves destination-path substitution cannot change committed bytes and unrelated pre-staged secret-shaped content remains staged but never enters the published commit. - [x] Multi-host push-on-write uses an isolated temporary Git index populated from approved in-memory blobs rather than pathname re-reads, verifies each committed blob ID, the exact changed-path allowlist, and both author/committer trailers before push, retries non-fast-forward races via identity-scoped fetch/rebase rather than LWW, and retains both sources and local copies when publication reachability is unknown. A real-repository abuse test proves destination-path substitution cannot change committed bytes and unrelated pre-staged secret-shaped content remains staged but never enters the published commit.
- [ ] Installer shell P7 invocation after C1 + MC-CRED integration; production command is registered but the C1 shell has not yet called it. - [ ] Installer shell P7 invocation after C1 + MC-CRED integration; production command is registered but the C1 shell has not yet called it.
@@ -1,4 +1,4 @@
import { afterEach, describe, expect, it } from 'vitest'; import { afterEach, describe, expect, it, vi } from 'vitest';
import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os'; import { tmpdir } from 'node:os';
import { join } from 'node:path'; import { join } from 'node:path';
@@ -30,6 +30,7 @@ function fixture(): { readonly root: string; readonly directory: string; readonl
} }
afterEach((): void => { afterEach((): void => {
vi.restoreAllMocks();
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
}); });
@@ -41,6 +42,19 @@ describe('security-critical brain configuration reads', (): void => {
expect(secure.readBrainConfigSecure(config.file, config.root)).toBe('{"version":1}\n'); expect(secure.readBrainConfigSecure(config.file, config.root)).toBe('{"version":1}\n');
}); });
it('rejects a managed root owned by a UID other than the running principal', async (): Promise<void> => {
const secure = await loadSecureConfig();
const config = fixture();
if (typeof process.getuid !== 'function') throw new Error('test requires POSIX getuid');
const processWithUid = process as typeof process & { getuid: () => number };
const actualUid = processWithUid.getuid();
vi.spyOn(processWithUid, 'getuid').mockReturnValue(actualUid + 1);
expect(() => secure.readBrainConfigSecure(config.file, config.root)).toThrow(
/config-ancestor-owner-unsafe/,
);
});
it('rejects a group/world-writable policy file', async (): Promise<void> => { it('rejects a group/world-writable policy file', async (): Promise<void> => {
const secure = await loadSecureConfig(); const secure = await loadSecureConfig();
const config = fixture(); const config = fixture();