(argv: string[])
| 1261 | * into SimpleCommand.envVars so no env-var stripping here. |
| 1262 | */ |
| 1263 | export function stripWrappersFromArgv(argv: string[]): string[] { |
| 1264 | let a = argv |
| 1265 | for (;;) { |
| 1266 | if (a[0] === 'time' || a[0] === 'nohup') { |
| 1267 | a = a.slice(a[1] === '--' ? 2 : 1) |
| 1268 | } else if (a[0] === 'timeout') { |
| 1269 | const i = skipTimeoutFlags(a) |
| 1270 | // SECURITY (PR #21503 round 3): unrecognized duration (`.5`, `+5`, |
| 1271 | // `inf` — strtod formats GNU timeout accepts) → return a unchanged. |
| 1272 | // Safe because checkSemantics (ast.ts) fails CLOSED on the same input |
| 1273 | // and runs first in bashToolHasPermission, so we never reach here. |
| 1274 | if (i < 0 || !a[i] || !/^\d+(?:\.\d+)?[smhd]?$/.test(a[i]!)) return a |
| 1275 | a = a.slice(i + 1) |
| 1276 | } else if (a[0] === 'nice') { |
| 1277 | // SECURITY (PR #21503 round 3): mirror checkSemantics — handle bare |
| 1278 | // `nice cmd` and legacy `nice -N cmd`, not just `nice -n N cmd`. |
| 1279 | // Previously only `-n N` was stripped: `nice rm /outside` → |
| 1280 | // baseCmd='nice' → passthrough → /outside never path-validated. |
| 1281 | if (a[1] === '-n' && a[2] && /^-?\d+$/.test(a[2])) |
| 1282 | a = a.slice(a[3] === '--' ? 4 : 3) |
| 1283 | else if (a[1] && /^-\d+$/.test(a[1])) a = a.slice(a[2] === '--' ? 3 : 2) |
| 1284 | else a = a.slice(a[1] === '--' ? 2 : 1) |
| 1285 | } else if (a[0] === 'stdbuf') { |
| 1286 | // SECURITY (PR #21503 round 3): PR-WIDENED. Pre-PR, `stdbuf -o0 -eL rm` |
| 1287 | // was rejected by fragment check (old checkSemantics slice(2) left |
| 1288 | // name='-eL'). Post-PR, checkSemantics strips both flags → name='rm' |
| 1289 | // → passes. But stripWrappersFromArgv returned unchanged → |
| 1290 | // baseCmd='stdbuf' → not in SUPPORTED_PATH_COMMANDS → passthrough. |
| 1291 | const i = skipStdbufFlags(a) |
| 1292 | if (i < 0) return a |
| 1293 | a = a.slice(i) |
| 1294 | } else if (a[0] === 'env') { |
| 1295 | // Same asymmetry: checkSemantics strips env, we didn't. |
| 1296 | const i = skipEnvFlags(a) |
| 1297 | if (i < 0) return a |
| 1298 | a = a.slice(i) |
| 1299 | } else { |
| 1300 | return a |
| 1301 | } |
| 1302 | } |
| 1303 | } |
| 1304 |
no test coverage detected