* Checks for New-Object -ComObject. COM objects like WScript.Shell, * Shell.Application, MMC20.Application, Schedule.Service, Msxml2.XMLHTTP * have their own execution/download capabilities — no IEX required. * * We can't enumerate all dangerous ProgIDs, so flag any -ComObject. Object * creatio
( parsed: ParsedPowerShellCommand, )
| 341 | * (.Run(), .Exec()) is separately caught by checkMemberInvocations. |
| 342 | */ |
| 343 | function checkComObject( |
| 344 | parsed: ParsedPowerShellCommand, |
| 345 | ): PowerShellSecurityResult { |
| 346 | for (const cmd of getAllCommands(parsed)) { |
| 347 | if (cmd.name.toLowerCase() !== 'new-object') { |
| 348 | continue |
| 349 | } |
| 350 | // -ComObject min abbrev is -com (New-Object params: -TypeName, -ComObject, |
| 351 | // -ArgumentList, -Property, -Strict; -co is ambiguous in PS5.1 due to |
| 352 | // common params like -Confirm, so use -com). |
| 353 | if (psExeHasParamAbbreviation(cmd, '-comobject', '-com')) { |
| 354 | return { |
| 355 | behavior: 'ask', |
| 356 | message: |
| 357 | 'Command instantiates a COM object which may have execution capabilities', |
| 358 | } |
| 359 | } |
| 360 | // SECURITY: checkTypeLiterals only sees [bracket] syntax from |
| 361 | // parsed.typeLiterals. `New-Object System.Net.WebClient` passes the type |
| 362 | // as a STRING ARG (StringConstantExpressionAst), not a TypeExpressionAst, |
| 363 | // so CLM never fires. Extract -TypeName (named, colon-bound, or |
| 364 | // positional-0) and run through isClmAllowedType. Closes attackVectors D4. |
| 365 | let typeName: string | undefined |
| 366 | for (let i = 0; i < cmd.args.length; i++) { |
| 367 | const a = cmd.args[i]! |
| 368 | const lower = a.toLowerCase() |
| 369 | // -TypeName abbrev: -t is unambiguous (no other New-Object -t* params). |
| 370 | // Handle colon-bound form first: -TypeName:Foo.Bar |
| 371 | if (lower.startsWith('-t') && lower.includes(':')) { |
| 372 | const colonIdx = a.indexOf(':') |
| 373 | const paramPart = lower.slice(0, colonIdx) |
| 374 | if ('-typename'.startsWith(paramPart)) { |
| 375 | typeName = a.slice(colonIdx + 1) |
| 376 | break |
| 377 | } |
| 378 | } |
| 379 | // Space-separated form: -TypeName Foo.Bar |
| 380 | if ( |
| 381 | lower.startsWith('-t') && |
| 382 | '-typename'.startsWith(lower) && |
| 383 | cmd.args[i + 1] !== undefined |
| 384 | ) { |
| 385 | typeName = cmd.args[i + 1] |
| 386 | break |
| 387 | } |
| 388 | } |
| 389 | // Positional-0 binds to -TypeName (NetParameterSet default). Named params |
| 390 | // (-Strict, -ArgumentList, -Property, -ComObject) may appear before the |
| 391 | // positional TypeName, so scan past them to find the first non-consumed arg. |
| 392 | if (typeName === undefined) { |
| 393 | // New-Object named params that consume a following value argument |
| 394 | const VALUE_PARAMS = new Set(['-argumentlist', '-comobject', '-property']) |
| 395 | // Switch params (no value argument) |
| 396 | const SWITCH_PARAMS = new Set(['-strict']) |
| 397 | for (let i = 0; i < cmd.args.length; i++) { |
| 398 | const a = cmd.args[i]! |
| 399 | if (a.startsWith('-')) { |
| 400 | const lower = a.toLowerCase() |
nothing calls this directly
no test coverage detected