Merge pull request #1434 from kaitranntt/codex/fix-unbounded-recursion-in-receipt-generation

fix(persist): bound Codex translator receipt scan
This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-05-30 15:46:29 -04:00
commit 93756489a5
3 files changed
+104 -15

No files matched your search

@@ -415,6 +415,43 @@ describe('persist command Claude extension parity', () => {
expect(renderedLogs).toContain('Native Codex target: ccsxp or ccs codex --target codex');
});
it('does not fail after writing settings when a cleared env value is deeply nested', async () => {
await writeUnifiedConfig();
const settingsPath = path.join(tempRoot, '.claude', 'settings.json');
await fs.promises.mkdir(path.dirname(settingsPath), { recursive: true });
const deepValue = `${'{"nested":'.repeat(20000)}"leaf"${'}'.repeat(20000)}`;
await fs.promises.writeFile(
settingsPath,
`{"env":{"KEEP_ME":"still-here","ANTHROPIC_AUTH_TOKEN":${deepValue}}}\n`,
'utf8'
);
const originalConsoleLog = console.log;
const capturedLogs: string[] = [];
console.log = (...args: unknown[]) => {
capturedLogs.push(args.map((arg) => String(arg)).join(' '));
};
try {
await withScopedHome(() => handlePersistCommand(['default', '--yes']));
} finally {
console.log = originalConsoleLog;
}
const persisted = JSON.parse(await fs.promises.readFile(settingsPath, 'utf8')) as {
env: Record<string, string>;
};
expect(persisted.env.KEEP_ME).toBe('still-here');
expect(persisted.env.ANTHROPIC_AUTH_TOKEN).toBeUndefined();
const renderedLogs = capturedLogs.join('\n');
expect(renderedLogs).toContain("Profile 'default' written to");
expect(renderedLogs).toContain('Config Receipt');
expect(renderedLogs).toContain('Codex translator URL: not found');
expect(renderedLogs).not.toContain('Failed to write settings');
});
it('warns in the persist receipt when a Codex translator URL remains in settings', async () => {
await writeUnifiedConfig();
@@ -31,4 +31,14 @@ describe('stale Codex translator settings scanner', () => {
'["custom-key"][0]',
]);
});
it('does not overflow the stack on deeply nested local settings values', () => {
let settings: Record<string, unknown> = { value: 'leaf' };
for (let depth = 0; depth < 20000; depth += 1) {
settings = { nested: settings };
}
expect(() => findCodexTranslatorUrlPaths(settings)).not.toThrow();
expect(findCodexTranslatorUrlPaths(settings)).toEqual([]);
});
});