3.1 made a FileSystemSecurity2 without an item reachable from a .NET
caller (the conversions from FileSecurity and DirectorySecurity), but
six sibling call sites still dereferenced the item without checking it
first, so a caller who converted a FileSecurity or DirectorySecurity
and then read its entries, owner, inheritance, or effective access met
a NullReferenceException instead of a usable result.
GetFileSystemAccessRules and GetFileSystemAuditRules (the
FileSystemSecurity2 overload) now give each returned entry the same
null FullName that the descriptor itself reports; asking for the
source of an inherited entry (getInheritedFrom) needs the path of the
item, so it now throws one clear InvalidOperationException instead,
naming the reason, when the descriptor has none. FileSystemOwner and
FileSystemInheritanceInfo give a null FullName (and Name) the same
way; FileSystemOwner.ToString, which must never return null, gives an
empty string instead, since there is no other representation to fall
back to. The FileSystemAccessRule2(rule, item) constructor, which
EffectiveAccess.GetEffectiveAccess uses to build its result, now
accepts a null item the same way, so effective access can be
calculated for such a descriptor and the result names no path.
Every existing behavior for an item that is not null is unchanged;
only a .NET caller that reads such a descriptor can notice the
difference, and only for the better: a usable result or a clear error
instead of an unrelated NullReferenceException. Public signatures are
unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
AddFileSystemAccessRule(string, List<IdentityReference2>, ...) and
AddFileSystemAuditRule(string, List<IdentityReference2>, ...) were
iterator methods (yield return), so nothing happened until the caller
enumerated the result: called as a statement, or assigned to a variable
that the caller never reads, they changed nothing. Their siblings, the
overloads for FileSystemInfo and FileSystemSecurity2 with a list, and
the single-account overloads, already built a list and wrote at once.
Both overloads now build the list and add the entries before returning,
like their siblings; a call that is never enumerated now adds the
entries just the same. An exception that adding an entry raises changes
only in timing: it is now thrown by the call itself, not by the later
enumeration of its result. The returned collection still holds one rule
per account, in the order of the account list, for both a file path and
a folder path.
Public signatures are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Both methods built a LINQ query that filtered the explicit entries down
to the ones whose account was in the given list, but never used the
result: the loop right below removed every explicit entry of the
descriptor regardless of the list, including an empty one. The cmdlets
never pass a list, so only a .NET caller of Security2.dll that does is
affected.
A non-null list now removes only the explicit entries whose account is
one of the listed accounts (compared like the rest of the class does,
by IdentityReference2 equality of the entry's SID); null still removes
every explicit entry, and an empty list now removes none. Inherited
entries are still never touched, as before. Both the FileSystemSecurity2
overload (in-memory only, until a caller writes the descriptor) and the
FileSystemInfo overload (which also writes) are fixed, for access and
audit entries alike.
Public signatures are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Converting a FileSecurity or DirectorySecurity object to
FileSystemSecurity2 (for example `[Security2.FileSystemSecurity2]
$fileSecurity`) always threw, because the conversion ignored the
descriptor it was given and built the result from an empty path, which
AlphaFS rejects. The conversion now wraps the descriptor that was
passed: SecurityDescriptor is that same instance, IsFile reflects
whether it came from a FileSecurity or a DirectorySecurity, the round
trip back to FileSecurity/DirectorySecurity returns the same instance,
and Equals/GetHashCode keep comparing descriptors by reference.
The resulting object has no item, since it was never read from a file
or folder: FullName and Name are now null instead of throwing a
NullReferenceException, and Write() throws a clear
InvalidOperationException instead of a NullReferenceException deep in
SetSecurity. A caller can still write such an object by setting its
Item property first, or by calling Write(FileSystemInfo) or
Write(string) directly, neither of which depends on an item being set.
No cmdlet reaches this conversion; only a .NET caller of Security2.dll
that converts a FileSecurity/DirectorySecurity it built itself is
affected. Public signatures are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
IdentityReference2.Equals(string) lowercased both names with ToLower(), which uses the
culture of the thread. In tr-TR, I becomes U+0131, so an account whose name holds an
uppercase I (NT AUTHORITY\SYSTEM, BUILTIN\Administrators) no longer matched the same name in
another case, for example $entry.Account -eq 'nt authority\system'. Compare with
StringComparison.OrdinalIgnoreCase.
The test sets the culture of the thread to tr-TR and compares the system account with its name
in three cases. Before the fix it fails in all four configurations (elevated and basic user,
Windows PowerShell 5.1 and PowerShell 7); after it, all rows pass. The unit suite under tr-TR
did not find it, because no test compares a name that holds an uppercase I.
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>
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>
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>
PSScriptAnalyzer flagged the plural nouns and the state-changing verb of three helpers; the repository suppresses that rule for test helpers with a justification.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Cover the access and audit rule helpers that take a path, including the lazy iterator overloads and exact versus partial removal, the inheritance helpers for paths, owner and descriptor objects, generic rights mapping, identity construction errors and the PrivilegeEnabler class. These public APIs have no cmdlet caller.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Reproduce and fix public rule paths, simplified audit comparisons and ReadData conversion, and boxed privilege equality. Add behavior guards for descriptor inheritance, unresolved identities, audit capability and recursive denial. Freeze this source for Release matrix measurement; final gate evidence and independent review follow.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>