This commit is contained in:
@@ -83,10 +83,10 @@ 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] 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] 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: 95/95 (secure config 4, store 44, runtime 18, owner resolver 16, provision 5, 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: 97/97 (secure config 4, store 45, runtime 19, owner resolver 16, provision 5, 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] 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. `.gitignore` is canonical allowlisted content only: an existing noncanonical regular file fails closed and is never merged into publication. Symlinked `.gitignore`, layout directories, and nested migration destinations fail closed; a dirty checkout blocks provisioning before skeleton publication.
|
||||
- [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. `.gitignore` is canonical allowlisted content only: an existing noncanonical regular file fails closed and is never merged into publication. Symlinked `.gitignore`, layout directories, and nested migration destinations fail closed; a dirty checkout blocks provisioning before skeleton publication. The brain root is principal-owned mode `0700` before clone and after clone, all memory-bearing layout directories are mode `0700` even under umask `0022`, and doctor reports owner-accessible roots as hard unsafe findings.
|
||||
- [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.
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user