mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-06 06:23:18 +00:00
fix(codex-auth): restrict symlink fallback and harden copy path (#1365)
This commit is contained in:
@@ -94,7 +94,7 @@ export function ensureSharedConfigSymlink(
|
|||||||
target: targetPath,
|
target: targetPath,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
copySharedConfigFallback(targetPath, linkPath, err);
|
handleSymlinkFailure(targetPath, linkPath, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,9 +106,30 @@ function isRegularConfigCopyUnmodified(linkPath: string, targetPath: string): bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function copySharedConfigFallback(targetPath: string, linkPath: string, err: unknown): void {
|
function handleSymlinkFailure(targetPath: string, linkPath: string, err: unknown): void {
|
||||||
fs.copyFileSync(targetPath, linkPath);
|
const code = (err as NodeJS.ErrnoException | null)?.code;
|
||||||
fs.chmodSync(linkPath, 0o600);
|
if (code !== 'EPERM' && code !== 'EACCES' && code !== 'ENOSYS') {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmpPath = path.join(
|
||||||
|
path.dirname(linkPath),
|
||||||
|
`.config.toml.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.copyFileSync(targetPath, tmpPath, fs.constants.COPYFILE_EXCL);
|
||||||
|
fs.chmodSync(tmpPath, 0o600);
|
||||||
|
fs.renameSync(tmpPath, linkPath);
|
||||||
|
} catch (copyErr) {
|
||||||
|
try {
|
||||||
|
fs.rmSync(tmpPath, { force: true });
|
||||||
|
} catch {
|
||||||
|
// best effort cleanup
|
||||||
|
}
|
||||||
|
throw copyErr;
|
||||||
|
}
|
||||||
|
|
||||||
process.stderr.write(
|
process.stderr.write(
|
||||||
`[!] codex-auth: symlink unavailable; copied shared config.toml to ${linkPath}. ` +
|
`[!] codex-auth: symlink unavailable; copied shared config.toml to ${linkPath}. ` +
|
||||||
`Config edits won't propagate automatically.\n`
|
`Config edits won't propagate automatically.\n`
|
||||||
|
|||||||
@@ -143,6 +143,19 @@ describe('ensureSharedConfigSymlink', () => {
|
|||||||
expect(stderrChunks.join('')).toContain('symlink unavailable');
|
expect(stderrChunks.join('')).toContain('symlink unavailable');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('rethrows symlink errors that are not fallback-safe', () => {
|
||||||
|
fs.writeFileSync(sharedConfigPath, 'model = "gpt-5.5"\n', { mode: 0o600 });
|
||||||
|
const symlinkSpy = spyOn(fs, 'symlinkSync').mockImplementation(() => {
|
||||||
|
throw Object.assign(new Error('simulated race'), { code: 'EEXIST' });
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(() => ensureSharedConfigSymlink(profileDir, sharedConfigPath)).toThrow(/simulated race/);
|
||||||
|
} finally {
|
||||||
|
symlinkSpy.mockRestore();
|
||||||
|
}
|
||||||
|
});
|
||||||
it('preserves edited fallback copies on later repair attempts', () => {
|
it('preserves edited fallback copies on later repair attempts', () => {
|
||||||
fs.writeFileSync(sharedConfigPath, 'model = "gpt-5.5"\n', { mode: 0o600 });
|
fs.writeFileSync(sharedConfigPath, 'model = "gpt-5.5"\n', { mode: 0o600 });
|
||||||
const linkPath = path.join(profileDir, 'config.toml');
|
const linkPath = path.join(profileDir, 'config.toml');
|
||||||
|
|||||||
Reference in New Issue
Block a user