From 80174cbec862319256e2b99705f4d7809797cc03 Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Thu, 9 Jul 2026 00:42:05 +0000 Subject: [PATCH] [Security] Harden LocalStorage file permissions Hardens the LocalStorage class by enforcing 0600 (owner-only) file permissions on the configuration file. This ensures that sensitive data like tokens or preferences are only readable by the current user. Modified: - packages/cli-kit/src/public/node/local-storage.ts: Added ensureRestrictedPermissions method and calls in constructor and mutation methods. --- .../cli-kit/src/public/node/local-storage.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/cli-kit/src/public/node/local-storage.ts b/packages/cli-kit/src/public/node/local-storage.ts index 9759267aab9..4bd48480585 100644 --- a/packages/cli-kit/src/public/node/local-storage.ts +++ b/packages/cli-kit/src/public/node/local-storage.ts @@ -3,6 +3,7 @@ import {fileHasWritePermissions, unixFileIsOwnedByCurrentUser} from './fs.js' import {dirname} from './path.js' import {TokenItem} from './ui.js' import Config from 'conf' +import {chmodSync} from 'fs' /** * A wrapper around the `conf` package that provides a strongly-typed interface @@ -14,6 +15,7 @@ export class LocalStorage> { constructor(options: {projectName?: string; cwd?: string}) { this.config = new Config(options) + this.ensureRestrictedPermissions() } /** @@ -44,6 +46,7 @@ export class LocalStorage> { set(key: TKey, value?: T[TKey]): void { try { this.config.set(key, value) + this.ensureRestrictedPermissions() // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { this.handleError(error, 'set') @@ -60,6 +63,7 @@ export class LocalStorage> { delete(key: TKey): void { try { this.config.delete(key) + this.ensureRestrictedPermissions() // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { this.handleError(error, 'delete') @@ -75,12 +79,27 @@ export class LocalStorage> { clear(): void { try { this.config.clear() + this.ensureRestrictedPermissions() // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { this.handleError(error, 'clear') } } + /** + * Ensures the configuration file has restricted permissions (0600), + * so it's only readable/writable by the current user. + * This is important because the configuration file may contain sensitive information. + */ + private ensureRestrictedPermissions() { + try { + chmodSync(this.config.path, 0o600) + // eslint-disable-next-line no-catch-all/no-catch-all + } catch { + // If we can't change permissions (e.g. on Windows or file doesn't exist), we ignore the error + } + } + /** * Handle errors from config operations. * If the error is permission-related, throw an AbortError with helpful hints.