utils.ts
1 import { 2 DANGEROUS_SHELL_SETTINGS, 3 SAFE_ENV_VARS, 4 } from '../../utils/managedEnvConstants.js' 5 import type { SettingsJson } from '../../utils/settings/types.js' 6 import { jsonStringify } from '../../utils/slowOperations.js' 7 8 type DangerousShellSetting = (typeof DANGEROUS_SHELL_SETTINGS)[number] 9 10 export type DangerousSettings = { 11 shellSettings: Partial<Record<DangerousShellSetting, string>> 12 envVars: Record<string, string> 13 hasHooks: boolean 14 hooks?: unknown 15 } 16 17 /** 18 * Extract dangerous settings from a settings object. 19 * 20 * Dangerous env vars are determined by checking against SAFE_ENV_VARS - 21 * any env var NOT in SAFE_ENV_VARS is considered dangerous. 22 * See managedEnv.ts for the authoritative list and threat categories. 23 */ 24 export function extractDangerousSettings( 25 settings: SettingsJson | null | undefined, 26 ): DangerousSettings { 27 if (!settings) { 28 return { 29 shellSettings: {}, 30 envVars: {}, 31 hasHooks: false, 32 } 33 } 34 35 // Extract dangerous shell settings 36 const shellSettings: Partial<Record<DangerousShellSetting, string>> = {} 37 for (const key of DANGEROUS_SHELL_SETTINGS) { 38 const value = settings[key] 39 if (typeof value === 'string' && value.length > 0) { 40 shellSettings[key] = value 41 } 42 } 43 44 // Extract dangerous env vars - any var NOT in SAFE_ENV_VARS is dangerous 45 const envVars: Record<string, string> = {} 46 if (settings.env && typeof settings.env === 'object') { 47 for (const [key, value] of Object.entries(settings.env)) { 48 if (typeof value === 'string' && value.length > 0) { 49 // Check if this env var is NOT in the safe list 50 if (!SAFE_ENV_VARS.has(key.toUpperCase())) { 51 envVars[key] = value 52 } 53 } 54 } 55 } 56 57 // Check for hooks 58 const hasHooks = 59 settings.hooks !== undefined && 60 settings.hooks !== null && 61 typeof settings.hooks === 'object' && 62 Object.keys(settings.hooks).length > 0 63 64 return { 65 shellSettings, 66 envVars, 67 hasHooks, 68 hooks: hasHooks ? settings.hooks : undefined, 69 } 70 } 71 72 /** 73 * Check if settings contain any dangerous settings 74 */ 75 export function hasDangerousSettings(dangerous: DangerousSettings): boolean { 76 return ( 77 Object.keys(dangerous.shellSettings).length > 0 || 78 Object.keys(dangerous.envVars).length > 0 || 79 dangerous.hasHooks 80 ) 81 } 82 83 /** 84 * Compare two sets of dangerous settings to see if the new settings 85 * have changed or added dangerous settings compared to the old settings 86 */ 87 export function hasDangerousSettingsChanged( 88 oldSettings: SettingsJson | null | undefined, 89 newSettings: SettingsJson | null | undefined, 90 ): boolean { 91 const oldDangerous = extractDangerousSettings(oldSettings) 92 const newDangerous = extractDangerousSettings(newSettings) 93 94 // If new settings don't have any dangerous settings, no prompt needed 95 if (!hasDangerousSettings(newDangerous)) { 96 return false 97 } 98 99 // If old settings didn't have dangerous settings but new does, prompt needed 100 if (!hasDangerousSettings(oldDangerous)) { 101 return true 102 } 103 104 // Compare the dangerous settings - any change triggers a prompt 105 const oldJson = jsonStringify({ 106 shellSettings: oldDangerous.shellSettings, 107 envVars: oldDangerous.envVars, 108 hooks: oldDangerous.hooks, 109 }) 110 const newJson = jsonStringify({ 111 shellSettings: newDangerous.shellSettings, 112 envVars: newDangerous.envVars, 113 hooks: newDangerous.hooks, 114 }) 115 116 return oldJson !== newJson 117 } 118 119 /** 120 * Format dangerous settings as a human-readable list for the UI 121 * Only returns setting names, not values 122 */ 123 export function formatDangerousSettingsList( 124 dangerous: DangerousSettings, 125 ): string[] { 126 const items: string[] = [] 127 128 // Shell settings (names only) 129 for (const key of Object.keys(dangerous.shellSettings)) { 130 items.push(key) 131 } 132 133 // Env vars (names only) 134 for (const key of Object.keys(dangerous.envVars)) { 135 items.push(key) 136 } 137 138 // Hooks 139 if (dangerous.hasHooks) { 140 items.push('hooks') 141 } 142 143 return items 144 }