mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-14 20:25:26 +02:00
test(security): bind fixture reads and mutations to private descriptors
This commit is contained in:
@@ -4666,9 +4666,12 @@ function runTests() {
|
||||
}
|
||||
for (const [rel, parts] of byFile) {
|
||||
const file = path.join(trivialRoot, rel);
|
||||
if (fs.existsSync(file)) continue;
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
fs.writeFileSync(file, `${parts.join('\n')}\n`);
|
||||
try {
|
||||
fs.writeFileSync(file, `${parts.join('\n')}\n`, { flag: 'wx' });
|
||||
} catch (error) {
|
||||
if (error.code !== 'EEXIST') throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
const trivialRun = (toolName, tool_input, env = {}) => {
|
||||
|
||||
@@ -648,29 +648,38 @@ test('dry-run snapshots local-scope inventory into isolated Claude and project r
|
||||
'.claude',
|
||||
'settings.local.json'
|
||||
);
|
||||
assert.strictEqual(fs.lstatSync(shadowSettingsPath).isFile(), true);
|
||||
const shadowInstalled = JSON.parse(fs.readFileSync(
|
||||
path.join(runOptions.env.CLAUDE_CONFIG_DIR, 'plugins', 'installed_plugins.json'),
|
||||
'utf8'
|
||||
));
|
||||
assert.strictEqual(
|
||||
shadowInstalled.plugins['ecc@ecc'][0].projectPath,
|
||||
runOptions.cwd
|
||||
const shadowSettingsFd = fs.openSync(
|
||||
shadowSettingsPath,
|
||||
fs.constants.O_RDWR | (fs.constants.O_NOFOLLOW || 0)
|
||||
);
|
||||
assert.ok(
|
||||
shadowInstalled.plugins['ecc@ecc'][0].installPath
|
||||
.startsWith(runOptions.env.CLAUDE_CONFIG_DIR)
|
||||
);
|
||||
fs.writeFileSync(shadowSettingsPath, '{"providerRead":true}\n');
|
||||
fs.mkdirSync(runOptions.env.XDG_DATA_HOME, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(runOptions.env.XDG_DATA_HOME, 'claude-provider-read.json'),
|
||||
'{"providerRead":true}\n'
|
||||
);
|
||||
if (args.join(' ') === 'plugin list --json') {
|
||||
return { stdout: JSON.stringify([installedPlugin('local')]) };
|
||||
try {
|
||||
assert.strictEqual(fs.fstatSync(shadowSettingsFd).isFile(), true);
|
||||
const shadowInstalled = JSON.parse(fs.readFileSync(
|
||||
path.join(runOptions.env.CLAUDE_CONFIG_DIR, 'plugins', 'installed_plugins.json'),
|
||||
'utf8'
|
||||
));
|
||||
assert.strictEqual(
|
||||
shadowInstalled.plugins['ecc@ecc'][0].projectPath,
|
||||
runOptions.cwd
|
||||
);
|
||||
assert.ok(
|
||||
shadowInstalled.plugins['ecc@ecc'][0].installPath
|
||||
.startsWith(runOptions.env.CLAUDE_CONFIG_DIR)
|
||||
);
|
||||
fs.ftruncateSync(shadowSettingsFd, 0);
|
||||
fs.writeFileSync(shadowSettingsFd, '{"providerRead":true}\n');
|
||||
fs.mkdirSync(runOptions.env.XDG_DATA_HOME, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(runOptions.env.XDG_DATA_HOME, 'claude-provider-read.json'),
|
||||
'{"providerRead":true}\n'
|
||||
);
|
||||
if (args.join(' ') === 'plugin list --json') {
|
||||
return { stdout: JSON.stringify([installedPlugin('local')]) };
|
||||
}
|
||||
return { stdout: JSON.stringify([officialMarketplace('local')]) };
|
||||
} finally {
|
||||
fs.closeSync(shadowSettingsFd);
|
||||
}
|
||||
return { stdout: JSON.stringify([officialMarketplace('local')]) };
|
||||
};
|
||||
|
||||
const result = setupClaudePlugin(
|
||||
|
||||
@@ -903,15 +903,30 @@ function runTests() {
|
||||
assert.strictEqual(fs.readFileSync(userPath, 'utf8'), 'Keep my settings.\n');
|
||||
}
|
||||
|
||||
const modifiedContent = `${fs.readFileSync(toolPath, 'utf8')}\n// User test customisation.\n`;
|
||||
fs.writeFileSync(toolPath, modifiedContent, 'utf8');
|
||||
const toolFd = fs.openSync(toolPath, fs.constants.O_RDWR | (fs.constants.O_NOFOLLOW || 0));
|
||||
let modifiedContent;
|
||||
try {
|
||||
assert.ok(fs.fstatSync(toolFd).isFile());
|
||||
modifiedContent = `${fs.readFileSync(toolFd, 'utf8')}\n// User test customisation.\n`;
|
||||
const modifiedBytes = Buffer.from(modifiedContent);
|
||||
assert.strictEqual(fs.writeSync(toolFd, modifiedBytes, 0, modifiedBytes.length, 0), modifiedBytes.length);
|
||||
fs.ftruncateSync(toolFd, modifiedBytes.length);
|
||||
} finally {
|
||||
fs.closeSync(toolFd);
|
||||
}
|
||||
const uninstalled = uninstallInstalledStates(lifecycleOptions);
|
||||
assert.strictEqual(uninstalled.results.length, 1);
|
||||
assert.strictEqual(uninstalled.results[0].status, 'partial', JSON.stringify(uninstalled));
|
||||
assert.ok(uninstalled.results[0].removedPaths.includes(barrelPath));
|
||||
assert.ok(!fs.existsSync(barrelPath), 'both legacy and repaired managed barrels are recognised');
|
||||
assert.deepStrictEqual(uninstalled.results[0].retainedPaths, [toolPath]);
|
||||
assert.strictEqual(fs.readFileSync(toolPath, 'utf8'), modifiedContent);
|
||||
const retainedFd = fs.openSync(toolPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
||||
try {
|
||||
assert.ok(fs.fstatSync(retainedFd).isFile());
|
||||
assert.strictEqual(fs.readFileSync(retainedFd, 'utf8'), modifiedContent);
|
||||
} finally {
|
||||
fs.closeSync(retainedFd);
|
||||
}
|
||||
assert.strictEqual(fs.readFileSync(userPath, 'utf8'), 'Keep my settings.\n');
|
||||
assert.ok(fs.existsSync(plan.installStatePath), 'modified managed content retains its ownership record');
|
||||
}
|
||||
|
||||
@@ -185,8 +185,8 @@ for (const phase of ['during hashing', 'after hashing']) {
|
||||
const edited = 'user edit bytes\n';
|
||||
assert.strictEqual(original.length, edited.length);
|
||||
writeFile(file, original);
|
||||
// Bind the fixture identity to its opened private file, keeping the later
|
||||
// pathname read an assertion rather than relying on a prior stat check.
|
||||
// Bind the fixture identity to its opened private file. Validate the
|
||||
// retained pathname through a fresh descriptor after the injected race.
|
||||
const identityFd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
||||
let identity;
|
||||
try { identity = fs.fstatSync(identityFd, { bigint: true }); }
|
||||
@@ -224,7 +224,15 @@ for (const phase of ['during hashing', 'after hashing']) {
|
||||
fs.fstatSync = originalFstat;
|
||||
}
|
||||
assert.ok(mutationApplied, 'The equal-length mutation must actually execute');
|
||||
assert.strictEqual(fs.readFileSync(file, 'utf8'), edited);
|
||||
const retainedFd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
||||
try {
|
||||
const retainedIdentity = fs.fstatSync(retainedFd, { bigint: true });
|
||||
assert.ok(retainedIdentity.isFile());
|
||||
assert.ok(matches(retainedIdentity), 'The mutated fixture inode must remain at the retained path');
|
||||
assert.strictEqual(fs.readFileSync(retainedFd, 'utf8'), edited);
|
||||
} finally {
|
||||
fs.closeSync(retainedFd);
|
||||
}
|
||||
assert.deepStrictEqual(result.removedPaths, []);
|
||||
assert.strictEqual(result.warnings.length, 1);
|
||||
assert.match(result.warnings[0], /changed while being verified/);
|
||||
|
||||
Reference in New Issue
Block a user