The branch deletion that followed the merge of #116 removed
ai/release-5.0.0-rc7, the base branch of #117, and GitHub closed#117
unmerged instead of retargeting it (events base_ref_deleted and closed,
three seconds after the merge). Nothing is lost: ai/quality-gate-coverage
is intact at f11ff41, and a simulated merge of the rest of the stack is
conflict-free.
Correct the deployment notes, which relied on a retarget, and record the
order: retarget, merge, and delete head branches last. Update the focus,
the next steps, and the progress for the merged #116 and the draft #119.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The built-in security-review agent (the custom security-reviewer still can't
start: its model isn't offered, and it wasn't overridden) read the branch and
found no exploitable vulnerability in the module changes. It reported two LOW
items that are not changed and are left for the maintainer: the swallowed
initialization exceptions of GetEffectiveAccess (older than the fixes and not
reproducible on any machine of the matrix) and the ACL of the stage folders
under C:\ in the lab kit. The record, the Memory Bank, and the next steps say
so, and the record says how the decision of "this computer" was tested.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The four ConvertTo-SecureString -AsPlainText calls of Probe-EffectiveAccess.ps1
and Run-MatrixLocalSuite.ps1 get a SuppressMessageAttribute with a
justification, as the controller already has for the same case: the lab
installation password comes from the AutomatedLab lab definition and the
passwords of the probe users are random and exist only in memory.
The header of Test-MatrixCleanup.ps1 says that Repair matches the prefixes of
the kit in the whole domain and on the whole machine, which the security review
pointed out, and that an unresolved S-1-5-21-* member of Performance Log Users
can be a real principal of a trust that is down.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Run-MatrixSequence.ps1 -Version 5.0.0-rc6 ran once on OSFile19 in
Windows PowerShell as a dry run. The controller used the published
module, the validation reported LIVE_RESULT_NOT_ACCEPTED as it must
(151 passed, 78 failed, 2 skipped: the live tests that rc6 predates), and
the cleanup verdict was CLEAN. It tests the mechanics and accepts nothing.
The deployment notes say how to pass the file servers and when to start
the evaluation client.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The second follow-up review (the first-lab run and the cleanup changes)
found no Blocker and no Major, and four Minors that are corrected. Both
labs are clean and the six VMs of the matrix run.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The cached-name form of a log-group entry and the profile of an unloaded
user did; a bare SID and a loaded profile did not. Every unresolved
S-1-5-21-* member of Performance Log Users counts as the probe's.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The second follow-up review found no Blocker and no Major, and four
Minors that are corrected. Test-MatrixCleanup.ps1 and the README say
that every unresolved S-1-5-21-* member of Performance Log Users counts
as an entry of the probe; a Verify with the new script found none on
the two machines of the first lab. The record says that the replay
shows that the module doesn't decide the outcome and that six runs can't
rule out a small effect, which the result bullet and the summary of the
evidence had left out; it lists the first-lab counts among its tables,
names the controller blob of rc7d, says that the first-lab check ran
before the profile and log-group fields existed, counts nine restarts
inside the series, and mentions the first attempt of the cleanup test
that died. The controller comment says "for the baseline and for the
final candidate alike" instead of "whichever version"; no code changed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The final candidate passed the live controller in the first lab, where
case 9 runs (245 passed, 0 failed, 1 skipped per edition). The follow-up
review found no Blocker and no Major, and five Minors that are corrected.
One lifetime that fits both tests is 9.95 to 10.25 minutes. net.exe lists
a local user by its bare name and deleting a local user removes its group
entries, which the cleanup check now accounts for.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The follow-up review found no Blocker and no Major, and five Minors, all
verified against the raw logs and all corrected: rc7e's first cell was
4.6 minutes after the removal of rc7d, which created and removed
accounts without a test (the timeline now includes rc7d); one lifetime
that fits both tests is 9.95 to 10.25 minutes, not 9.35 to 10.20; the
client restarted at 05:34, which matters for the counterfactual of ab7
only; and five statements that the record's own data contradicted
(the age of the entries in failing and passing cells, the blob of the live
tests of the replay, the exporter's scope, the folders that the check
counts, and the blob of the controller of ab7 to ab10).
The record says that six replay runs cannot rule out a small effect of the
module, that the window of the client test is 0.3 minute wide, and that
the cleanup check ran with real residue of every kind.
The final candidate also passed the live controller in the first lab,
where case 9 runs: 245 passed, 0 failed, 1 skipped per edition, with the
fixture removed and verified clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Test-MatrixCleanup.ps1 now also counts and removes the profiles and the
profile folders C:\Users\NtfsProbe* (retried, because a profile that the
last task used stays loaded for a few seconds) and the entries of the
account probe in Performance Log Users. net.exe lists a local user by its
bare name and a deleted domain account by its SID or its cached name, so
the check matches NtfsProbe anywhere in the line or a SID.
Export-CellTimeline.ps1 takes the account of a cell that stopped before its
tests from the snapshot of its fixture, so that the series can include
such a cell, and reports SameNameAsPreviousCell and SameAccountAsPreviousCell
instead of one column that compared names only.
Test-StaleAuthzModel.ps1 computes its grid of lifetimes by index (the
accumulated step lost the grid point 10.25), prints a range as segments,
and prints the range of one lifetime for both tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The failures of the effective-access tests in the Windows Server 2022
cell are not a defect of the module and not a Kerberos S4U staleness of
the fixture: a replay with the baseline and the final candidate
alternating failed both, and the remote authorization managers answer as
if the account had no groups while the local manager and a Kerberos
logon are right. The Windows mechanism is unknown.
The notes say that fdd7a8b reverts cleanly while 962887a conflicts with
it, that the three fixes are in two commits, and that Decision 24 is in
the index of the patterns.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The effective-access failures of the Admin role in the Windows Server
2022 cell don't depend on the module. A replay of seven cells with the
baseline and the final candidate alternating failed the baseline in two
of three cells and the final candidate in one of three, not counting the
warm-up. In a failing cell the remote authorization managers answer as
if the account had no groups while the name resolution, the Kerberos
logon, and the local manager are right in the same second.
One model with one lifetime (9.35 to 10.20 minutes) fits all 43 Admin
role runs of 27 cells, and none of 5,000 random assignments of the
outcomes does. Four more cells with unique account names pass, two of
them where the model predicts a failure for a reused name.
The record, the README, and a timeline CSV now say what the evidence
supports and what it doesn't establish, that the cleanup check ran with
real residue, and that fdd7a8b reverts cleanly while 962887a conflicts
with it.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Test-MatrixCleanup.ps1 now counts and repairs the probe folders
(C:\NtfsProbeRecreation and C:\NtfsProbeModules), the local NtfsProbe*
users with their profiles, and the NtfsProbe* objects of the directory.
The scripts of the matrix default to the client OSWin11E.
Export-CellTimeline.ps1 writes one row for every cell, edition, and
Admin role: the module, the account and its relative ID, the times, and
the three effective-access tests. Test-StaleAuthzModel.ps1 replays such a
timeline against a model of the failures: the fit, a listing of the runs,
the cells of a controller that reuses the account name, and a permutation
test.
The comment in the controller says what the replay showed. The code of
the controller is unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Decision 24 (proposed): the matrix lab, what its deployment and the runs showed,
and what the maintainer decides. The context, patterns, progress, and deployment
notes carry the lessons: the Authz regime of a domain member, the stale Kerberos
S4U state of a re-created account, the evaluation client, and the integration of
the stacked branches.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The record, its tables (the suite per candidate, the failing tests, and the
controller cells), and the README of the lab. The final local candidate passes the
module's suite on Windows Server 2019, 2022, and 2025, Windows 11 22H2 and 26H1,
and the host in both editions, elevated and as a basic user, and the live
controller in three cells. The record states what a local build does not prove.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The suite runner removes its stage on a machine after it has copied the results
back; after an early stop, the stage stays for the diagnosis. The end-state check
counts the items in the stage folders and the scheduled tasks and standard users
of the kit, and -Mode Repair removes what is left of the stage and the tasks.
Probe-AccountRecreation.ps1 deletes an account and creates it again with the same
name in a loop, logs the user on with Kerberos S4U on the domain controller, the
client, and the file server, and asks Get-NTFSEffectiveAccess of each module under
test. It shows that Windows returns the old SID and groups, whichever version of
the module asks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
When an account is deleted and created again with the same name, a Kerberos S4U
logon for it keeps returning the SID and the groups of the deleted account for
a while, on the domain controller, the client, and the file server. The matrix
deletes the fixture after each cell and creates it for the next, so the
effective-access tests of the Admin role found no access for the new account in
cells that followed within minutes (Windows Server 2022 cell, candidate and
baseline alike, shown by a probe that creates the accounts in a loop). A new
fixture now gets NtfsLiveSubject and four digits; a fixture that exists keeps
its account.
The end-state check of the matrix also reports leftover scheduled tasks, stage
folders, standard users, and probe accounts, which the review asked for.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The review of the matrix kit found no Blocker or Major issue and these
Minor ones, all fixed here:
- Add-OsMatrixMachine.ps1 assigned the path of the AutomatedLab disk
deployment lock before it checked that the lock exists, so a refusal because
another deployment held the lock made the finally block delete that foreign
lock. The path is kept until this script has created the lock.
- Repair-OsMatrixBoot.ps1 tested the switches of the machine with an array
-ne, which is false for a machine without an adapter, so the guard that is
meant to refuse a machine outside the lab let it through and the script
turned it off. The guard counts the switches now.
- Deploy-OsMatrixLab.ps1 took the installation and domain administrator
password of the lab from Get-Random, which isn't a cryptographic generator.
It uses RandomNumberGenerator without a remainder bias, as the controller
does.
- Run-MatrixLocalSuite.ps1 removed its scheduled tasks, which store the
password of the account that runs them, only after a successful poll. The
finally block of the machine removes the tasks of the run now.
- Probe-EffectiveAccess.ps1 cleaned up the domain controller before the machine
without a try block, so a failure there skipped the cleanup of the machine.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Probe-EffectiveAccess.ps1 runs Invoke-EffectiveAccessProbe.ps1 on one machine
of the operating-system matrix under up to four tokens and copies the output
back: the lab account in a scheduled task at the highest run level, the same
account with the token of a basic user (SAFER level Normal User), a local
standard user, and a standard user of the domain. The standard users are
created for the run with a random password that exists only in memory, get
the batch logon right through Performance Log Users, and are removed again
with their profiles and group memberships; the names carry a time stamp,
because Windows keeps the SID of a deleted account for its name for a while.
For the account of the token and for well-known SIDs and the accounts of the
domain, the probe asks the cmdlet for the default server name, localhost, an
empty name, the names of this computer, and other computers, and writes the
result, the warnings, and the native error with the failing method. It showed
that the remote interface of the authorization manager of a computer in a
domain refuses every user who isn't an administrator, which is the defect
that the previous commit fixes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
On a computer in a domain, Get-NTFSEffectiveAccess wrote "Access is denied"
and no result for every user who wasn't an administrator of the computer,
also for the default -ServerName localhost and for every other name of this
computer. The remote interface of the authorization manager of a computer
answers only its administrators and the members of Access Control Assistance
Operators, and a computer in a domain offers that interface to every caller.
On a computer outside a domain the interface is not reachable, so the cmdlet
already used the local authorization manager there, which is why the tests
passed on the development host and on the CI runners.
For a name of this computer, the cmdlet now uses the local authorization
manager when the remote one refuses the user. That manager is the one the name
asks for, and it answered correctly in every probe on Windows Server 2019,
2022, and 2025 and on Windows 11: for a standard domain user, a local standard
user, and an administrator with a filtered token, for the user's own account,
Everyone, the Administrator of the computer, and the Administrator and Domain
Users of the domain. For the name of another computer, the denial stays an
error, as the cmdlet page and the live test of the delegated account describe.
Twenty tests of the suite failed in the basic-user mode on every domain-joined
machine of the operating-system matrix, with the published 5.0.0-rc7 code and
with the code before this change, and pass with it (Windows Server 2019: basic
user 782 and 780 passed, 0 failed, in Windows PowerShell and PowerShell 7). A
new live test runs the case as the administrator of the file server, who isn't
an administrator of the client.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Tests\Lab\Acceptance gets the scripts of Decision 24, which extends the
acceptance of the live tests from one lab to several operating-system builds:
- Deploy-OsMatrixLab.ps1, Add-OsMatrixMachine.ps1, Complete-OsMatrixLab.ps1
and Repair-OsMatrixBoot.ps1 build NtfsSecurityOsMatrixLab with AutomatedLab
(Server 2019, 2022 and 2025 file servers, Windows 11 clients) and repair a
base image whose host-side bcdboot left an empty EFI system partition.
- Test-MatrixReadiness.ps1 and Test-MatrixCleanup.ps1 gate each run and prove
that it left nothing behind (fixture accounts, shares, folders, group
members, orphaned SIDs, profiles); -Mode Repair removes what is left.
- Run-MatrixSequence.ps1 runs the live controller for every cell of the matrix
and stops after an infrastructure failure.
- Run-MatrixLocalSuite.ps1 and Invoke-LocalSuite.ps1 run the module's own
Pester suite on each machine in both editions, elevated and as a basic user,
as scheduled tasks: a process started from a remoting session gets every
privilege enabled, which eight of the tests do not expect.
- Export-MatrixResults.ps1 collates the cells, the skipped tests and the
package hashes into the results table.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The operating-system matrix runs on Windows Server 2019, 2022 and 2025 found
three defects in the fixture setup and removal of Invoke-NTFSSecurityLabTest.ps1:
- A native command's stderr that is redirected with 2>&1 is a terminating
error in Windows PowerShell 5.1 under $ErrorActionPreference = 'Stop'. The
first "The directory is not empty" line from PowerShell 7 ended the removal
on Server 2019 before any retry. The removal is now a bounded loop whose
PowerShell 7 command writes its errors to its output.
- Get-LocalGroupMember fails with "Failed to compare two elements in the
array" when a group holds an orphaned SID, for example that of an account an
earlier run deleted. The setup adds the members with Add-LocalGroupMember and
ignores MemberExistsException instead of checking the members first.
- A user profile that is gone in the meantime no longer fails the client
cleanup, and the retries are reported to the host.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The module's own tests ran on Windows Server 2019, 2022, and 2025 and on
Windows 11 in the operating-system matrix. Two tests failed on every
domain-joined machine and passed on the development host:
- Get-NTFSInheritance -SecurityDescriptor reported AuditInheritanceEnabled
as $false for an item without audit entries, where -Path reported $true.
On these computers Windows reports the SACL as protected from
inheritance when it reads all sections together, and as not protected
when it reads the SACL alone. The descriptor now takes the audit section
from a separate read, like it already did for the access section. Write()
stores the sections that were read, so a descriptor with the wrong flag
would also have written the SACL back as protected.
- Get-NTFSEffectiveAccess -ServerName '' wrote an "Access is denied"
error on computers where Windows takes an empty name for this computer.
An empty name no longer asks the remote interface of the authorization
manager; the cmdlet warns and returns the result of this computer, like
for any name that can't be reached.
The existing tests that found them stay as the regression guards: they were
red on three virtual machines (Windows Server 2022 and 2025, Windows 11)
and are green after the change. Three tests are new: the audit state of a
file and a folder without audit entries, and of a file and a folder with
disabled audit inheritance, by path and by descriptor.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The independent review of the acceptance record found no Blocker or Major
issue. This commit corrects what it found: the commit that fixed the break
row, what the State test shows, the baseline failures that carry no message,
the count of results, the wording about the folders before the first run, the
truncated messages in the results file, the README row of case 10, and the
review section of the record.
The 42 messageless baseline failures are now explained by a diagnostic that
runs the bodies of those tests in a TEMP sandbox: on the base, the second item
is removed, copied, moved, re-owned, or rewritten after Select-Object -First 1
or a throw; on the candidate it stays. The diagnostic, the check of the result
files, and a read-only check of a published version are in Tests/Lab/Acceptance.
Decision 22 no longer says that Copy-Item2 is like Copy-Item for a folder: the
built-in Copy-Item creates the missing parent folders of a folder copy,
Copy-Item2 of rc6 and of the candidate doesn't. The choice stays, the reference
point is corrected, and the question is left to the maintainer. The migration
hint of item 8 is stated as it is.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Decision 23 separates what the reporters of #34 said from what was tested
(Windows only), names the gaps, and lays out the maintainer's options: wait
for a report on the published candidate, or accept the untested risk with a
release-note caveat. It accepts nothing and keeps the gate open. It also holds
a draft comment for the issue, which the maintainer posts.
The checklist in Tests/Lab tells a storage administrator and a delegated user
how to run the commands of case 1 against a disposable folder on a NetApp, EMC,
or IBM file server, what to report, and what to keep out of the report.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The maintainer delegated open decisions to the agent on 2026-10-09 ("do it and
report about it later"). The agent checked the ten choices of Decision 22
against the source, the tests, the cmdlet pages, and the changelog, and
confirmed all of them. The record lists the impact for a caller and the
documentation of each choice, and says how to revert one. Its status stays
proposed until the maintainer confirms it.
The Memory Bank notes the repeated lab acceptance of the paths fixes and the
lesson about proving a fix with a baseline run.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Case 10 of the lab tests checks, over SMB and on the file server, what the
fixes of ai/quality-gate-paths changed: the owner restore, InheritedFrom, a
later command that ends the pipeline or throws (also at the verbose, debug,
and error streams), Get-ChildItem2 -Filter, and the privileges that a
stopped cmdlet left enabled. The fixture adds the folders that the tests
need. 78 tests per edition are new.
The record compares the candidate 83149ee with its base f11ff41 in the lab:
the candidate passed 486 tests and failed none, the base failed 148 of the
same tests, and each of them passes on the candidate. The fixture was removed
and the end state verified independently.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Handoff 1 of the pre-5.0.0 quality gate. The report classifies every
C# method that no test visited at 5a5d58b (231 methods, 442 sequence
points: 223 explained, 8 open for the maintainer) and records the
measurement (3,192/3,634 sequence points, 87.84%), 26 bounded
mutations, a red/green matrix of the fix commits over ten states of
the branch, the open items, the handoff to gate 3, and nine static
review passes. The CSV files hold the method, cmdlet, and parameter
set tables, the guard rows of the matrix with their test files, and
the SHA-256 of each of its 40 logs.
The Memory Bank notes (active context, progress, tech context, system
patterns) and the lab README point to it. No code, test, or help
changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The comment above the recording Write methods of BaseCmdlet named three
cmdlets that call ShouldProcess in a try block; only Remove-Item2 does.
The Get-ChildItem2 page now says that the dot rules of the matching can
differ, not that they differ, and the generated help follows. No test and
no executable code changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The cmdlet page said that the AlphaFS enumeration alone decides which names match, while the code compares each returned name with the pattern again; both apply, and the page now says so. The comment of PipelineControl said that every Write method is noted, but WriteWarning is not, and it names ShouldProcess as an example of an unnoted call. The changelog entries about the privileges name the cmdlets that enable them for the duration of their command, because Enable-Privileges keeps them enabled by design.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The independent review of the delta found the tests below weak. The verbose and debug rows of PipelineControl ran their command without an error action; the CI runner sets Stop, under which the handler that reports a later command's exception as an item error ended the pipeline with it, so the mutations that stop recording the verbose or the debug exception escaped (M19 in two configurations, M20 in all four). The rows now say -ErrorAction SilentlyContinue. The Enable-Privileges tests for a script named NTFSSecurity.Init.ps1 could not fail for the branch they name when the module setting was true, because the module enables the privileges itself before the cmdlet runs; they now also assert the verbose message that only the cmdlet writes, and they disable the privileges before each test so that the child process inherits none.
The first-nested-folder test asserts the first folder, not either. Set-TestNullDacl refuses an item that is a link, which the native call would follow out of the sandbox. A table pins the three outcomes that the filter documentation lists for a dot (Report.*, a trailing dot, an empty value), so a change of those rules is a decision.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The CI runner sets $ErrorActionPreference to Stop, so three new PipelineControl tests that take the error of a nested folder through 2>&1 ended at the first error before the later command saw it (elevated Windows PowerShell 5.1 of the frozen run: three failures). They now pass -ErrorAction Continue. The focused runner of this work did not set the preference and missed it; it does now.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Add-NTFSAccess, Remove-NTFSAccess, and Add-NTFSAudit report an AddAceError or RemoveAceError for each item when .NET refuses an entry without rights, change nothing, and return nothing with -PassThru. Get-NTFSAccess returns the one entry that .NET reports for a NULL DACL without a source; the new helper Set-TestNullDacl writes it through SetNamedSecurityInfo inside the sandbox guard. Get-NTFSHardLink lists the names of a file whose read rights are denied, which is why its UnauthorizedAccessException handler has no trigger. The public Extensions.ForEach and GetParent helpers, which a static scan listed as unused although cmdlets call them, are tested directly.
All of these characterize behavior that was already correct, so none was red before; the mutations that prove their detection run against the frozen measurement commit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Get-ChildItem2 swallowed what a later command threw for the error of a nested folder, for example `Get-ChildItem2 -Recurse 2>&1 | ForEach-Object { throw 'x' }`: the recursion took it for a failure of the folder above, wrote a verbose message, and left the loop over the folders, so the listing ended early and the caller never saw the exception. BaseCmdlet now notes the exception that WriteError raises, as it does for WriteObject, WriteVerbose, and WriteDebug. The report that -ErrorAction Stop is swallowed there too was not reproducible (the error reaches the caller), and a break or Select-Object -First was already passed on by type; the tests keep both.
A cmdlet that enables the privileges wrote the debug message "..enabled" before it noted the privilege, so a later command that ended the pipeline or threw at that message left the privilege enabled: Dispose disables only what is noted. TryEnablePrivilege also took the exception of a later command for a failure to enable the privilege and went on with the next one, so `Get-NTFSOwner 5>&1 | ForEach-Object { if ($_.Message -eq '..enabled') { throw 'x' } }` enabled all four privileges and hid the exception. The privilege is noted before the message, and TryEnablePrivilege passes the exception on.
Red before the fix in the Release build of 7aa8315: three tests (the throw on the error stream, in four configurations the first, in the two elevated ones the two privilege tests). Also covers the typed-throw rows that fail if PowerShell stops wrapping a thrown exception, and Enable-Privileges in a script named NTFSSecurity.Init.ps1.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The catch for an UnauthorizedAccessException of Get-ChildItem2 had an exception filter that passes on what a later command raises. A bounded mutation that removed the filter was not detected: PowerShell wraps the exception of a throw, so a cmdlet never sees the type that was thrown, and no later command can raise a raw UnauthorizedAccessException through a Write call. The filter and the test style that was meant to reach it are removed instead of leaving a branch that nothing can enter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The Write wrappers of BaseCmdlet note what a later command raises, so PipelineControl.IsEnd is reached only for an exception that did not pass through one, and its branches had no test. The tests call it through reflection: for a PipelineStoppedException, for a stand-in type that derives from a class named FlowControlException, as PowerShell keeps the exceptions of break and continue internal, and for failures of an item, also one that has an inner exception that ends the pipeline.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
A throw in a later command, or an error with -ErrorAction Stop, reaches a cmdlet through its Write call as an ordinary exception. The catch for the failures of an item reported it as the error of that item and went on, so that Remove-Item2 -PassThru removed the next item after a throw, and the caller never saw the exception. The earlier check found only the end of the pipeline and a break or continue. BaseCmdlet now notes the exception that its WriteObject, WriteVerbose, and WriteDebug raised, and every catch that can enclose a write passes it on; Get-DiskSpace writes outside its try. Set-NTFSSecurityDescriptor and Get-FileHash2 also caught it at a verbose message.
Get-ChildItem2 -Filter *.* returns every item, as Get-ChildItem does. The cmdlet compared each name with the pattern again and dropped the items without a dot, most folders among them; the dot stays an ordinary character in other patterns.
The failed lookup of InheritedFrom frees its native buffer. The help paragraph of -Filter has no pair of asterisks, which platyPS turns into emphasis, and the page has an example for *.*.
Review of the independent pass: the restored-owner test asserts that a plain write is denied, the drive-mapping helper has guard tests and takes letters that the no-volume tests do not, and the pipeline tests cover a throw, an error with -ErrorAction Stop, and the verbose and debug streams for every cmdlet that can reach the code.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The escape of brackets in the pattern read the filter before the pattern could reject it, so a null filter ended in a NullReferenceException. The parameter now rejects null with a validation error that names it; an empty filter still matches no item.
The cmdlet compares each name that the enumeration returns with the pattern again. The two disagree for *.*: the enumeration returns every item, as Get-ChildItem does, and the comparison drops the names without a dot, files and folders alike. A test pins this and reaches the branch that drops an item; the help says that a dot is an ordinary character.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The restore test of Set-NTFSSecurityDescriptor used a deny entry for the user, which a member of the owner group Administrators does not feel, so the first write succeeded and the ownership retry never ran. A deny entry for OWNER RIGHTS stops that write also for the owner; taking ownership drops the entry, the cmdlet writes the descriptor, and the user sets the group back without the Restore privilege. A probe shows the plain write is denied in that setup in both editions.
Remove-NTFSAccess with -SecurityDescriptor and -AccessType Deny had no test, although the overload for a descriptor adds Synchronize to allow entries only.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The cases for the retry of Set-NTFSSecurityDescriptor covered a descriptor that sets the owner, an owner that did not change, and an owner that the user cannot assign without the Restore privilege. The success path was not tested: the user can set a group of its access token back as the owner, such as Administrators in an elevated session, so the cmdlet restores the owner and reports nothing. The test runs elevated only, because the filtered token of the basic user cannot assign that group.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The cmdlet compares the name of every item that the enumeration returns with the pattern again, and it built that comparison with the wildcard syntax of PowerShell. A bracket then began a character class, so Report[1].txt was returned by the enumeration, which treats a bracket as itself, and dropped by the comparison, and a file with brackets in its name could not be found for its name with -Filter, which Get-ChildItem does. The documentation names only * and ? as wildcards, so a bracket and a backtick now stand for themselves in the comparison.
The regression test fails without the fix in all four configurations. The probe also showed that AlphaFS compares only the long name, so the test for *.htm guards the documented contract and no 8.3 behavior. The page of the cmdlet names the rule and the help file is generated again.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
New guards cover the default AllAccess token handle, token handles that lack the right to query or to adjust privileges, and the finalizer of an abandoned PrivilegeEnabler, which closes the handle that it owns; writes to the root of a drive that subst maps to a sandbox folder, which skip for the restricted token of the basic-user runner that cannot define a drive letter; Get-ChildItem2 -Filter against a match by short name; retargeting a descriptor with Item; the path overloads of the inheritance helpers for a missing path and of the audit rule reader for folders; removing a generic right while another account holds an exact entry; and an empty -ServerName.
Follow-ups of the independent review: dispose the enabler in the finally block before its handle, don't pin that the iterator overloads write only when enumerated, arrange the previous owner of the RestoreOwnerError test, and describe what ObjectApis.Tests.ps1 does.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
A catch for the failures of an item wrapped the write of its object. When a later command ended the pipeline, a break or continue in a script block or Select-Object -First, the exception passed through that catch, which reported it as an error of the item and went on with the next one. Remove-Item2, Copy-Item2, and Move-Item2 with -PassThru then removed, copied, or moved every item although the caller had ended the pipeline, and Set-NTFSOwner and Set-NTFSSecurityDescriptor changed every item. Get-NTFSSecurityDescriptor, Get-NTFSSimpleAccess, and Get-DiskSpace ignored the break, and Get-ChildItem2 ignored it for items below the first folder.
A helper recognizes the end of the pipeline and the control-flow exceptions of PowerShell by their base type, and these catches pass them on. The new table-driven tests run every cmdlet that writes objects: 26 cases for the nine cmdlets fail without the fix in all four configurations and the 64 cases of the others pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
When Windows cannot name the folder of an inherited entry, such as for an item that was deleted after its descriptor was read or for a folder above it that the user cannot read, the module fills InheritedFrom with a fallback text. The callers removed the last character of every source, which belongs to the trailing backslash of a real folder, so the fallback read 'unknown paren'. The fallback also named an unknown parent for explicit entries, which have no source. Remove only a trailing backslash, and name an unknown parent for inherited entries only.
The regression tests fail without the fix in all four configurations for access entries and in both elevated configurations for audit entries. The cmdlet pages name the text, and the help file is generated again.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The removal helpers for a path ran only against a file. Run both removal tests for a file and a folder, and add the deny variant of the iterator overload that adds the entries of several accounts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>