fix(security): enforce no-follow semantics on Windows

This commit is contained in:
haelyra
2026-08-13 17:16:53 -04:00
parent bab38ae91b
commit 3f5bf22966
2 changed files with 33 additions and 4 deletions
+31 -3
View File
@@ -17,6 +17,16 @@ function getStatePath(codexHome) {
function openRegularFileNoFollow(filePath, writable = false) {
const noFollow = fs.constants.O_NOFOLLOW || 0;
const flags = (writable ? fs.constants.O_RDWR : fs.constants.O_RDONLY) | noFollow;
let pathStat;
try {
pathStat = fs.lstatSync(filePath, { bigint: true });
} catch (error) {
if (error.code === 'ENOENT') return null;
throw error;
}
if (!pathStat.isFile() || pathStat.isSymbolicLink()) {
throw new Error(`Refusing to manage non-regular legacy sync path: ${filePath}`);
}
let descriptor;
try {
descriptor = fs.openSync(filePath, flags);
@@ -27,12 +37,30 @@ function openRegularFileNoFollow(filePath, writable = false) {
}
throw error;
}
const stat = fs.fstatSync(descriptor);
if (!stat.isFile()) {
const descriptorStat = fs.fstatSync(descriptor, { bigint: true });
let finalPathStat;
try {
finalPathStat = fs.lstatSync(filePath, { bigint: true });
} catch (error) {
fs.closeSync(descriptor);
if (error.code === 'ENOENT') {
throw new Error(`Legacy sync path changed while opening: ${filePath}`);
}
throw error;
}
if (
!descriptorStat.isFile()
|| !finalPathStat.isFile()
|| finalPathStat.isSymbolicLink()
|| descriptorStat.dev !== pathStat.dev
|| descriptorStat.ino !== pathStat.ino
|| descriptorStat.dev !== finalPathStat.dev
|| descriptorStat.ino !== finalPathStat.ino
) {
fs.closeSync(descriptor);
throw new Error(`Refusing to manage non-regular legacy sync path: ${filePath}`);
}
return { descriptor, stat };
return { descriptor, stat: fs.fstatSync(descriptor) };
}
function readRegularFileNoFollow(filePath, encoding = null) {
+2 -1
View File
@@ -194,6 +194,7 @@ function runTests() {
const createdPath = path.join(codexHome, 'prompts', 'ecc-review.md');
fs.mkdirSync(path.dirname(existingPath), { recursive: true });
fs.writeFileSync(existingPath, '# User prompt\n', { mode: 0o640 });
const originalMode = fs.statSync(existingPath).mode & 0o777;
const statePath = beginLegacySyncState({
codexHome,
@@ -215,7 +216,7 @@ function runTests() {
assert.strictEqual(result.status, 'rolled-back');
assert.strictEqual(fs.readFileSync(existingPath, 'utf8'), '# User prompt\n');
assert.strictEqual(fs.statSync(existingPath).mode & 0o777, 0o640);
assert.strictEqual(fs.statSync(existingPath).mode & 0o777, originalMode);
assert.ok(!fs.existsSync(createdPath));
assert.strictEqual(hooksValue, '/tmp/user-hooks');
assert.ok(!fs.existsSync(statePath));