fix(claude-extension): enforce private permissions for settings writes (#1378)

This commit is contained in:
Kai (Tam Nhu) Tran
2026-05-23 21:45:07 -04:00
committed by GitHub
parent 210ab761e2
commit 0db9705d75
2 changed files with 14 additions and 2 deletions
@@ -231,8 +231,12 @@ function writeJsonDocument(
const tempPath = `${filePath}.tmp.${uniqueFileNonce()}`;
try {
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2) + '\n', 'utf8');
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2) + '\n', {
encoding: 'utf8',
mode: 0o600,
});
fs.renameSync(tempPath, filePath);
fs.chmodSync(filePath, 0o600);
} catch (error) {
if (fs.existsSync(tempPath)) {
fs.rmSync(tempPath, { force: true });
@@ -313,6 +313,12 @@ describe('web-server claude-extension-routes', () => {
it('creates a binding and applies managed settings to shared + IDE targets', async () => {
const ideSettingsPath = path.join(tempHome, 'ide', 'vscode', 'settings.json');
const sharedSettingsPath = path.join(tempHome, '.claude', 'settings.json');
fs.mkdirSync(path.dirname(ideSettingsPath), { recursive: true });
fs.mkdirSync(path.dirname(sharedSettingsPath), { recursive: true });
fs.writeFileSync(sharedSettingsPath, JSON.stringify({ env: {} }, null, 2) + '\n', { mode: 0o600 });
fs.writeFileSync(ideSettingsPath, JSON.stringify({}, null, 2) + '\n', { mode: 0o600 });
const createResponse = await fetch(`${baseUrl}/api/claude-extension/bindings`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -352,7 +358,6 @@ describe('web-server claude-extension-routes', () => {
expect(applied.sharedSettings.state).toBe('applied');
expect(applied.ideSettings.state).toBe('applied');
const sharedSettingsPath = path.join(tempHome, '.claude', 'settings.json');
const sharedSettings = JSON.parse(fs.readFileSync(sharedSettingsPath, 'utf8')) as {
env?: Record<string, string>;
};
@@ -370,6 +375,9 @@ describe('web-server claude-extension-routes', () => {
)
).toBe(true);
expect(ideSettings['claudeCode.disableLoginPrompt']).toBe(true);
expect(fs.statSync(sharedSettingsPath).mode & 0o777).toBe(0o600);
expect(fs.statSync(ideSettingsPath).mode & 0o777).toBe(0o600);
});
it('resets only managed keys and preserves unrelated shared + IDE settings', async () => {