fix(redaction): close independent review findings (#1873)

This commit was merged in pull request #1916.
This commit is contained in:
jason.woltje committed 2026-11-10 19:56:05 -06:00
1 parent c8ff8ea128
commit a3e82efe23
9 files changed
+40 -15

No files matched your search

+1 -1
View File
@@ -86,7 +86,7 @@ function trackerFor(system, b, warn, dataRoot) {
if (dataRoot && managedState({ dataRoot, business: b.id, project: id, entry }) === 'reserved') continue;
if (!existsSync(projectFilePath(entry.root))) continue;
const project = loadProject(entry.root);
if (project.id !== id) throw new CliError(`project file ${project.file} has id ${project.id}, but business ${b.id} declares it as ${id}`, 3);
if (project.id !== id) throw new CliError(`project file ${project.file} has id that does not match the declared project key`, 3);
const vars = resolveInstance({ system, business: b, project, instance: first }).vars;
if (vars["tracker.project"] !== undefined) named.push({ id, vars });
}
+12 -3
View File
@@ -46,7 +46,7 @@ for (const [field, edit] of configCases) test(`config ${field} omits the configu
test('config-command refuses system config without relaying stderr file values', t => {
const root = scratch(t), f = fixture(root), d = JSON.parse(readFileSync(f.config, 'utf8')); d.execution.model = secret + '\n'; writeFileSync(f.config, JSON.stringify(d));
let err = ''; const status = runConfigCommand(['validate', 'acme'], { env: f.env, stdout: { write() {} }, stderr: { write(s) { err += s; } } });
assert.equal(status, 3); assert.match(err, /system config problem \(mosaic-config exit 2\)/); assert.ok(err.includes(f.config), err); assert.doesNotMatch(err, /sk-test|NOT-A-SECRET|execution.model/);
assert.equal(status, 3); assert.match(err, /system config problem \(mosaic-config exit 2\)/); assert.ok(err.includes(f.config), err); assert.doesNotMatch(err, /sk-test|NOT-A-SECRET/); assert.match(err, /execution.model/); assert.match(err, /expected a 1-200 character name/);
});
test('project id mismatch omits the file value in config-command', t => {
const root = scratch(t), f = fixture(root), file = join(f.doc.projects.stack.root, '.mosaic', 'project.json');
@@ -71,12 +71,21 @@ for (const mission of [false, true]) test(`duplicate ${mission ? 'mission' : 'ta
const r = spawnSync(process.execPath, [join(repo, 'scripts/mosaic-task.mjs'), 'validate', file], { encoding: 'utf8', env: { PATH: process.env.PATH, HOME: process.env.HOME, TMPDIR: process.env.TMPDIR } });
assert.equal(r.status, 2); assert.match(r.stderr, /duplicate tool in/); assert.doesNotMatch(r.stderr, /read/);
});
test('config-command does not relay arbitrary child stderr', t => {
for (const childStderr of [secret, `mosaic-config: execution.model must match ${secret}`]) test(`config-command does not relay arbitrary child stderr${childStderr.startsWith('mosaic-config:') ? ' after a known field' : ''}`, t => {
const root = scratch(t), file = join(root, 'config.json');
const code = `import cp from 'node:child_process'; import { syncBuiltinESMExports } from 'node:module';
cp.spawnSync = () => ({ status: 2, stderr: ${JSON.stringify(secret)}, stdout: '' }); syncBuiltinESMExports();
cp.spawnSync = () => ({ status: 2, stderr: ${JSON.stringify(childStderr)}, stdout: '' }); syncBuiltinESMExports();
const { runConfigCommand } = await import(${JSON.stringify(new URL('../../organization/src/config-command.mjs', import.meta.url).href)});
process.exitCode = runConfigCommand(['validate', 'acme'], { env: { MOSAIC_CONFIG: ${JSON.stringify(file)} } });`;
const r = spawnSync(process.execPath, ['--input-type=module', '-e', code], { encoding: 'utf8', env: { PATH: process.env.PATH, HOME: process.env.HOME, TMPDIR: process.env.TMPDIR } });
assert.equal(r.status, 3, r.stderr); assert.ok(r.stderr.includes(file), r.stderr); assert.doesNotMatch(r.stderr, /sk-test|NOT-A-SECRET/);
});
import { bootConfig, loadSystem } from '../src/config.mjs';
import { CliError } from '../src/errors.mjs';
test('CLI boot project id mismatch omits the configured value and keeps exit 3', t => {
const root = scratch(t), f = fixture(root), file = join(f.doc.projects.stack.root, '.mosaic', 'project.json');
mkdirSync(join(f.doc.projects.stack.root, '.mosaic'));
writeFileSync(file, JSON.stringify({ projectVersion: 1, id: secret.toLowerCase() }));
assert.throws(() => bootConfig({ system: loadSystem({ env: f.env }), businessId: 'acme', env: f.env }), safe(file, CliError, { exitCode: 3 }, secret.toLowerCase()));
});
+1 -1
View File
@@ -343,7 +343,7 @@ function setsparkConfig(raw, where, users) {
const approvers = {};
for (const u of users) {
const name = u.name.trim().toLowerCase();
if (Object.hasOwn(approvers, name)) throw new DiscordError(`${where}: two users named ${name}; approver names must be distinct`);
if (Object.hasOwn(approvers, name)) throw new DiscordError(`${where}: two users share a name; approver names must be distinct (case-insensitive)`);
approvers[name] = u.id;
}
try {
+1 -1
View File
@@ -96,7 +96,7 @@ export function loadSetsparkConfig(raw, where = "setspark") {
function loadApprovers(raw, where) {
if (!isObject(raw) || Object.keys(raw).length > 64) throw new Error(`${where}: must be an object of at most 64 names`);
for (const [name, id] of Object.entries(raw)) {
if (name.length === 0 || name.length > 64 || name !== name.trim().toLowerCase() || /[\u0000-\u001f\u007f]/.test(name)) throw new Error(`${where}: ${JSON.stringify(name.slice(0, 64))} must be a trimmed lower-case name`);
if (name.length === 0 || name.length > 64 || name !== name.trim().toLowerCase() || /[\u0000-\u001f\u007f]/.test(name)) throw new Error(`${where}: an approver name must be trimmed, lower-case, 1-64 characters, with no control characters`);
if (typeof id !== "string" || !SNOWFLAKE.test(id)) throw new Error(`${where}.${name}: must be a Discord user id`);
}
if (new Set(Object.values(raw)).size !== Object.keys(raw).length) throw new Error(`${where}: one Discord user id under two names`);
@@ -32,3 +32,12 @@ for (const mode of ['read', 'list']) test(`turn ${mode} parser omits file bytes
const file = join(root, 'turns', 'turn-1.json'); writeFileSync(file, secret);
assert.throws(() => mode === 'read' ? readTurn(root, 'turn-1') : listTurns(root), safe(file, SyntaxError));
});
for (const duplicate of [true, false]) test(`binding approver ${duplicate ? 'duplicate' : 'invalid'} name omits user values`, t => {
const root = scratch(t), keyFile = join(root, 'fixture-key'); writeFileSync(keyFile, 'fixture\n', { mode: 0o600 });
const name = secret.toLowerCase() + (duplicate ? '' : '\u0001');
const users = [{ id: '100000000000000100', name }];
if (duplicate) users.push({ id: '100000000000000101', name });
const raw = rawBinding({ users, tools: { roots: [{ name: 'docs', path: root }], setspark: { baseUrl: 'https://api.setspark.io', keyFile, principal: 'sage' } } });
assert.throws(() => validateBinding(raw), safe(duplicate ? 'users' : 'approvers', DiscordError, { exitCode: 2 }, secret.toLowerCase()));
});
+1 -1
View File
@@ -157,7 +157,7 @@ test("setspark config: approvers come from the binding's users, never from the b
const b = validateBinding(rawBinding({ users, tools: { roots: [{ name: "docs", path: docs }], setspark: { baseUrl: "https://api.setspark.io", keyFile: kf, principal: "sage" } } }));
assert.deepEqual(b.tools.setspark.approvers, { jason: "100000000000000100", carmen: "100000000000000101" });
assert.throws(() => validateBinding(rawBinding({ users, tools: { roots: [{ name: "docs", path: docs }], setspark: { baseUrl: "https://api.setspark.io", keyFile: kf, principal: "sage", approvers: { mallory: "100000000000000199" } } } })), /approvers come from users/);
assert.throws(() => validateBinding(rawBinding({ users: [{ id: "100000000000000100", name: "Jason" }, { id: "100000000000000101", name: "jason" }], tools: { roots: [{ name: "docs", path: docs }], setspark: { baseUrl: "https://api.setspark.io", keyFile: kf, principal: "sage" } } })), /two users named jason/);
assert.throws(() => validateBinding(rawBinding({ users: [{ id: "100000000000000100", name: "Jason" }, { id: "100000000000000101", name: "jason" }], tools: { roots: [{ name: "docs", path: docs }], setspark: { baseUrl: "https://api.setspark.io", keyFile: kf, principal: "sage" } } })), /two users share a name/);
const withCarmenOut = validateBinding(rawBinding({ users: [users[0]], tools: { roots: [{ name: "docs", path: docs }], setspark: { baseUrl: "https://api.setspark.io", keyFile: kf, principal: "sage" } } }));
assert.throws(() => reloadDiff(b, withCarmenOut), /tools cannot change/, "pi's approvers are fixed at start, so a user change needs a restart");
const channelsOnly = validateBinding(rawBinding({ users: [{ ...users[0], channels: [rawBinding().channels[0].id] }, users[1]], tools: { roots: [{ name: "docs", path: docs }], setspark: { baseUrl: "https://api.setspark.io", keyFile: kf, principal: "sage" } } }));
+12 -5
View File
@@ -67,12 +67,19 @@ export function runConfigCommand(argv, { stdout = process.stdout, stderr = proce
maxBuffer: 1024 * 1024,
env,
});
// Keep the parser-refusal fragment callers already recognize, without relaying
// any child stderr bytes (including parser excerpts or unsupported values).
// Render known key paths with fixed constraints; never relay child stderr.
if (proc.status !== 0) {
const syntax = proc.stderr.trim().startsWith("mosaic-config: configuration is not valid JSON (")
? "configuration is not valid JSON: " : "";
fail(3, `system config problem (mosaic-config exit ${proc.status}): ${syntax}${env.MOSAIC_CONFIG || join(env.HOME || "~", ".config", "mosaic-dev", "config.json")}${proc.error?.code ? ` (${proc.error.code})` : ""}`);
const problem = proc.stderr.trim();
const field = problem.startsWith('mosaic-config: execution "dataRoot"') ? "dataRoot"
: /^mosaic-config: (?:unsupported )?"?((?:configVersion|environment|dataRoot|execution|console|federation|compactionReadBytes|compactionStreams)(?:\.[A-Za-z][A-Za-z0-9]*|\[\d+\])*)"?(?=[: ])/u.exec(problem)?.[1];
const name = "a 1-200 character name starting with a letter or digit and containing only letters, digits, . _ : / -";
const expected = { configVersion: "1", environment: "development or production", dataRoot: "a canonical absolute instance data path",
"execution.backend": "docker", "execution.adapter": "pi or mock", "execution.provider": name, "execution.model": name };
const detail = problem.startsWith("mosaic-config: configuration is not valid JSON (")
? "configuration is not valid JSON; expected readable JSON: "
: field ? `${field}: expected ${expected[field] ?? "a value satisfying the system configuration schema"}: `
: "expected valid system configuration: ";
fail(3, `system config problem (mosaic-config exit ${proc.status}): ${detail}${env.MOSAIC_CONFIG || join(env.HOME || "~", ".config", "mosaic-dev", "config.json")}${proc.error?.code ? ` (${proc.error.code})` : ""}`);
}
try {
return JSON.parse(proc.stdout);
+1 -1
View File
@@ -311,7 +311,7 @@ function checkRound(v, n) {
}
export function validateRow(row) {
keysExactly(row, ROW_KEYS, `row id`);
keysExactly(row, ROW_KEYS, `row`);
const w = `row ${row.id}`;
checkId(row.id);
checkCellText(row.piece, `${w} piece`, { max: 300 });
+2 -2
View File
@@ -22,7 +22,7 @@ for (const [field, fn] of [
['owner', () => checkName(secret, 'owner')],
['path', () => checkRepoPath(secret + '/', 'path')],
['manifest', () => parseManifest(secret + '\n')],
['row id', () => validateRow({ id: secret })],
['row', () => validateRow({ id: secret })],
['version', () => loadDoc(Buffer.from(serialize({ version: secret, canonicalRoot: '/x', revision: 0, rows: [], log: [] })))],
]) test(`queue ${field} semantic refusal omits the value`, () => assert.throws(fn, safe(field, QueueError, { code: 2 }, secret.toLowerCase())));
import { checkTransport, parseBriefSpec, genesisRows, parseMigrationMap, render, sha256, genesisReceipt } from '../src/queue.mjs';
@@ -123,7 +123,7 @@ test('migration map duplicate owner omits unvalidated values', () => {
const row = structuredClone(MAP_ROWS[1]); row.owner = secret; row.reviewers = [secret];
assert.throws(() => parseMigrationMap(mapText([row])), safe('owner', QueueError, { code: 2 }));
});
test('genesis expected claim omits the stored op', () => {
test('BASE control: genesis expected claim already omits the stored op', () => {
const d = doc(); d.log[0].op = seatValue; d.log[0].result.receipt = genesisReceipt(seatValue, d.rows.length);
assert.throws(() => loadDoc(Buffer.from(serialize(d))), safe('claim', QueueError, { code: 2 }, seatValue));
});