fix(mosaic): enforce private brain checkout
ci/woodpecker/pr/ci Pipeline failed

This commit is contained in:
2026-08-05 17:19:51 -05:00
parent 22cedbb506
commit a50b5a6b4d
8 changed files with 167 additions and 14 deletions
@@ -50,7 +50,8 @@ function renderReport(report: DoctorRuntimeReport): InstalledDoctorResult {
(finding): boolean =>
finding.code.endsWith('-error') ||
finding.code.endsWith('-indeterminate') ||
finding.code === 'brain-not-git-repository',
finding.code === 'brain-not-git-repository' ||
finding.code === 'brain-root-permissions-unsafe',
);
const status: InstalledDoctorResult['status'] =
findings.length === 0 ? 'ok' : hard ? 'error' : 'warn';
@@ -1,5 +1,13 @@
import { afterEach, describe, expect, it } from 'vitest';
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import {
existsSync,
lstatSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
@@ -359,6 +367,7 @@ describe('P7 brain provisioning orchestration', (): void => {
const requests: CommandRequest[] = [];
let commitOrdinal = 0;
let approvedPaths: string[] = [];
let privateAtClone = false;
const runner: CommandRunner = (request): CommandResult => {
requests.push(request);
if (request.program === 'mosaic') {
@@ -380,6 +389,7 @@ describe('P7 brain provisioning orchestration', (): void => {
};
}
if (request.args[0] === 'clone') {
privateAtClone = existsSync(input.root) && (lstatSync(input.root).mode & 0o077) === 0;
mkdirSync(join(input.root, '.git'), { recursive: true });
return { status: 0, stdout: '', stderr: '' };
}
@@ -455,6 +465,7 @@ describe('P7 brain provisioning orchestration', (): void => {
owner: { verdict: 'resolved', reasonCode: 'owner-verified' },
migration: { status: 'reported' },
});
expect(privateAtClone).toBe(true);
expect(existsSync(source)).toBe(true);
const imported = result.migration?.reported ?? [];
expect(imported).toEqual(
@@ -1,8 +1,10 @@
import { existsSync } from 'node:fs';
import { parseRequestedOwner, resolveProviderDurableOwner } from './brain-owner-resolver.js';
import {
createBrainSkeleton,
deriveBrainTarget,
discoverBrainMigration,
ensureBrainRootPrivate,
migrateBrainState,
type MigrationResult,
type MigrationOwnerResolution,
@@ -94,6 +96,13 @@ export async function provisionBrain(
const brainNamespace = parseRequestedOwner(input.requestedOwner);
if (brainNamespace === null) return blocked('owner-name-invalid', []);
const target = deriveBrainTarget(input.estateRegistrySource, input.targetGitUrl, brainNamespace);
if (existsSync(input.root)) {
try {
ensureBrainRootPrivate(input.root);
} catch {
return blocked('brain-root-permissions-unsafe', []);
}
}
const doctorInput = {
registrySource: input.estateRegistrySource,
targetGitUrl: input.targetGitUrl,
@@ -159,6 +168,11 @@ export async function provisionBrain(
}
if (report.findings.some((finding): boolean => finding.code === 'brain-clone-missing')) {
try {
ensureBrainRootPrivate(input.root);
} catch {
return failed('brain-root-permissions-unsafe', report.findings);
}
const clone: CommandRequest = {
program: 'git',
args: ['clone', '--branch', 'main', '--single-branch', target.cloneUrl, input.root],
@@ -166,6 +180,11 @@ export async function provisionBrain(
};
const cloneResult: CommandResult = dependencies.run(clone);
if (cloneResult.status !== 0) return failed('brain-clone-failed', report.findings);
try {
ensureBrainRootPrivate(input.root);
} catch {
return failed('brain-root-permissions-unsafe', report.findings);
}
report = collectBrainDoctorReport(doctorInput, dependencies.run);
}
@@ -177,6 +196,7 @@ export async function provisionBrain(
finding.code === 'brain-branch-mismatch' ||
finding.code === 'brain-uncommitted-state' ||
finding.code === 'brain-git-state-indeterminate' ||
finding.code === 'brain-root-permissions-unsafe' ||
finding.code.startsWith('brain-write-access-'),
);
if (blockingCloneFindings.length > 0) {
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it } from 'vitest';
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
@@ -361,6 +361,57 @@ describe('doctor runtime observation', (): void => {
}
});
it('reports an owner-accessible brain root as unsafe instead of ready', async (): Promise<void> => {
const runtime = await loadRuntime('MB-REQ-06 owner-only doctor measurement');
const root = join(tempRoot(), 'brain');
mkdirSync(join(root, '.git'), { recursive: true });
chmodSync(root, 0o755);
const report = runtime.collectBrainDoctorReport(
{
registrySource: registry(),
targetGitUrl: 'https://git.mosaicstack.dev/mosaicstack/stack.git',
brainNamespace: 'mosaicstack',
identity: 'seat-a',
root,
},
(request): CommandResult => {
if (request.program === 'mosaic') {
return {
status: 0,
stdout: validateResult('ok', 'validation-verified').replaceAll(
'synthetic-no-token',
'seat-a',
),
stderr: '',
};
}
const command = request.args.join(' ');
if (command.includes('rev-parse --is-inside-work-tree')) {
return { status: 0, stdout: 'true\n', stderr: '' };
}
if (command.includes('remote get-url origin')) {
return {
status: 0,
stdout: 'https://git.mosaicstack.dev/mosaicstack/mosaic-brain.git\n',
stderr: '',
};
}
if (command.includes('branch --show-current')) {
return { status: 0, stdout: 'main\n', stderr: '' };
}
if (command.includes('status --porcelain')) {
return { status: 0, stdout: '', stderr: '' };
}
return { status: 99, stdout: '', stderr: 'unexpected command' };
},
);
expect(report.findings).toEqual(
expect.arrayContaining([expect.objectContaining({ code: 'brain-root-permissions-unsafe' })]),
);
});
it.each(['remote', 'branch', 'status'] as const)(
'reports %s measurement failure as indeterminate rather than healthy or mismatched',
async (failedMeasurement): Promise<void> => {
@@ -4,7 +4,9 @@ import { tmpdir } from 'node:os';
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
import {
assessCredentialResult,
brainRootIsPrivate,
deriveBrainTarget,
ensureBrainRootPrivate,
evaluateBrainDoctor,
planBrainDoctorFix,
type BrainDoctorFinding,
@@ -174,6 +176,7 @@ export function collectBrainDoctorReport(
const observation: BrainDoctorObservation = {
rootExists,
rootPrivate: rootExists && brainRootIsPrivate(input.root),
gitRepository,
remote,
branch,
@@ -300,6 +303,13 @@ export function repairBrainDoctor(
if (report.access.outcome !== 'ok') return report;
continue;
}
if (action.findingCode === 'brain-clone-missing') {
try {
ensureBrainRootPrivate(input.root);
} catch {
return collectBrainDoctorReport(input, run);
}
}
const result = runGit(run, input.identity, action.args);
if (result.status !== 0) return collectBrainDoctorReport(input, run);
}
@@ -1,6 +1,7 @@
import { afterEach, describe, expect, it } from 'vitest';
import {
existsSync,
lstatSync,
mkdtempSync,
mkdirSync,
readFileSync,
@@ -90,6 +91,7 @@ interface MigrationResult {
interface BrainDoctorObservation {
readonly rootExists: boolean;
readonly rootPrivate: boolean;
readonly gitRepository: boolean;
readonly remote: string | null;
readonly branch: string | null;
@@ -359,6 +361,26 @@ describe('R6 — brain layout refuses secret material', (): void => {
);
});
it('creates the brain root and memory directories owner-only under a permissive umask', async (): Promise<void> => {
const sut = await loadSut('MB-REQ-06 owner-only brain checkout');
const brain = join(tempRoot(), 'brain');
const previousUmask = process.umask(0o022);
try {
sut.createBrainSkeleton(brain);
} finally {
process.umask(previousUmask);
}
for (const path of [
brain,
...['agents', 'lanes', 'board', 'specs', 'methods', 'archives'].map((name) =>
join(brain, name),
),
]) {
expect(lstatSync(path).mode & 0o077, path).toBe(0);
}
});
it('refuses existing noncanonical gitignore content instead of publishing it', async (): Promise<void> => {
const sut = await loadSut('MB-REQ-06 unapproved gitignore content');
const root = tempRoot();
@@ -998,6 +1020,7 @@ describe('R8 — doctor diagnoses defects and fixes only through approved seams'
const missing = sut.evaluateBrainDoctor(
{
rootExists: false,
rootPrivate: false,
gitRepository: false,
remote: null,
branch: null,
@@ -1013,6 +1036,7 @@ describe('R8 — doctor diagnoses defects and fixes only through approved seams'
const defects = sut.evaluateBrainDoctor(
{
rootExists: true,
rootPrivate: true,
gitRepository: true,
remote: 'https://git.uscllc.com/usc/mosaic-brain.git',
branch: 'main',
+45 -9
View File
@@ -1,4 +1,5 @@
import {
chmodSync,
closeSync,
constants as fsConstants,
existsSync,
@@ -19,6 +20,7 @@ import { createHash, randomUUID } from 'node:crypto';
import { platform } from 'node:os';
import { z } from 'zod';
import { parseCredentialEstateRegistry } from '../credentials/estate-registry.js';
import { assertNoSymlinkAncestors } from '../fleet/secure-file.js';
const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
const COMMIT = /^[0-9a-f]{40}$/;
@@ -174,6 +176,7 @@ export interface MigrationResult {
export interface BrainDoctorObservation {
readonly rootExists: boolean;
readonly rootPrivate: boolean;
readonly gitRepository: boolean;
readonly remote: string | null;
readonly branch: string | null;
@@ -343,6 +346,36 @@ export function deriveBrainTarget(
};
}
function processUid(): number {
if (typeof process.getuid !== 'function') throw new Error('brain-owner-check-unsupported');
return process.getuid();
}
export function brainRootIsPrivate(root: string): boolean {
try {
const status = lstatSync(root);
return (
status.isDirectory() &&
!status.isSymbolicLink() &&
status.uid === processUid() &&
(status.mode & 0o077) === 0
);
} catch {
return false;
}
}
export function ensureBrainRootPrivate(root: string): void {
assertNoSymlinkAncestors(root);
if (!existsSync(root)) mkdirSync(root, { recursive: true, mode: 0o700 });
const status = lstatSync(root);
if (!status.isDirectory() || status.isSymbolicLink() || status.uid !== processUid()) {
throw new Error('brain-layout-root-unsafe');
}
chmodSync(root, 0o700);
if (!brainRootIsPrivate(root)) throw new Error('brain-layout-root-permissions-unsafe');
}
function syncFile(path: string): void {
const descriptor = openSync(path, 'r');
try {
@@ -358,27 +391,22 @@ export function createBrainSkeleton(root: string): {
} {
const created: string[] = [];
const publicationEntries: MigrationPublishEntry[] = [];
if (existsSync(root)) {
const rootStatus = lstatSync(root);
if (!rootStatus.isDirectory() || rootStatus.isSymbolicLink()) {
throw new Error('brain-layout-root-unsafe');
}
}
ensureBrainRootPrivate(root);
for (const directory of BRAIN_DIRECTORIES) {
const path = join(root, directory);
if (existsSync(path)) {
const status = lstatSync(path);
if (!status.isDirectory() || status.isSymbolicLink()) {
if (!status.isDirectory() || status.isSymbolicLink() || status.uid !== processUid()) {
throw new Error('brain-layout-directory-unsafe');
}
chmodSync(path, 0o700);
}
}
mkdirSync(root, { recursive: true });
for (const directory of BRAIN_DIRECTORIES) {
const path = join(root, directory);
if (!existsSync(path)) {
mkdirSync(path, { recursive: true });
mkdirSync(path, { recursive: true, mode: 0o700 });
created.push(path);
}
const placeholder = join(path, '.gitkeep');
@@ -1075,8 +1103,16 @@ export function evaluateBrainDoctor(
];
}
const findings: BrainDoctorFinding[] = [];
if (!observation.rootPrivate) {
findings.push({
code: 'brain-root-permissions-unsafe',
repairable: false,
reasonCode: 'owner-only-root-required',
});
}
if (!observation.gitRepository) {
findings.push({ code: 'brain-not-git-repository', repairable: true, reasonCode: null });
if (access !== null) findings.push(access);
return findings;
}
if (observation.remote === null) {