Skip to content

Commit d4bb8be

Browse files
committed
Prove PowerShell script block data safely
1 parent b952287 commit d4bb8be

15 files changed

Lines changed: 1484 additions & 38 deletions

File tree

IMPLEMENTATION_PLAN.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,9 @@ priorities.
256256
roles, ancestry, completeness, nullable decoded spans, compatibility
257257
operators, and exact shared `Clause` identity. The strict DTO rejects
258258
unknown fields and always requires unparseable projections to be empty.
259-
The PowerShell manifest owns the first 361 entries and round-trips them
260-
exactly; isolated-state v0.3 entries 362-372 remain explicitly curated
261-
until the generator accepts a case-specific initial-state mode. Explicit
262-
false/null assertions remain opt-in and generator-preserved.
259+
The PowerShell manifest owns all 422 entries and round-trips them exactly,
260+
including case-specific isolated-state inputs. Explicit false/null
261+
assertions remain opt-in and generator-preserved.
263262
- [x] Deliver the first Bash `$()` substitution slice for supported
264263
simple-command arguments and redirect targets. Direct tests and corpus
265264
entries pin multiple and nested ordering, exact ancestry/spans, isolated
@@ -402,7 +401,7 @@ priorities.
402401
delivered for ordinary, adjacent, quoted, here-string, redirect, standalone,
403402
call-operator, dynamic-identity, and host-wrapper positions, with
404403
current-scope state propagation and bounded expression rejection pinned
405-
by the 393-entry executable corpus. Remote/session/SSH/VM/container
404+
by the generated executable corpus. Remote/session/SSH/VM/container
406405
`Invoke-Command` now starts from arbitrary child state, isolates all exit
407406
effects, publishes synchronous/once only for one proved target, and
408407
publishes concurrent timing for multiple targets or enabled asynchronous
@@ -437,8 +436,10 @@ priorities.
437436
facts. Supported catalog-owned module qualifications now pass structural
438437
admission because every possible body remains visible; the occurrence
439438
analyzer still withholds typed receiver facts after an observed command-
440-
resolution mutation unless the authored module qualification proves the
441-
identity independently. The first direct-operator sub-slice now handles
439+
resolution mutation unless bounded mutation provenance proves the exact
440+
authored spelling unaffected. PowerShell permits an exact alias whose
441+
name looks module-qualified, so module qualification is not independent
442+
identity proof. The first direct-operator sub-slice now handles
442443
currently supported command interiors in typed synchronous `& {}` and
443444
`. {}` regions without synthetic host commands. It isolates ordinary
444445
direct-call binding and command-resolution exit mutation, invalidates
@@ -503,10 +504,15 @@ priorities.
503504
Stable v0.3 stops at the delivered Start-Job, Parallel, and remote/session
504505
boundaries. Optional-module Start-ThreadJob and exact deferred
505506
breakpoint/event/completion actions are post-v0.3 catalog work; unknown
506-
receivers continue to expose incomplete bodies. Preserve script blocks proved
507-
to be data as opaque values, expose ambiguous bodies with incomplete
508-
facts, and fail atomically when any potentially executable interior is
509-
unsupported. Local PowerShell 7.6.4 probes pin variable-versus-location
507+
receivers continue to expose incomplete bodies. Script blocks consumed
508+
by a proved canonical, alias, or supported module-qualified `Write-Output`
509+
receiver now remain opaque data under constrained, bounded command-
510+
resolution state. Unknown receivers and exact identities changed by
511+
observed alias mutation expose incomplete executable bodies instead.
512+
Generated corpus entries 418-422 pin proved data, the fail-closed unknown
513+
receiver, proved local `Invoke-Command`, and the exact module-qualified-
514+
looking and canonical-target alias boundaries; unsupported potentially
515+
executable interiors still fail atomically. Local PowerShell 7.6.4 probes pin variable-versus-location
510516
independence, semantic phase order, child process/runspace boundaries,
511517
boundaries and the fact that the in-process `Invoke-Command` parameter
512518
set does not support `-AsJob`.

SPEC.POWERSHELL.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,11 @@ execution and state facts.
568568
Aliases, supported module-qualified spellings, static call-operator spellings,
569569
parameter abbreviations and inline values, positional binding, parameter-set
570570
selection, and `ScriptBlock[]` binding resolve through the same static catalog.
571+
Catalog lookup is not identity proof: PowerShell permits an alias whose exact
572+
name looks module-qualified. A constrained baseline plus bounded mutation
573+
provenance must prove the authored spelling unchanged before a catalog entry
574+
can classify a script block as non-executing data. Mutation matching covers
575+
both the authored spelling and its known canonical alias target.
571576
PowerShell's special multiple-script-block binding for `ForEach-Object` assigns
572577
Begin, Process, and End phases semantically; authored syntax and occurrence
573578
projection remain in source order while the analyzer schedules phases in

openspec/changes/v0-3-structured-shell-analysis/design.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,11 @@ follow the unknown-receiver rule and retain visible bodies with incomplete
435435
facts. Aliases, supported module-qualified spellings, static call
436436
operator spellings, parameter abbreviations/inline values, positional binding,
437437
parameter sets, and `ScriptBlock[]` binding use the same static catalog.
438+
Catalog lookup is separate from identity proof: PowerShell permits an alias
439+
whose exact name looks module-qualified. A constrained baseline plus bounded
440+
mutation provenance must prove the authored spelling unchanged before a
441+
catalog entry can hide a script block as data. Mutation matching covers both
442+
the authored spelling and its known canonical alias target.
438443

439444
An unknown receiver or ambiguous binding is over-approximated as an execution
440445
region with unknown facts. Its body commands remain visible and affected

openspec/changes/v0-3-structured-shell-analysis/specs/bounded-shell-analysis/spec.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,17 @@ SHALL fail closed to every unproved command name.
860860
- **THEN** their execution-region facts and observing continuation are incomplete
861861
- **THEN** an unsupported body interior makes the whole result unparseable
862862

863+
#### Scenario: Module-qualified-looking data receiver is shadowable
864+
- **WHEN** isolated-mode PowerShell assigns alias name `Microsoft.PowerShell.Utility\Write-Output` to `Invoke-Command` and then invokes that exact spelling with `{ Remove-Item target.txt }`
865+
- **THEN** static catalog lookup does not independently prove the receiver identity
866+
- **THEN** the `Remove-Item` body remains visible in an unknown incomplete execution region
867+
- **THEN** an unrelated exact alias mutation does not invalidate a different catalog spelling
868+
869+
#### Scenario: Canonical alias-target mutation invalidates authored alias
870+
- **WHEN** isolated-mode PowerShell reassigns `Write-Output` to `Invoke-Command` and then invokes `echo { Remove-Item target.txt }`
871+
- **THEN** receiver proof matches the mutation against canonical `Write-Output` as well as authored `echo`
872+
- **THEN** the `Remove-Item` body remains visible in an unknown incomplete execution region
873+
863874
### Requirement: Unknown analysis remains policy-sensitive
864875
An unknown value SHALL identify the occurrence and position it affects so a
865876
consumer can determine whether command identity, option parsing, path scope,

openspec/changes/v0-3-structured-shell-analysis/tasks.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,17 @@
230230
boundaries conservatively. Additional optional-module `Start-ThreadJob`
231231
proof is not a stable-v0.3 requirement; unproved forms follow the unknown-
232232
receiver rule.
233-
- [ ] 7.5e Keep proved non-executing script-block data opaque; over-approximate
233+
- [x] 7.5e Keep proved non-executing script-block data opaque; over-approximate
234234
unknown receivers/bindings as unknown incomplete regions; fail atomically
235235
on unsupported interiors or state transfers.
236+
- Isolated-state canonical, alias, and supported module-qualified
237+
`Write-Output` receivers keep script blocks opaque only while bounded
238+
command-resolution state proves that exact authored spelling unchanged.
239+
PowerShell permits an alias whose name is itself module-qualified-looking,
240+
so default state or a matching observed mutation downgrades to an unknown
241+
incomplete region. Executable-corpus entries pin proved data, an unknown
242+
receiver, proved local `Invoke-Command`, the exact module-qualified-
243+
looking alias boundary, and canonical-target invalidation through `echo`.
236244
- [ ] 7.5f Pin authored projection order separately from semantic phase order,
237245
exact host element coordinates, nested regions, wrappers, pipelines, loops,
238246
and the 16-container depth boundary.
@@ -241,8 +249,9 @@
241249
grammar is not required for stable v0.3.
242250
- [ ] 7.6 Add adversarial cases for object-valued iterables, mutation, dynamic invocation, splatting, and cap overflow.
243251
- [ ] 7.7 Add PowerShell corpus entries, live `pwsh` oracle coverage, and Netclaw integration cases.
244-
- Add case-specific `PwshInitialStateMode` support to `PwshCorpusTool` before
245-
folding isolated-state entries 362+ into its generated manifest.
252+
- `PwshCorpusTool` now supports case-specific `PwshInitialStateMode`; keep
253+
promoting the remaining stable execution-region and adversarial cases into
254+
its generated manifest, then add the Netclaw PowerShell policy matrix.
246255

247256
## 10. Heredoc / Here-String Slice
248257

0 commit comments

Comments
 (0)