mirror of https://github.com/raandree/NTFSSecurity
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8.4 KiB
8.4 KiB
| status | last-verified | owner | source |
|---|---|---|---|
| current | 2026-10-08 | active-agent | repository evidence |
System patterns
Architecture
NTFSSecurity.psd1 ─┬─ ScriptsToProcess: NTFSSecurity.Init.ps1
│ Add-Type: Security2.dll, PrivilegeControl.dll,
│ ProcessPrivileges.dll, inline NTFS.DriveInfoExt;
│ Update-FormatData -PrependPath format.ps1xml
├─ TypesToProcess: NTFSSecurity.types.ps1xml
│ (Owner, IsInheritanceBlocked, LengthOnDisk on
│ FileInfo/DirectoryInfo; AccountType on ACEs)
├─ RootModule: NTFSSecurity.psm1 (aliases)
├─ NestedModules: NTFSSecurity.dll (36 cmdlets)
└─ en-US\NTFSSecurity.dll-Help.xml (Get-Help; generated
from Docs/Cmdlets, Decision 8)
NTFSSecurity.dll ── cmdlets ──> Security2.dll (FileSystemAccessRule2,
FileSystemAuditRule2, IdentityReference2,
FileSystemInheritanceInfo, EffectiveAccess)
── long paths ──> AlphaFS
── privileges ──> PrivilegeControl / ProcessPrivileges
BaseCmdletresolves only relative paths, against the current file system location of the session (not$PWD, #86). Path parameters carry[FileSystemPathTransformation], which binds file objects as full paths.- On access denied, most cmdlets retry through
InvokeAsOwner, which takes ownership and restores the previous owner on every exit path. BaseCmdletWithPrivControlenables Backup, Restore, TakeOwnership, and Security inBeginProcessingwhenPrivateData.EnablePrivilegesis$true, and disables the ones it enabled inEndProcessingand, since 5.0.0-rc6, inDispose: PowerShell skipsEndProcessingwhen a later command, such asSelect-Object -First, or a terminating error stops the pipeline, but callsDispose.Enable-Privilegeskeeps them (KeepEnabledPrivileges). The cleanup reads the current state of each privilege, because another command in the pipeline can have changed it, and tries every privilege even when one fails:EndProcessingwarns,Disposestays silent, because PowerShell ignores exceptions thrown there and no stream is open anymore.PrivateDataswitches:EnablePrivileges,GetInheritedFrom,GetFileSystemModeProperty,IdentifyHardLinks,ShowAccountSid.- Cmdlets accept
-Path(aliasFullName) or-SecurityDescriptor; the SD sets change the object in memory untilSet-NTFSSecurityDescriptor.
Decisions
Each Decision record is a file in decisions/; read only the relevant ones.
Patterns
Writing cmdlets
- A parameter that takes pipeline input needs a getter that doesn't
throw: PowerShell reads it before it binds each input object, and an
exception turns every object into
GetDefaultValueFailed(the link cmdlets before 5.0.0-rc7). - An error for one item is non-terminating, so that the cmdlet goes on with the next path or pipeline object; since 5.0.0-rc7, the link cmdlets too. Its message names the item, and its target object is the item that the cmdlet was asked to process. Resolving a path can throw in Windows PowerShell for an invalid character, so that belongs inside the per-item error handling.
- Folders move without
MoveOptions.CopyAllowed: for another volume, AlphaFS then copies and deletes, which lost empty folders. Windows refuses such a move withNotSameDeviceException(17). Tests reach another volume through\\localhost\C$, elevated only.
Verifying documentation
- Run platyPS in Windows PowerShell 5.1 against a Release build; a copy of
Docs/Cmdletsmust round-trip throughUpdate-MarkdownHelpunchanged. Keep cmdlet pages ASCII-only. platyPS takesPositionandRequiredfrom the shipped help file: after such a change, edit the page YAML, runNew-ExternalHelp, rebuild, and check the round trip. - MarkdownLinkCheck checks relative
Docslinks,Tests\Wiki.Tests.ps1the wiki links and anchors. The wiki is generated fromDocs(never edit it);Docs/README.mdbecomes Home, its cmdlet groups the sidebar. - In cmdlet pages, end a sentence with a link (platyPS drops the space
after it). Verify examples in a
$env:TEMPsandbox, never on real data.
Testing the module
- Pester 5 tests in
Tests/*.Tests.ps1import the Release build; CI runs them in Windows PowerShell 5.1 and PowerShell 7 (Decision 11). - A test that changes files, links, or security descriptors uses
Tests\TestHelpers.psm1: its own sandbox,Assert-TestSandboxPathbefore each change,Remove-TestSandbox. Cases that need a privilege skip withTest-PrivilegeHeld, cases that need its absence skip when elevated; CI runs the suite elevated and as a basic user in both editions, so each case runs somewhere.Block-Test*make a read or a write fail without elevation;Set-TestOwnerwithEnablePrivileges = $falsereproduces an owner the user can't assign. - Fixtures write a DACL with
SetAccessControl, never withSet-Acl:Set-AclcomparesAreAuditRulesProtectedof the new descriptor withAreAccessRulesProtectedof the item (FileSystemSecurity.csof PowerShell), so for an item with a protected DACL it writes the audit section too. Without the Security privilege that fails withPrivilegeNotHeldException; with it,Set-Aclwrites every section and drops the audit entries. Windows PowerShell hasFileInfo/DirectoryInfo.SetAccessControl; PowerShell 7 has[System.IO.FileSystemAclExtensions]::SetAccessControl. Get-Help -Onlinetests run only in Windows PowerShell, which honors the hookBypassOnlineHelpRetrieval.Manifest.Tests.ps1andRelease.Tests.ps1check the manifest, the version (Decision 10), the release notes, and the packages.- The live tests in
Tests\Lab(Decision 20) run as domain accounts in a lab: on the client over SMB, then on the file server, which checks what the client runs left. They read and write descriptors as Windows stores them withGetFileSecurityandSetFileSecurity, becauseGetNamedSecurityInfoconverts a DACL without the auto-inherit flag and returns its owner. The expected effective rights come from the S4U tokens of the file server and the client, like the Effective Access tab.