Files
ECC/tests/lib/opencode-hook-consent-safety.test.js
T
affaan-m 46df9ccc87 fix(install): preserve inactive user configs and recheck consent before completion
Preserve contributor history while applying the exact reviewed current-main repair.

Source-PR: https://github.com/affaan-m/ECC/pull/3008
Source-Parent: 615b7f107a
Review-Manifest-SHA256: dcec1e51ab4e14305f1daf1f020cf1d1f4f990137a5941b17ab4d9a0db108b2c
2026-09-28 03:52:42 -04:00

751 lines
44 KiB
JavaScript

'use strict';
const assert = require('assert');
const crypto = require('crypto');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { createFileSystemLoader } = require('./helpers/load-with-file-system');
const fileSystem = { ...fs };
const load = createFileSystemLoader(fileSystem);
const { applyInstallPlan } = load(require.resolve('../../scripts/lib/install/apply'));
const { withHookConsent } = require('../../scripts/lib/install/hook-consent');
const { createInstallState, readInstallState, writeInstallState } = require('../../scripts/lib/install-state');
const { buildDoctorReport, repairInstalledStates } = load(require.resolve('../../scripts/lib/install-lifecycle'));
const REPO_ROOT = path.resolve(__dirname, '../..');
const sha256 = value => crypto.createHash('sha256').update(value).digest('hex');
// Authentic public ECC source fixtures, kept as inert bytes (never imported).
// Copying these bytes to an alias does not claim they were published build output.
const legacyPluginFixtures = [
{
name: "2.2.1 barrel",
// https://github.com/affaan-m/ECC/blob/ca185ef5f7667078a1e70a763bd3a9c71c48acf0/.opencode/plugins/index.ts
sha256: '965c5fac76ce0c3ceb3836814f5eb9ede8c9db50373a508c734f949cb321a21a',
content: `/**
* ECC Plugins for OpenCode
*
* This module exports all ECC plugins for OpenCode integration.
* Plugins provide hook-based automation that mirrors Claude Code's hook system
* while taking advantage of OpenCode's more sophisticated 20+ event types.
*/
export { ECCHooksPlugin, default } from "./ecc-hooks.js"
// Re-export for named imports
export * from "./ecc-hooks.js"
`,
},
{
name: "early barrel",
// https://github.com/affaan-m/ECC/blob/6d440c036df2c1b2fec957627d1202c3708e0627/.opencode/plugins/index.ts
sha256: 'e42c733adb177f84cea813663aa34c7868dbaa98c96950d0ef91cd211b8aa169',
content: `/**
* Everything Claude Code (ECC) Plugins for OpenCode
*
* This module exports all ECC plugins for OpenCode integration.
* Plugins provide hook-based automation that mirrors Claude Code's hook system
* while taking advantage of OpenCode's more sophisticated 20+ event types.
*/
export { ECCHooksPlugin, default } from "./ecc-hooks"
// Re-export for named imports
export * from "./ecc-hooks"
`,
},
];
function fixture(callback, enabled = false) {
const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-opencode-write-')));
const homeDir = path.join(root, 'home');
const sourceRoot = path.join(root, 'source');
const targetRoot = path.join(homeDir, '.config', 'opencode');
const adapter = { id: 'opencode-home', target: 'opencode', kind: 'home' };
try {
fs.mkdirSync(path.join(sourceRoot, 'manifests'), { recursive: true });
for (const name of ['install-modules.json', 'install-components.json', 'install-profiles.json']) {
fs.copyFileSync(path.join(REPO_ROOT, 'manifests', name), path.join(sourceRoot, 'manifests', name));
}
fs.copyFileSync(path.join(REPO_ROOT, 'package.json'), path.join(sourceRoot, 'package.json'));
const operations = ['opencode.json', 'plugins/ecc-hooks.ts'].map(relativePath => {
const sourceRelativePath = `.opencode/${relativePath}`;
const sourcePath = path.join(sourceRoot, sourceRelativePath);
const destinationPath = path.join(targetRoot, relativePath);
const content = relativePath === 'opencode.json'
? '{"plugin":["./plugins"],"userSetting":true}\n'
: 'export default async () => ({ "session.created": () => {} });\n';
fs.mkdirSync(path.dirname(sourcePath), { recursive: true });
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
fs.writeFileSync(sourcePath, content);
fs.writeFileSync(destinationPath, content);
return { kind: 'copy-file', moduleId: 'platform-configs', sourceRelativePath, sourcePath,
destinationPath, ownership: 'managed', scaffoldOnly: false,
strategy: 'preserve-relative-path', contentSha256: sha256(content) };
});
const dist = path.join(sourceRoot, '.opencode', 'dist');
fs.mkdirSync(path.join(dist, 'plugins'), { recursive: true });
fs.mkdirSync(path.join(dist, 'tools'), { recursive: true });
fs.writeFileSync(path.join(dist, 'index.js'), 'module.exports = {};\n');
const installStatePath = path.join(targetRoot, 'ecc-install-state.json');
const state = createInstallState({
adapter, targetRoot, installStatePath,
request: { modules: ['platform-configs'], legacyMode: true,
hookConsent: enabled ? 'enabled' : null },
resolution: { selectedModules: enabled ? ['platform-configs', 'hooks-runtime'] : ['platform-configs'],
skippedModules: [] },
operations, source: { repoVersion: '2.2.2', manifestVersion: 1 },
});
writeInstallState(installStatePath, state);
const basePlan = { target: 'opencode', adapter, homeDir, sourceRoot, targetRoot,
installRoot: targetRoot, installStatePath, operations, warnings: [],
selectedModuleIds: state.resolution.selectedModules, statePreview: state };
const previousCwd = process.cwd();
try {
// Hosted Windows checkouts and os.tmpdir() may be on different drives.
// Anchor relative-root callers inside their private fixture on every OS.
process.chdir(root);
callback({ root, homeDir, sourceRoot, targetRoot, installStatePath, state, basePlan,
declinePlan: withHookConsent(basePlan, 'declined') });
} finally {
process.chdir(previousCwd);
}
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
}
function repair(value, extra = {}) {
return repairInstalledStates({ repoRoot: value.sourceRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'],
buildOpencodePayload() { throw new Error('Unexpected compiler execution'); }, ...extra });
}
function withWritableOpenMutation(filePath, mutate, callback) {
const originalOpen = fileSystem.openSync;
const originalClose = fileSystem.closeSync;
let injected = false;
const descriptors = new Set();
fileSystem.openSync = function (candidate, flags, ...rest) {
const writable = typeof flags === 'number'
? Boolean(flags & (fs.constants.O_WRONLY | fs.constants.O_RDWR))
: /[wa+]/.test(flags);
const matches = typeof candidate === 'string' && path.resolve(candidate) === path.resolve(filePath);
if (matches && writable && !injected) {
injected = true;
mutate();
}
const fd = originalOpen.call(fs, candidate, flags, ...rest);
if (matches && writable) descriptors.add(fd);
return fd;
};
fileSystem.closeSync = function (fd) {
descriptors.delete(fd);
return originalClose.call(fs, fd);
};
try {
callback();
assert.ok(injected, 'The destination writable-open boundary must be exercised');
assert.strictEqual(descriptors.size, 0, 'Every owned writable descriptor must close');
} finally {
fileSystem.openSync = originalOpen;
fileSystem.closeSync = originalClose;
}
}
function assertPriorOwnership(value, filePath) {
const after = readInstallState(value.installStatePath);
const prior = value.state.operations.find(operation => operation.destinationPath === filePath);
const current = after.operations.find(operation => operation.destinationPath === filePath);
assert.strictEqual(current.contentSha256, prior.contentSha256, 'Do not adopt raced bytes');
assert.strictEqual(after.request.hookConsent, value.state.request.hookConsent);
}
function runTests() {
let passed = 0;
let failed = 0;
const test = (name, callback) => {
try { callback(); passed++; console.log(` PASS ${name}`); }
catch (error) { failed++; console.error(` FAIL ${name}: ${error.stack}`); }
};
test('relative-root fixture remains relative across Windows checkout and temp drives', () => {
const source = 'C:\\private\\source';
assert.strictEqual(path.win32.isAbsolute(path.win32.relative('D:\\checkout', source)), true);
const relative = path.win32.relative('C:\\private', source);
assert.strictEqual(relative, 'source');
assert.strictEqual(path.win32.isAbsolute(relative), false);
assert.strictEqual(path.win32.resolve('C:\\private', relative), source);
});
test('private filesystem injection never patches the process filesystem or module cache', () => fixture(value => {
const nativeOpen = fs.openSync;
const nativeClose = fs.closeSync;
const cachedApply = require.cache[require.resolve('../../scripts/lib/install/apply')];
const destination = path.join(value.targetRoot, 'plugins/ecc-hooks.ts');
withWritableOpenMutation(destination, () => {
assert.strictEqual(fs.openSync, nativeOpen);
assert.strictEqual(fs.closeSync, nativeClose);
}, () => assert.strictEqual(applyInstallPlan(value.declinePlan).applied, true));
assert.strictEqual(fs.openSync, nativeOpen);
assert.strictEqual(fs.closeSync, nativeClose);
assert.strictEqual(require.cache[require.resolve('../../scripts/lib/install/apply')], cachedApply);
}));
for (const relativePath of ['plugins/ecc-hooks.ts', 'opencode.json']) {
for (const mode of ['apply', 'repair']) {
test(`${mode} preserves a same-inode ${relativePath} edit at writable open`, () => fixture(value => {
const destination = path.join(value.targetRoot, relativePath);
const content = relativePath === 'opencode.json'
? '{"plugin":["./plugins"],"userEdit":"keep"}\n' : '// user edit: preserve this\n';
withWritableOpenMutation(destination, () => fs.writeFileSync(destination, content), () => {
if (mode === 'apply') {
assert.throws(() => applyInstallPlan(value.declinePlan), /changed after preflight/i);
} else {
const result = repair(value).results[0];
assert.strictEqual(result.status, 'error');
assert.match(result.error, /changed after preflight/i);
assert.notStrictEqual(result.stateRefreshed, true);
}
assert.strictEqual(fs.readFileSync(destination, 'utf8'), content);
assertPriorOwnership(value, destination);
});
}, mode === 'apply'));
}
}
for (const mode of ['apply', 'repair']) {
test(`${mode} preserves a file created after expected absence`, () => fixture(value => {
const destination = path.join(value.targetRoot, 'plugins/ecc-hooks.ts');
fs.unlinkSync(destination);
const content = '// newly created user file\n';
withWritableOpenMutation(destination, () => fs.writeFileSync(destination, content), () => {
if (mode === 'apply') assert.throws(() => applyInstallPlan(value.declinePlan), /EEXIST|changed after preflight/i);
else assert.strictEqual(repair(value).results[0].status, 'error');
assert.strictEqual(fs.readFileSync(destination, 'utf8'), content);
assertPriorOwnership(value, destination);
});
}, mode === 'apply'));
test(`${mode} does not recreate an expected existing file removed at open`, () => fixture(value => {
const destination = path.join(value.targetRoot, 'opencode.json');
withWritableOpenMutation(destination, () => fs.unlinkSync(destination), () => {
if (mode === 'apply') assert.throws(() => applyInstallPlan(value.declinePlan), /ENOENT|changed after preflight/i);
else assert.strictEqual(repair(value).results[0].status, 'error');
assert.strictEqual(fs.existsSync(destination), false);
assertPriorOwnership(value, destination);
});
}, mode === 'apply'));
}
for (const mode of ['apply', 'doctor', 'repair']) {
test(`${mode} reports malformed activation config with source context`, () => fixture(value => {
const destination = path.join(value.targetRoot, 'opencode.json');
fs.writeFileSync(destination, '{ malformed');
const before = fs.readFileSync(value.installStatePath);
if (mode === 'apply') assert.throws(() => applyInstallPlan(value.declinePlan), /Failed to parse .*opencode\.json/);
else if (mode === 'repair') assert.match(repair(value).results[0].error, /Failed to parse .*opencode\.json/);
else {
const result = buildDoctorReport({ repoRoot: value.sourceRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'] }).results[0];
assert.match(JSON.stringify(result.issues), /Failed to parse .*opencode\.json/);
}
assert.strictEqual(fs.readFileSync(destination, 'utf8'), '{ malformed');
assert.deepStrictEqual(fs.readFileSync(value.installStatePath), before);
}));
}
test('apply rejects another enabled writer and repair while holding its target lease', () => fixture(value => {
let checked = false;
applyInstallPlan(value.declinePlan, {
beforeOperationWrite() {
if (checked) return;
checked = true;
assert.throws(() => applyInstallPlan(withHookConsent(value.basePlan, 'enabled')), /Another ECC process|OpenCode.*lock/i);
assert.strictEqual(repair(value).results[0].status, 'error');
},
});
assert.ok(checked);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
assert.strictEqual(applyInstallPlan(value.declinePlan).applied, true);
}));
test('repair preserves ownership for a destination-classified activation with a recorded transform', () => fixture(value => {
const operation = value.state.operations.find(entry => entry.sourceRelativePath.endsWith('ecc-hooks.ts'));
const oldDestination = operation.destinationPath;
operation.sourceRelativePath = '.opencode/tools/fixture.js';
operation.contentTransform = 'opencode-disable-plugin-entrypoint';
operation.destinationPath = path.join(value.targetRoot, 'plugins', 'custom.js');
const source = path.join(value.sourceRoot, operation.sourceRelativePath);
fs.mkdirSync(path.dirname(source), { recursive: true });
fs.copyFileSync(oldDestination, source);
fs.unlinkSync(oldDestination);
writeInstallState(value.installStatePath, value.state);
const content = '// preserve unowned new custom plugin\n';
withWritableOpenMutation(operation.destinationPath, () => fs.writeFileSync(operation.destinationPath, content), () => {
const result = repair(value).results[0];
assert.strictEqual(result.status, 'error');
assert.match(result.error, /changed after preflight/i);
assert.strictEqual(fs.readFileSync(operation.destinationPath, 'utf8'), content);
assertPriorOwnership(value, operation.destinationPath);
});
}));
test('repair completes historical deactivation with exact inert bytes and releases its lock', () => fixture(value => {
const result = repair(value).results[0];
assert.strictEqual(result.status, 'repaired', result.error);
assert.strictEqual(result.stateRefreshed, true);
assert.strictEqual(fs.readFileSync(path.join(value.targetRoot, 'plugins/ecc-hooks.ts'), 'utf8'),
'export default async () => ({});\n');
assert.deepStrictEqual(JSON.parse(fs.readFileSync(path.join(value.targetRoot, 'opencode.json'))),
{ plugin: [], userSetting: true });
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
for (const consent of [null, 'declined']) {
for (const plugin of [undefined, ['user-plugin']]) {
test(`fresh ${consent || 'default'} apply preserves inactive user config ${JSON.stringify(plugin)}`, () => fixture(value => {
fs.unlinkSync(value.installStatePath);
fs.unlinkSync(path.join(value.targetRoot, 'plugins/ecc-hooks.ts'));
const destination = path.join(value.targetRoot, 'opencode.json');
const content = `${JSON.stringify({ userSetting: 'keep formatting', plugin }, null, 4)}\n`;
fs.writeFileSync(destination, content);
const result = applyInstallPlan(withHookConsent(value.basePlan, consent));
assert.strictEqual(result.applied, true);
assert.strictEqual(fs.readFileSync(destination, 'utf8'), content);
assert.ok(result.warnings.includes(`Skipped user-owned file ${destination}: the existing file is not recorded in ECC install-state.`));
assert.ok(result.skippedOperations.some(operation => operation.destinationPath === destination));
for (const operations of [result.operations, readInstallState(value.installStatePath).operations]) {
assert.ok(!operations.some(operation => operation.destinationPath === destination), 'Do not adopt a user config');
}
assert.strictEqual(fs.readFileSync(path.join(value.targetRoot, 'plugins/ecc-hooks.ts'), 'utf8'),
'export default async () => ({});\n');
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
}
test(`fresh ${consent || 'default'} apply still refuses active unrecorded config`, () => fixture(value => {
fs.unlinkSync(value.installStatePath);
fs.unlinkSync(path.join(value.targetRoot, 'plugins/ecc-hooks.ts'));
const destination = path.join(value.targetRoot, 'opencode.json');
const content = fs.readFileSync(destination);
assert.throws(() => applyInstallPlan(withHookConsent(value.basePlan, consent)), /Refusing OpenCode hook deactivation/);
assert.deepStrictEqual(fs.readFileSync(destination), content);
assert.strictEqual(fs.existsSync(path.join(value.targetRoot, 'plugins/ecc-hooks.ts')), false);
assert.strictEqual(fs.existsSync(value.installStatePath), false);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
test(`fresh ${consent || 'default'} apply rechecks skipped user config after writes`, () => fixture(value => {
fs.unlinkSync(value.installStatePath);
const plugin = path.join(value.targetRoot, 'plugins/ecc-hooks.ts');
fs.unlinkSync(plugin);
const destination = path.join(value.targetRoot, 'opencode.json');
fs.writeFileSync(destination, '{"userSetting":"initially inactive"}\n');
const activated = '{"plugin":["./plugins"],"userSetting":"late edit"}\n';
let injected = false;
const stateWritePhases = [];
assert.throws(() => applyInstallPlan(withHookConsent(value.basePlan, consent), {
beforeInstallStateWrite() { stateWritePhases.push(injected); },
beforeOperationWrite({ operation }) {
if (operation.destinationPath === plugin) {
injected = true;
fs.writeFileSync(destination, activated);
}
},
}), /OpenCode hook activation remains active/);
assert.ok(injected, 'Exercise the write boundary after preserving the inactive config');
assert.strictEqual(fs.readFileSync(destination, 'utf8'), activated);
assert.ok(!stateWritePhases.includes(true), 'Do not reach final state persistence');
// A retryable bridge may record the inert plugin already written, but
// must never adopt the preserved user config after this refusal.
const checkpoint = readInstallState(value.installStatePath);
assert.deepStrictEqual(checkpoint.operations.map(operation => operation.destinationPath), [plugin]);
assert.strictEqual(fs.readFileSync(plugin, 'utf8'), 'export default async () => ({});\n');
assert.strictEqual(checkpoint.operations[0].contentSha256, sha256(fs.readFileSync(plugin)));
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
test(`fresh ${consent || 'default'} apply preserves unrelated unrecorded plugin aliases`, () => fixture(value => {
fs.unlinkSync(value.installStatePath);
for (const operation of value.basePlan.operations) fs.unlinkSync(operation.destinationPath);
const aliases = ['index.ts', 'index.js', 'index.mjs', 'index.cjs', 'ecc-hooks.js'];
const content = 'export default async () => ({ "user.plugin": () => {} });\n';
for (const alias of aliases) fs.writeFileSync(path.join(value.targetRoot, 'plugins', alias), content);
const result = applyInstallPlan(withHookConsent(value.basePlan, consent));
assert.strictEqual(result.applied, true);
const state = readInstallState(value.installStatePath);
for (const alias of aliases) {
const destination = path.join(value.targetRoot, 'plugins', alias);
assert.strictEqual(fs.readFileSync(destination, 'utf8'), content);
assert.ok(!state.operations.some(operation => operation.destinationPath === destination), 'Do not adopt a user plugin');
}
}));
}
for (const missingDigest of [false, true]) {
test(`apply refuses inactive managed config with ${missingDigest ? 'missing digest' : 'changed bytes'}`, () => fixture(value => {
const destination = path.join(value.targetRoot, 'opencode.json');
const content = '{"userSetting":"inactive managed edit"}\n';
fs.writeFileSync(destination, content);
if (missingDigest) {
delete value.state.operations.find(operation => operation.destinationPath === destination).contentSha256;
writeInstallState(value.installStatePath, value.state);
}
const stateBefore = fs.readFileSync(value.installStatePath);
const plugin = path.join(value.targetRoot, 'plugins/ecc-hooks.ts');
const pluginBefore = fs.readFileSync(plugin);
assert.throws(() => applyInstallPlan(value.declinePlan), /Refusing OpenCode hook deactivation/);
assert.strictEqual(fs.readFileSync(destination, 'utf8'), content);
assert.deepStrictEqual(fs.readFileSync(value.installStatePath), stateBefore);
assert.deepStrictEqual(fs.readFileSync(plugin), pluginBefore);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
}
for (const missing of ['profile', 'module']) {
for (const mode of ['doctor', 'repair']) {
test(`${mode} reports missing ${missing} as a planning failure before any writes`, () => fixture(value => {
value.state.request.legacyMode = false;
value.state.request.profile = missing === 'profile' ? 'missing-fixture-profile' : null;
value.state.request.modules = missing === 'module' ? ['missing-fixture-module'] : [];
writeInstallState(value.installStatePath, value.state);
const paths = [value.installStatePath, ...value.basePlan.operations.map(operation => operation.destinationPath)];
const before = paths.map(file => fs.readFileSync(file));
let buildCalls = 0;
if (mode === 'doctor') {
const result = buildDoctorReport({ repoRoot: value.sourceRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'] }).results[0];
assert.strictEqual(result.status, 'error');
const planning = result.issues.filter(issue => issue.code === 'resolution-unavailable');
assert.strictEqual(planning.length, 1, JSON.stringify(result.issues));
assert.match(planning[0].message, new RegExp(`missing-fixture-${missing}`));
assert.strictEqual(result.issues.filter(issue => issue.code === 'opencode-hook-consent-violation').length, 0);
} else {
const result = repair(value, { buildOpencodePayload() { buildCalls++; throw new Error('Unexpected build'); } }).results[0];
assert.strictEqual(result.status, 'error');
assert.match(result.error, new RegExp(`missing-fixture-${missing}`));
assert.notStrictEqual(result.stateRefreshed, true);
}
assert.strictEqual(buildCalls, 0);
paths.forEach((file, index) => assert.deepStrictEqual(fs.readFileSync(file), before[index]));
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
}
}
test('doctor still identifies active declined activation as a consent violation', () => fixture(value => {
value.state.request.hookConsent = 'declined';
writeInstallState(value.installStatePath, value.state);
const before = fs.readFileSync(value.installStatePath);
const result = buildDoctorReport({ repoRoot: value.sourceRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'] }).results[0];
assert.strictEqual(result.status, 'error');
assert.strictEqual(result.issues.filter(issue => issue.code === 'opencode-hook-consent-violation').length, 1);
assert.strictEqual(result.issues.filter(issue => issue.code === 'resolution-unavailable').length, 0);
assert.deepStrictEqual(fs.readFileSync(value.installStatePath), before);
}));
for (const mode of ['doctor', 'repair']) {
test(`${mode} preserves an unrelated plugin without reporting ECC activation`, () => fixture(value => {
applyInstallPlan(value.declinePlan);
const destination = path.join(value.targetRoot, 'plugins', 'index.js');
const content = 'export default async () => ({ "user.plugin": () => {} });\n';
fs.writeFileSync(destination, content);
const before = fs.readFileSync(value.installStatePath);
if (mode === 'doctor') {
const result = buildDoctorReport({ repoRoot: value.sourceRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'] }).results[0];
assert.ok(!result.issues.some(issue => issue.code === 'opencode-hook-consent-violation'), JSON.stringify(result.issues));
} else {
const result = repair(value).results[0];
assert.notStrictEqual(result.status, 'error', result.error);
}
assert.strictEqual(fs.readFileSync(destination, 'utf8'), content);
const { lastValidatedAt: _beforeValidation, ...priorState } = JSON.parse(before);
const { lastValidatedAt: _afterValidation, ...afterState } = readInstallState(value.installStatePath);
assert.deepStrictEqual(afterState, priorState, 'Only the legitimate validation timestamp may change');
assert.ok(!afterState.operations.some(operation => operation.destinationPath === destination), 'Do not adopt a user plugin');
}));
}
for (const legacy of legacyPluginFixtures) {
for (const consent of [null, 'declined']) {
test(`fresh ${consent || 'default'} apply refuses the unrecorded historical ${legacy.name}`, () => fixture(value => {
assert.strictEqual(sha256(legacy.content), legacy.sha256, 'Preserve exact public-source fixture bytes');
assert.notStrictEqual(sha256(fs.readFileSync(path.join(value.sourceRoot, '.opencode/plugins/ecc-hooks.ts'))), legacy.sha256);
assert.notStrictEqual(sha256(fs.readFileSync(path.join(REPO_ROOT, '.opencode/plugins/index.ts'))), legacy.sha256);
fs.unlinkSync(value.installStatePath);
for (const operation of value.basePlan.operations) fs.unlinkSync(operation.destinationPath);
const alias = path.join(value.targetRoot, 'plugins', 'index.js');
assert.ok(!value.basePlan.operations.some(operation => operation.destinationPath === alias));
fs.writeFileSync(alias, legacy.content);
assert.throws(() => applyInstallPlan(withHookConsent(value.basePlan, consent)), /Refusing OpenCode hook deactivation/);
assert.strictEqual(fs.readFileSync(alias, 'utf8'), legacy.content);
assert.strictEqual(fs.existsSync(value.installStatePath), false, 'Do not adopt an unrecorded historical alias');
for (const operation of value.basePlan.operations) assert.strictEqual(fs.existsSync(operation.destinationPath), false);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
}
for (const mode of ['doctor', 'repair']) {
test(`${mode} refuses the unrecorded historical ${legacy.name} without changing ownership`, () => fixture(value => {
applyInstallPlan(value.declinePlan);
const alias = path.join(value.targetRoot, 'plugins', 'index.js');
fs.writeFileSync(alias, legacy.content);
const before = fs.readFileSync(value.installStatePath);
const operationsBefore = value.basePlan.operations.map(operation => fs.readFileSync(operation.destinationPath));
assert.ok(!readInstallState(value.installStatePath).operations.some(operation => operation.destinationPath === alias));
if (mode === 'doctor') {
const result = buildDoctorReport({ repoRoot: value.sourceRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'] }).results[0];
const issue = result.issues.find(entry => entry.code === 'opencode-hook-consent-violation');
assert.ok(issue, JSON.stringify(result.issues));
assert.match(issue.message, /OpenCode hook activation remains active/);
} else {
const result = repair(value).results[0];
assert.strictEqual(result.status, 'error');
assert.match(result.error, /Refusing OpenCode hook deactivation/);
assert.notStrictEqual(result.stateRefreshed, true);
}
assert.strictEqual(fs.readFileSync(alias, 'utf8'), legacy.content);
assert.deepStrictEqual(fs.readFileSync(value.installStatePath), before);
assert.deepStrictEqual(value.basePlan.operations.map(operation => fs.readFileSync(operation.destinationPath)), operationsBefore);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
}
}
for (const mode of ['apply', 'repair']) {
test(`${mode} refuses a historical alias inserted after preflight before state refresh`, () => fixture(value => {
const destination = path.join(value.targetRoot, 'plugins', 'ecc-hooks.ts');
const alias = path.join(value.targetRoot, 'plugins', 'index.js');
const content = legacyPluginFixtures[0].content;
withWritableOpenMutation(destination, () => fs.writeFileSync(alias, content), () => {
if (mode === 'apply') assert.throws(() => applyInstallPlan(value.declinePlan), /OpenCode hook activation remains active/);
else {
const result = repair(value).results[0];
assert.strictEqual(result.status, 'error');
assert.match(result.error, /OpenCode hook activation remains active/);
assert.notStrictEqual(result.stateRefreshed, true);
}
assert.strictEqual(fs.readFileSync(alias, 'utf8'), content);
const state = readInstallState(value.installStatePath);
assert.ok(!state.operations.some(operation => operation.destinationPath === alias));
assert.strictEqual(state.request.hookConsent, value.state.request.hookConsent);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
});
}));
}
for (const consent of [null, 'declined', 'enabled']) {
test(`nested JavaScript entrypoint applies with ${consent || 'default'} consent semantics`, () => fixture(value => {
const sourceRelativePath = '.opencode/plugins/custom/index.js';
const sourcePath = path.join(value.sourceRoot, sourceRelativePath);
const destinationPath = path.join(value.targetRoot, 'plugins/custom/index.js');
const content = 'export default async () => ({ event: () => {} });\n';
fs.mkdirSync(path.dirname(sourcePath), { recursive: true });
fs.writeFileSync(sourcePath, content);
const operation = { kind: 'copy-file', moduleId: 'platform-configs', sourceRelativePath,
sourcePath, destinationPath, ownership: 'managed', scaffoldOnly: false };
const selectedModuleIds = consent === 'enabled' ? ['platform-configs', 'hooks-runtime'] : ['platform-configs'];
const plan = withHookConsent({ ...value.basePlan, selectedModuleIds,
operations: [...value.basePlan.operations, operation] }, consent);
assert.strictEqual(applyInstallPlan(plan).applied, true);
assert.strictEqual(fs.readFileSync(destinationPath, 'utf8'), consent === 'enabled'
? content : 'export default async () => ({});\n');
}));
}
test('nested package metadata fails before writing and never receives a JavaScript tombstone', () => fixture(value => {
const destinationPath = path.join(value.targetRoot, 'plugins/custom/package.json');
const sourcePath = path.join(value.sourceRoot, '.opencode/plugins/custom/package.json');
fs.mkdirSync(path.dirname(sourcePath), { recursive: true });
fs.writeFileSync(sourcePath, '{"main":"index.js"}\n');
const before = fs.readFileSync(value.installStatePath);
const operation = { kind: 'copy-file', sourceRelativePath: '.opencode/plugins/custom/package.json',
sourcePath, destinationPath, ownership: 'managed' };
assert.throws(() => applyInstallPlan(withHookConsent({ ...value.basePlan,
operations: [...value.basePlan.operations, operation] }, 'declined')), /unsupported.*package/i);
assert.strictEqual(fs.existsSync(destinationPath), false);
assert.deepStrictEqual(fs.readFileSync(value.installStatePath), before);
}));
for (const rootForm of ['relative', 'missing']) {
for (const consent of [null, 'declined']) {
test(`${rootForm} source root keeps historical refusal for fresh ${consent || 'default'} apply`, () => fixture(value => {
const legacy = legacyPluginFixtures[0];
assert.strictEqual(sha256(legacy.content), legacy.sha256);
fs.unlinkSync(value.installStatePath);
for (const operation of value.basePlan.operations) fs.unlinkSync(operation.destinationPath);
const alias = path.join(value.targetRoot, 'plugins', 'index.js');
fs.writeFileSync(alias, legacy.content);
const sourceRoot = rootForm === 'relative'
? path.relative(process.cwd(), value.sourceRoot) : undefined;
if (sourceRoot) assert.strictEqual(path.isAbsolute(sourceRoot), false);
const plan = withHookConsent({ ...value.basePlan, sourceRoot }, consent);
assert.throws(() => applyInstallPlan(plan), /Refusing OpenCode hook deactivation/);
assert.strictEqual(fs.readFileSync(alias, 'utf8'), legacy.content);
assert.strictEqual(fs.existsSync(value.installStatePath), false);
for (const operation of value.basePlan.operations) assert.strictEqual(fs.existsSync(operation.destinationPath), false);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
}
}
for (const mode of ['doctor', 'repair']) {
for (const historical of [true, false]) {
test(`${mode} with relative repoRoot ${historical ? 'refuses historical ECC' : 'preserves unrelated user'} aliases`, () => fixture(value => {
applyInstallPlan(value.declinePlan);
const alias = path.join(value.targetRoot, 'plugins', 'index.js');
const content = historical ? legacyPluginFixtures[0].content
: 'export default async () => ({ "user.plugin": () => {} });\n';
fs.writeFileSync(alias, content);
const before = fs.readFileSync(value.installStatePath);
const operationsBefore = value.basePlan.operations.map(operation => fs.readFileSync(operation.destinationPath));
const repoRoot = path.relative(process.cwd(), value.sourceRoot);
assert.strictEqual(path.isAbsolute(repoRoot), false);
if (mode === 'doctor') {
const result = buildDoctorReport({ repoRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'] }).results[0];
const issue = result.issues.find(entry => entry.code === 'opencode-hook-consent-violation');
assert.strictEqual(Boolean(issue), historical, JSON.stringify(result.issues));
if (historical) assert.match(issue.message, /OpenCode hook activation remains active/);
} else {
const result = repair(value, { repoRoot }).results[0];
if (historical) {
assert.strictEqual(result.status, 'error');
assert.match(result.error, /Refusing OpenCode hook deactivation/);
assert.notStrictEqual(result.stateRefreshed, true);
} else assert.strictEqual(result.status, 'ok', result.error);
}
assert.strictEqual(fs.readFileSync(alias, 'utf8'), content);
if (historical) assert.deepStrictEqual(fs.readFileSync(value.installStatePath), before);
else {
const { lastValidatedAt: _beforeValidation, ...priorState } = JSON.parse(before);
const { lastValidatedAt: _afterValidation, ...afterState } = readInstallState(value.installStatePath);
assert.deepStrictEqual(afterState, priorState, 'Only the validation timestamp may change');
}
assert.ok(!readInstallState(value.installStatePath).operations.some(operation => operation.destinationPath === alias));
assert.deepStrictEqual(value.basePlan.operations.map(operation => fs.readFileSync(operation.destinationPath)), operationsBefore);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
}
}
for (const mode of ['apply', 'repair']) {
test(`${mode} with relative root refuses a historical alias inserted after preflight`, () => fixture(value => {
const alias = path.join(value.targetRoot, 'plugins', 'index.js');
const content = legacyPluginFixtures[0].content;
const repoRoot = path.relative(process.cwd(), value.sourceRoot);
withWritableOpenMutation(path.join(value.targetRoot, 'plugins', 'ecc-hooks.ts'),
() => fs.writeFileSync(alias, content), () => {
if (mode === 'apply') {
assert.throws(() => applyInstallPlan({ ...value.declinePlan, sourceRoot: repoRoot }),
/OpenCode hook activation remains active/);
} else {
const result = repair(value, { repoRoot }).results[0];
assert.strictEqual(result.status, 'error');
assert.match(result.error, /OpenCode hook activation remains active/);
assert.notStrictEqual(result.stateRefreshed, true);
}
assert.strictEqual(fs.readFileSync(alias, 'utf8'), content);
const state = readInstallState(value.installStatePath);
assert.ok(!state.operations.some(operation => operation.destinationPath === alias));
assert.strictEqual(state.request.hookConsent, value.state.request.hookConsent);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
});
}));
}
for (const artifact of ['source', 'build']) {
test(`relative repoRoot attributes only trusted current ${artifact} bytes in doctor and repair`, () => fixture(value => {
applyInstallPlan(value.declinePlan);
const source = artifact === 'source'
? path.join(value.sourceRoot, '.opencode', 'plugins', 'ecc-hooks.ts')
: path.join(value.sourceRoot, '.opencode', 'dist', 'plugins', 'index.js');
if (artifact === 'build') fs.writeFileSync(source, 'module.exports = { eccHook: true };\n');
const content = fs.readFileSync(source);
const alias = path.join(value.targetRoot, 'plugins', 'index.js');
fs.writeFileSync(alias, content);
const before = fs.readFileSync(value.installStatePath);
const operationsBefore = value.basePlan.operations.map(operation => fs.readFileSync(operation.destinationPath));
const repoRoot = path.relative(process.cwd(), value.sourceRoot);
assert.strictEqual(path.isAbsolute(repoRoot), false);
const doctor = buildDoctorReport({ repoRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'] }).results[0];
assert.ok(doctor.issues.some(issue => issue.code === 'opencode-hook-consent-violation'), JSON.stringify(doctor.issues));
const result = repair(value, { repoRoot }).results[0];
assert.strictEqual(result.status, 'error');
assert.match(result.error, /Refusing OpenCode hook deactivation/);
assert.notStrictEqual(result.stateRefreshed, true);
assert.deepStrictEqual(fs.readFileSync(alias), content);
assert.deepStrictEqual(fs.readFileSync(value.installStatePath), before);
assert.deepStrictEqual(value.basePlan.operations.map(operation => fs.readFileSync(operation.destinationPath)), operationsBefore);
assert.strictEqual(fs.existsSync(`${value.installStatePath}.ecc.lock`), false);
}));
}
for (const mode of ['doctor', 'repair']) {
test(`${mode} still rejects unsupported explicit repoRoot types`, () => fixture(value => {
for (const repoRoot of [{}, true, 1]) {
const invoke = mode === 'doctor'
? () => buildDoctorReport({ repoRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'] })
: () => repair(value, { repoRoot });
assert.throws(invoke, error => error instanceof TypeError && error.code === 'ERR_INVALID_ARG_TYPE');
}
}));
}
for (const artifact of ['source', 'build']) {
test(`unrecorded ${artifact}-identical ECC alias still fails closed`, () => fixture(value => {
applyInstallPlan(value.declinePlan);
const source = artifact === 'source'
? path.join(value.sourceRoot, '.opencode', 'plugins', 'ecc-hooks.ts')
: path.join(value.sourceRoot, '.opencode', 'dist', 'plugins', 'index.js');
if (artifact === 'build') fs.writeFileSync(source, 'module.exports = { eccHook: true };\n');
const content = fs.readFileSync(source);
const alias = path.join(value.targetRoot, 'plugins', 'index.js');
fs.writeFileSync(alias, content);
const before = fs.readFileSync(value.installStatePath);
assert.throws(() => applyInstallPlan(value.declinePlan), /OpenCode hook deactivation/);
const doctor = buildDoctorReport({ repoRoot: value.sourceRoot, homeDir: value.homeDir,
projectRoot: value.homeDir, targets: ['opencode'] }).results[0];
assert.ok(doctor.issues.some(issue => issue.code === 'opencode-hook-consent-violation'));
assert.strictEqual(repair(value).results[0].status, 'error');
assert.deepStrictEqual(fs.readFileSync(alias), content);
assert.deepStrictEqual(fs.readFileSync(value.installStatePath), before);
}));
}
test('an unrecorded collision at a planned ECC plugin destination still fails closed', () => fixture(value => {
fs.unlinkSync(value.installStatePath);
fs.unlinkSync(path.join(value.targetRoot, 'opencode.json'));
const destination = path.join(value.targetRoot, 'plugins', 'ecc-hooks.ts');
const content = '// user-owned file at an ECC destination\n';
fs.writeFileSync(destination, content);
assert.throws(() => applyInstallPlan(value.declinePlan), /user-owned|unverifiable/i);
assert.strictEqual(fs.readFileSync(destination, 'utf8'), content);
assert.strictEqual(fs.existsSync(value.installStatePath), false);
}));
for (const planned of [false, true]) {
test(`${planned ? 'planned ECC' : 'unrecorded user'} plugin read failures respect the attribution boundary`, () => fixture(value => {
const destination = path.join(value.targetRoot, 'plugins', planned ? 'ecc-hooks.ts' : 'index.js');
const content = planned ? fs.readFileSync(destination) : Buffer.from('// unreadable user plugin\n');
if (!planned) fs.writeFileSync(destination, content);
const originalOpen = fileSystem.openSync;
let refusedReads = 0;
fileSystem.openSync = function (candidate, ...args) {
if (typeof candidate === 'string' && path.resolve(candidate) === destination) {
refusedReads++;
throw Object.assign(new Error('Synthetic plugin read permission denied'), { code: 'EACCES' });
}
return originalOpen.call(fs, candidate, ...args);
};
try {
if (planned) assert.throws(() => applyInstallPlan(value.declinePlan), /permission denied/);
else assert.strictEqual(applyInstallPlan(value.declinePlan).applied, true);
assert.ok(refusedReads > 0, 'The permission boundary must be exercised');
} finally {
fileSystem.openSync = originalOpen;
}
assert.deepStrictEqual(fs.readFileSync(destination), content);
if (!planned) assert.ok(!readInstallState(value.installStatePath).operations.some(operation => operation.destinationPath === destination));
}));
}
test('repair preserves the primary failure and replacement lock when release also fails', () => fixture(value => {
const destination = path.join(value.targetRoot, 'opencode.json');
const lock = `${value.installStatePath}.ecc.lock`;
withWritableOpenMutation(destination, () => {
fs.writeFileSync(destination, '{"userEdit":true}');
fs.renameSync(lock, `${lock}.owned`);
fs.writeFileSync(lock, 'replacement lock');
}, () => {
const result = repair(value).results[0];
assert.strictEqual(result.status, 'error');
assert.match(result.error, /changed after preflight/i);
assert.match(result.releaseError, /changed OpenCode install lock/);
assert.strictEqual(fs.readFileSync(lock, 'utf8'), 'replacement lock');
assertPriorOwnership(value, destination);
});
}));
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
return { passed, failed };
}
if (require.main === module) process.exitCode = runTests().failed ? 1 : 0;
module.exports = { runTests };