source dump of claude code
at main 185 lines 6.2 kB view raw
1/** 2 * Shared constants for PowerShell cmdlets that execute arbitrary code. 3 * 4 * These lists are consumed by both the permission-engine validators 5 * (powershellSecurity.ts) and the UI suggestion gate (staticPrefix.ts). 6 * Keeping them here avoids duplicating the lists and prevents sync drift 7 * — add a cmdlet once, both consumers pick it up. 8 */ 9 10import { CROSS_PLATFORM_CODE_EXEC } from '../permissions/dangerousPatterns.js' 11import { COMMON_ALIASES } from './parser.js' 12 13/** 14 * Cmdlets that accept a -FilePath (or positional path) and execute the 15 * file's contents as a script. 16 */ 17export const FILEPATH_EXECUTION_CMDLETS = new Set([ 18 'invoke-command', 19 'start-job', 20 'start-threadjob', 21 'register-scheduledjob', 22]) 23 24/** 25 * Cmdlets where a scriptblock argument executes arbitrary code (not just 26 * filtering/transforming pipeline input like Where-Object). 27 */ 28export const DANGEROUS_SCRIPT_BLOCK_CMDLETS = new Set([ 29 'invoke-command', 30 'invoke-expression', 31 'start-job', 32 'start-threadjob', 33 'register-scheduledjob', 34 'register-engineevent', 35 'register-objectevent', 36 'register-wmievent', 37 'new-pssession', 38 'enter-pssession', 39]) 40 41/** 42 * Cmdlets that load and execute module/script code. `.psm1` files run 43 * their top-level body on import — same code-execution risk as iex. 44 */ 45export const MODULE_LOADING_CMDLETS = new Set([ 46 'import-module', 47 'ipmo', 48 'install-module', 49 'save-module', 50 'update-module', 51 'install-script', 52 'save-script', 53]) 54 55/** 56 * Shells and process spawners. Small, stable — add here only for cmdlets 57 * not covered by the validator lists above. 58 */ 59const SHELLS_AND_SPAWNERS = [ 60 'pwsh', 61 'powershell', 62 'cmd', 63 'bash', 64 'wsl', 65 'sh', 66 'start-process', 67 'start', 68 'add-type', 69 'new-object', 70] as const 71 72function aliasesOf(targets: ReadonlySet<string>): string[] { 73 return Object.entries(COMMON_ALIASES) 74 .filter(([, target]) => targets.has(target.toLowerCase())) 75 .map(([alias]) => alias) 76} 77 78/** 79 * Network cmdlets — wildcard rules for these enable exfil/download without 80 * prompt. No legitimate narrow prefix exists. 81 */ 82export const NETWORK_CMDLETS = new Set([ 83 'invoke-webrequest', 84 'invoke-restmethod', 85]) 86 87/** 88 * Alias/variable mutation cmdlets — Set-Alias rebinds command resolution, 89 * Set-Variable can poison $PSDefaultParameterValues. checkRuntimeStateManipulation 90 * validator in powershellSecurity.ts independently gates on the permission path. 91 */ 92export const ALIAS_HIJACK_CMDLETS = new Set([ 93 'set-alias', 94 'sal', // alias not in COMMON_ALIASES — list explicitly 95 'new-alias', 96 'nal', // alias not in COMMON_ALIASES — list explicitly 97 'set-variable', 98 'sv', // alias not in COMMON_ALIASES — list explicitly 99 'new-variable', 100 'nv', // alias not in COMMON_ALIASES — list explicitly 101]) 102 103/** 104 * WMI/CIM process spawn — Invoke-WmiMethod -Class Win32_Process -Name Create 105 * is a Start-Process equivalent that bypasses checkStartProcess. No legitimate 106 * narrow prefix exists; any invocation can spawn arbitrary processes. 107 * checkWmiProcessSpawn validator gates on the permission path. 108 * (security finding #34) 109 */ 110export const WMI_CIM_CMDLETS = new Set([ 111 'invoke-wmimethod', 112 'iwmi', // alias not in COMMON_ALIASES — list explicitly 113 'invoke-cimmethod', 114]) 115 116/** 117 * Cmdlets in CMDLET_ALLOWLIST with additionalCommandIsDangerousCallback. 118 * 119 * The allowlist auto-allows these for safe args (StringConstant identifiers). 120 * The permission dialog only fires when the callback rejected — i.e. the args 121 * contain a scriptblock, variable, subexpression, etc. Accepting a 122 * `Cmdlet:*` wildcard at that point would match ALL future invocations via 123 * prefix-startsWith, bypassing the callback forever. 124 * `ForEach-Object:*` → `ForEach-Object { Remove-Item -Recurse / }` auto-allows. 125 * 126 * Sync with readOnlyValidation.ts — test/utils/powershell/dangerousCmdlets.test.ts 127 * asserts this set covers every additionalCommandIsDangerousCallback entry. 128 */ 129export const ARG_GATED_CMDLETS = new Set([ 130 'select-object', 131 'sort-object', 132 'group-object', 133 'where-object', 134 'measure-object', 135 'write-output', 136 'write-host', 137 'start-sleep', 138 'format-table', 139 'format-list', 140 'format-wide', 141 'format-custom', 142 'out-string', 143 'out-host', 144 // Native executables with callback-gated args (e.g. ipconfig /flushdns 145 // is rejected, ipconfig /all is allowed). Same bypass risk. 146 'ipconfig', 147 'hostname', 148 'route', 149]) 150 151/** 152 * Commands to never suggest as a wildcard prefix in the permission dialog. 153 * 154 * Derived from the validator lists above plus the small static shells list. 155 * Add a cmdlet to the appropriate validator list and it automatically 156 * appears here — no separate maintenance. 157 */ 158export const NEVER_SUGGEST: ReadonlySet<string> = (() => { 159 const core = new Set<string>([ 160 ...SHELLS_AND_SPAWNERS, 161 ...FILEPATH_EXECUTION_CMDLETS, 162 ...DANGEROUS_SCRIPT_BLOCK_CMDLETS, 163 ...MODULE_LOADING_CMDLETS, 164 ...NETWORK_CMDLETS, 165 ...ALIAS_HIJACK_CMDLETS, 166 ...WMI_CIM_CMDLETS, 167 ...ARG_GATED_CMDLETS, 168 // ForEach-Object's -MemberName (positional: `% Delete`) resolves against 169 // the runtime pipeline object — `Get-ChildItem | % Delete` invokes 170 // FileInfo.Delete(). StaticParameterBinder identifies the 171 // PropertyAndMethodSet parameter set, but the set handles both; the arg 172 // is a plain StringConstantExpressionAst with no property/method signal. 173 // Pipeline type inference (upstream OutputType → GetMember) misses ETS 174 // AliasProperty members and has no answer for `$var | %` or external 175 // upstream. Not in ARG_GATED (no allowlist entry to sync with). 176 'foreach-object', 177 // Interpreters/runners — `node script.js` stops at the file arg and 178 // suggests bare `node:*`, auto-allowing arbitrary code via -e/-p. The 179 // auto-mode classifier strips these rules (isDangerousPowerShellPermission) 180 // but the suggestion gate didn't. Multi-word entries ('npm run') are 181 // filtered out — NEVER_SUGGEST is a single-name lookup on cmd.name. 182 ...CROSS_PLATFORM_CODE_EXEC.filter(p => !p.includes(' ')), 183 ]) 184 return new Set([...core, ...aliasesOf(core)]) 185})()