fix(mosaic): isolate brain publication index
This commit is contained in:
@@ -83,13 +83,13 @@ 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: 86/86 (secure config 3, store 43, runtime 12, 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: 87/87 (secure config 3, 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] 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] 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] 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 retries non-fast-forward races via fetch/rebase rather than LWW, and unknown publication reachability retains both sources and local copies.
|
||||
- [x] Multi-host push-on-write uses an isolated temporary Git index, verifies the exact changed-path allowlist plus 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 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.
|
||||
- [ ] Implementation green on merged dependency base.
|
||||
- [ ] Independent code review.
|
||||
|
||||
@@ -301,6 +301,7 @@ describe('P7 brain provisioning orchestration', (): void => {
|
||||
writeFileSync(source, 'durable finding\n');
|
||||
const requests: CommandRequest[] = [];
|
||||
let commitOrdinal = 0;
|
||||
let approvedPaths: string[] = [];
|
||||
const runner: CommandRunner = (request): CommandResult => {
|
||||
requests.push(request);
|
||||
if (request.program === 'mosaic') {
|
||||
@@ -325,6 +326,24 @@ describe('P7 brain provisioning orchestration', (): void => {
|
||||
mkdirSync(join(input.root, '.git'), { recursive: true });
|
||||
return { status: 0, stdout: '', stderr: '' };
|
||||
}
|
||||
if (command.includes(' add -- ')) {
|
||||
const separator = request.args.indexOf('--');
|
||||
approvedPaths = separator === -1 ? [] : [...request.args.slice(separator + 1)];
|
||||
return { status: 0, stdout: '', stderr: '' };
|
||||
}
|
||||
if (command.includes('diff --cached --quiet')) {
|
||||
return { status: 1, stdout: '', stderr: '' };
|
||||
}
|
||||
if (command.includes('diff-tree')) {
|
||||
return { status: 0, stdout: `${approvedPaths.join('\0')}\0`, stderr: '' };
|
||||
}
|
||||
if (command.includes('show -s')) {
|
||||
return {
|
||||
status: 0,
|
||||
stdout: 'seat-a\[email protected]\0seat-a\[email protected]\n',
|
||||
stderr: '',
|
||||
};
|
||||
}
|
||||
if (command.includes('rev-parse --is-inside-work-tree')) {
|
||||
return { status: 0, stdout: 'true\n', stderr: '' };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
@@ -46,6 +47,7 @@ interface PublishEvidence {
|
||||
}
|
||||
|
||||
interface BrainRuntimeModule {
|
||||
readonly systemCommandRunner: CommandRunner;
|
||||
collectBrainDoctorReport(
|
||||
input: {
|
||||
readonly registrySource: string;
|
||||
@@ -544,6 +546,64 @@ describe('R5 production refusal control', (): void => {
|
||||
});
|
||||
|
||||
describe('push-on-write publication', (): void => {
|
||||
it('never commits or pushes an unrelated pre-staged secret-shaped file', async (): Promise<void> => {
|
||||
const runtime = await loadRuntime('MB-REQ-06 isolated publication index');
|
||||
const root = tempRoot();
|
||||
const remote = join(root, 'remote.git');
|
||||
const checkout = join(root, 'brain');
|
||||
execFileSync('git', ['init', '--bare', '--initial-branch=main', remote]);
|
||||
execFileSync('git', ['init', '--initial-branch=main', checkout]);
|
||||
writeFileSync(join(checkout, 'README.md'), 'brain\n');
|
||||
execFileSync('git', ['-C', checkout, 'add', 'README.md']);
|
||||
execFileSync('git', [
|
||||
'-C',
|
||||
checkout,
|
||||
'-c',
|
||||
'user.name=fixture',
|
||||
'-c',
|
||||
'[email protected]',
|
||||
'commit',
|
||||
'-m',
|
||||
'seed',
|
||||
]);
|
||||
execFileSync('git', ['-C', checkout, 'remote', 'add', 'origin', remote]);
|
||||
execFileSync('git', ['-C', checkout, 'push', '-u', 'origin', 'main']);
|
||||
writeFileSync(join(checkout, '.env'), 'TEST_ONLY_SECRET=fixture-value\n');
|
||||
execFileSync('git', ['-C', checkout, 'add', '-f', '.env']);
|
||||
const approved = join(checkout, 'finding.md');
|
||||
writeFileSync(approved, 'approved finding\n');
|
||||
|
||||
const evidence = runtime.publishBrainPaths(
|
||||
{
|
||||
root: checkout,
|
||||
identity: 'seat-a',
|
||||
paths: [approved],
|
||||
message: 'append approved finding',
|
||||
},
|
||||
runtime.systemCommandRunner,
|
||||
);
|
||||
|
||||
const committed = execFileSync('git', [
|
||||
'-C',
|
||||
checkout,
|
||||
'diff-tree',
|
||||
'--no-commit-id',
|
||||
'--name-only',
|
||||
'-r',
|
||||
evidence.commit,
|
||||
]).toString();
|
||||
const staged = execFileSync('git', [
|
||||
'-C',
|
||||
checkout,
|
||||
'diff',
|
||||
'--cached',
|
||||
'--name-only',
|
||||
]).toString();
|
||||
expect(committed.trim().split('\n')).toEqual(['finding.md']);
|
||||
expect(staged.trim().split('\n')).toContain('.env');
|
||||
expect(evidence.reachable).toBe(true);
|
||||
});
|
||||
|
||||
it('commits with command-scoped identity, pushes immediately, and proves reachability from origin/main', async (): Promise<void> => {
|
||||
const runtime = await loadRuntime('MB-REQ-07 publish-on-write reachability');
|
||||
const root = tempRoot();
|
||||
@@ -558,6 +618,19 @@ describe('push-on-write publication', (): void => {
|
||||
if (command.includes('rev-parse origin/main')) {
|
||||
return { status: 0, stdout: `${remoteHead}\n`, stderr: '' };
|
||||
}
|
||||
if (command.includes('diff --cached --quiet')) {
|
||||
return { status: 1, stdout: '', stderr: '' };
|
||||
}
|
||||
if (command.includes('diff-tree')) {
|
||||
return { status: 0, stdout: '.gitignore\0lanes/lane-a/.gitkeep\0', stderr: '' };
|
||||
}
|
||||
if (command.includes('show -s')) {
|
||||
return {
|
||||
status: 0,
|
||||
stdout: 'seat-a\[email protected]\0seat-a\[email protected]\n',
|
||||
stderr: '',
|
||||
};
|
||||
}
|
||||
return { status: 0, stdout: '', stderr: '' };
|
||||
};
|
||||
|
||||
@@ -632,6 +705,23 @@ describe('push-on-write publication', (): void => {
|
||||
if (command.includes('rev-parse origin/main')) {
|
||||
return { status: 0, stdout: `${remoteHead}\n`, stderr: '' };
|
||||
}
|
||||
if (command.includes('diff --cached --quiet')) {
|
||||
return { status: 1, stdout: '', stderr: '' };
|
||||
}
|
||||
if (command.includes('diff-tree')) {
|
||||
return {
|
||||
status: 0,
|
||||
stdout: 'lanes/lane-a/findings/host-a.md\0',
|
||||
stderr: '',
|
||||
};
|
||||
}
|
||||
if (command.includes('show -s')) {
|
||||
return {
|
||||
status: 0,
|
||||
stdout: 'seat-a\[email protected]\0seat-a\[email protected]\n',
|
||||
stderr: '',
|
||||
};
|
||||
}
|
||||
return { status: 0, stdout: '', stderr: '' };
|
||||
},
|
||||
);
|
||||
@@ -679,7 +769,7 @@ describe('push-on-write publication', (): void => {
|
||||
);
|
||||
|
||||
expect(evidence).toEqual({ commit, remoteHead: commit, reachable: true });
|
||||
expect(requests.some((request) => request.args.includes('push'))).toBe(true);
|
||||
expect(requests.some((request) => request.args.includes('push'))).toBe(false);
|
||||
});
|
||||
|
||||
it('does not manufacture reachability when merge-base rejects the new commit', async (): Promise<void> => {
|
||||
@@ -703,6 +793,19 @@ describe('push-on-write publication', (): void => {
|
||||
if (command.includes('rev-parse origin/main')) {
|
||||
return { status: 0, stdout: `${remoteHead}\n`, stderr: '' };
|
||||
}
|
||||
if (command.includes('diff --cached --quiet')) {
|
||||
return { status: 1, stdout: '', stderr: '' };
|
||||
}
|
||||
if (command.includes('diff-tree')) {
|
||||
return { status: 0, stdout: '.gitignore\0', stderr: '' };
|
||||
}
|
||||
if (command.includes('show -s')) {
|
||||
return {
|
||||
status: 0,
|
||||
stdout: 'seat-a\[email protected]\0seat-a\[email protected]\n',
|
||||
stderr: '',
|
||||
};
|
||||
}
|
||||
if (command.includes('merge-base --is-ancestor')) {
|
||||
return { status: 1, stdout: '', stderr: '' };
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { existsSync } from 'node:fs';
|
||||
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
||||
import {
|
||||
assessCredentialResult,
|
||||
deriveBrainTarget,
|
||||
@@ -84,6 +85,15 @@ function runGit(run: CommandRunner, identity: string, args: readonly string[]):
|
||||
return run({ program: 'git', args, env: commandEnv(identity) });
|
||||
}
|
||||
|
||||
function runGitWithEnv(
|
||||
run: CommandRunner,
|
||||
identity: string,
|
||||
args: readonly string[],
|
||||
env: Readonly<Record<string, string>>,
|
||||
): CommandResult {
|
||||
return run({ program: 'git', args, env: { ...commandEnv(identity), ...env } });
|
||||
}
|
||||
|
||||
export function collectBrainDoctorReport(
|
||||
input: {
|
||||
readonly registrySource: string;
|
||||
@@ -316,32 +326,6 @@ export function publishBrainPaths(
|
||||
if (input.message.trim().length === 0) throw new Error('brain-publish-message-empty');
|
||||
const paths = input.paths.map((path: string): string => containedRelative(input.root, path));
|
||||
|
||||
requireSuccess(
|
||||
runGit(run, input.identity, ['-C', input.root, 'add', '--', ...paths]),
|
||||
'brain-git-add',
|
||||
);
|
||||
const commitAttempt = runGit(run, input.identity, [
|
||||
'-C',
|
||||
input.root,
|
||||
'-c',
|
||||
`user.name=${input.identity}`,
|
||||
'-c',
|
||||
`user.email=${input.identity}@fleet.mosaicstack.dev`,
|
||||
'commit',
|
||||
'-m',
|
||||
input.message,
|
||||
]);
|
||||
if (commitAttempt.status !== 0) {
|
||||
const stagedDifference = runGit(run, input.identity, [
|
||||
'-C',
|
||||
input.root,
|
||||
'diff',
|
||||
'--cached',
|
||||
'--quiet',
|
||||
'--exit-code',
|
||||
]);
|
||||
if (stagedDifference.status !== 0) throw new Error('brain-git-commit-failed');
|
||||
}
|
||||
const readHead = (): string => {
|
||||
const result = runGit(run, input.identity, ['-C', input.root, 'rev-parse', 'HEAD']);
|
||||
requireSuccess(result, 'brain-git-read-commit');
|
||||
@@ -349,9 +333,100 @@ export function publishBrainPaths(
|
||||
if (!COMMIT.test(value)) throw new Error('brain-git-commit-shape-invalid');
|
||||
return value;
|
||||
};
|
||||
let commit = readHead();
|
||||
let pushed = false;
|
||||
for (let attempt = 0; attempt < 3; attempt += 1) {
|
||||
const verifyCommitIdentity = (commit: string): void => {
|
||||
const result = runGit(run, input.identity, [
|
||||
'-C',
|
||||
input.root,
|
||||
'show',
|
||||
'-s',
|
||||
'--format=%an%x00%ae%x00%cn%x00%ce',
|
||||
commit,
|
||||
]);
|
||||
requireSuccess(result, 'brain-git-read-commit-identity');
|
||||
const expectedEmail = `${input.identity}@fleet.mosaicstack.dev`;
|
||||
const [author, authorEmail, committer, committerEmail] = result.stdout.trimEnd().split('\0');
|
||||
if (
|
||||
author !== input.identity ||
|
||||
authorEmail !== expectedEmail ||
|
||||
committer !== input.identity ||
|
||||
committerEmail !== expectedEmail
|
||||
) {
|
||||
throw new Error('brain-git-commit-identity-mismatch');
|
||||
}
|
||||
};
|
||||
const verifyCommitPaths = (commit: string): void => {
|
||||
const changed = runGit(run, input.identity, [
|
||||
'-C',
|
||||
input.root,
|
||||
'diff-tree',
|
||||
'--root',
|
||||
'--no-commit-id',
|
||||
'--name-only',
|
||||
'-r',
|
||||
'-z',
|
||||
commit,
|
||||
]);
|
||||
requireSuccess(changed, 'brain-git-read-commit-paths');
|
||||
const names = changed.stdout.split('\0').filter(Boolean);
|
||||
const approved = new Set(paths);
|
||||
if (names.length === 0 || names.some((name: string): boolean => !approved.has(name))) {
|
||||
throw new Error('brain-git-commit-paths-unapproved');
|
||||
}
|
||||
};
|
||||
|
||||
const base = readHead();
|
||||
const indexRoot = mkdtempSync(join(tmpdir(), 'mosaic-brain-index-'));
|
||||
const isolatedEnv = { GIT_INDEX_FILE: join(indexRoot, 'index') };
|
||||
let commit = base;
|
||||
let createdCommit = false;
|
||||
try {
|
||||
requireSuccess(
|
||||
runGitWithEnv(run, input.identity, ['-C', input.root, 'read-tree', base], isolatedEnv),
|
||||
'brain-git-isolated-index-init',
|
||||
);
|
||||
requireSuccess(
|
||||
runGitWithEnv(run, input.identity, ['-C', input.root, 'add', '--', ...paths], isolatedEnv),
|
||||
'brain-git-add',
|
||||
);
|
||||
const difference = runGitWithEnv(
|
||||
run,
|
||||
input.identity,
|
||||
['-C', input.root, 'diff', '--cached', '--quiet', '--exit-code', base, '--', ...paths],
|
||||
isolatedEnv,
|
||||
);
|
||||
if (difference.status === 1) {
|
||||
requireSuccess(
|
||||
runGitWithEnv(
|
||||
run,
|
||||
input.identity,
|
||||
[
|
||||
'-C',
|
||||
input.root,
|
||||
'-c',
|
||||
`user.name=${input.identity}`,
|
||||
'-c',
|
||||
`user.email=${input.identity}@fleet.mosaicstack.dev`,
|
||||
'commit',
|
||||
'-m',
|
||||
input.message,
|
||||
],
|
||||
isolatedEnv,
|
||||
),
|
||||
'brain-git-commit',
|
||||
);
|
||||
commit = readHead();
|
||||
verifyCommitPaths(commit);
|
||||
verifyCommitIdentity(commit);
|
||||
createdCommit = true;
|
||||
} else if (difference.status !== 0) {
|
||||
throw new Error('brain-git-isolated-diff-failed');
|
||||
}
|
||||
} finally {
|
||||
rmSync(indexRoot, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
let pushed = !createdCommit;
|
||||
for (let attempt = 0; createdCommit && attempt < 3; attempt += 1) {
|
||||
const push = runGit(run, input.identity, ['-C', input.root, 'push', 'origin', 'HEAD:main']);
|
||||
if (push.status === 0) {
|
||||
pushed = true;
|
||||
@@ -364,10 +439,21 @@ export function publishBrainPaths(
|
||||
'brain-git-fetch-concurrent',
|
||||
);
|
||||
requireSuccess(
|
||||
runGit(run, input.identity, ['-C', input.root, 'rebase', 'origin/main']),
|
||||
runGit(run, input.identity, [
|
||||
'-C',
|
||||
input.root,
|
||||
'-c',
|
||||
`user.name=${input.identity}`,
|
||||
'-c',
|
||||
`user.email=${input.identity}@fleet.mosaicstack.dev`,
|
||||
'rebase',
|
||||
'origin/main',
|
||||
]),
|
||||
'brain-git-rebase-concurrent',
|
||||
);
|
||||
commit = readHead();
|
||||
verifyCommitPaths(commit);
|
||||
verifyCommitIdentity(commit);
|
||||
}
|
||||
if (!pushed) throw new Error('brain-git-push-failed');
|
||||
requireSuccess(
|
||||
|
||||
Reference in New Issue
Block a user