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.