feat(powershell): comprehensive portable Authenticode cmdlet suite#12
Merged
Marc-André Moreau (mamoreau-devolutions) merged 15 commits intoMay 24, 2026
Conversation
…ract - Change PortableSignature.Status to SignatureStatus enum type - Change TrustStatus to SignatureStatus? enum type - Add computed SignatureType property (System.Management.Automation.SignatureType) - Add SubjectAlternativeName extraction from signer certificate SAN extension - Add PortableStatus/PortableTrustStatus string accessors for backward compat - Add LP alias to LiteralPath on both Get/Set cmdlets - Add ValueFromPipeline/ValueFromPipelineByPropertyName to content params - Add ValidateNotNullOrEmpty to Content parameter - Add certificate code-signing suitability preflight to Set-PortableSignature (validates EKU 1.3.6.1.5.5.7.3.3 and KeyUsage DigitalSignature) - Add Pester 5 metadata compatibility tests - Add migration guide to docs/portable-powershell-module.md Scripts using $sig.Status -eq 'Valid' or enum comparisons continue to work because PowerShell coerces between strings and SignatureStatus enum. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ain building Add automatic trust verification for Get-PortableSignature on Linux/cross-platform: - AuthRootCache.cs: downloads and caches Microsoft AuthRoot CAB at ~/.psign/authroot/authrootstl.cab with configurable max age (30 days) - Auto-trust in Get-PortableSignature: when no explicit trust params are provided, automatically uses the cached AuthRoot CAB for verification - -SkipTrust switch and PSIGN_NO_AUTO_TRUST env var for opt-out - Fix CTL parsing: handle naked CTL children in econtent (real-world authrootstl.cab uses back-to-back ASN.1 elements without outer SEQUENCE) - Trust-at-boundary chain building: issuer_chain_excluding_leaf(_online) now accepts Option<&AnchorStore> and terminates when a cert's thumbprint matches the anchor store (avoids needing root cert DER when only thumbprints are available from the CTL) - Auto-enable AIA when authroot_cab is specified: intermediate certs typically have AIA extensions pointing to the root CA URL, enabling the chain builder to fetch missing root certs on demand - Test isolation: PSIGN_NO_AUTO_TRUST=1 in existing test suites to prevent auto-trust from interfering with self-signed cert tests End-to-end verified: Get-PortableSignature on pwsh.exe now returns Status=Valid, TrustStatus=Valid with automatic AuthRoot trust. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement a NavigationCmdletProvider that exposes psign's file-based certificate store (~/.psign/cert-store/) as a pcert:\ PowerShell drive, mirroring the Windows cert:\ provider hierarchy. Features: - Auto-registers pcert: drive on module import - Full navigation: cd, Get-ChildItem at root/scope/store levels - CRUD: New-Item (import cert), Get-Item, Remove-Item, Copy-Item - Custom drives via New-PSDrive -Root for alternate store locations - Accepts X509Certificate2, byte[] (DER), string (PEM/path) for import - Returns X509Certificate2 objects (same as Windows cert:\ provider) - HasPrivateKey reflects .key file existence Refactored shared cert store helpers from SetPortableSignatureCommand into Provider/CertStorePathHelper.cs for reuse. 21 Pester tests covering drive registration, navigation, CRUD, custom drives, and error handling all pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lidation Implements a cmdlet that validates whether a PowerShell module would load cleanly under AllSigned or RemoteSigned execution policies. This enables portable, cross-platform module signature compliance checking. The cmdlet: - Parses the .psd1 manifest to discover RootModule, ScriptsToProcess, NestedModules, TypesToProcess, and FormatsToProcess - Accurately models PowerShell engine behavior: .psd1 is never checked, .dll binary modules bypass execution policy, only .ps1/.psm1/.ps1xml/ .cdxml are validated - Reports per-file pass/fail with role, status, and failure reason - Supports -RequireTrustedPublisher to also verify the signer's leaf cert is in pcert:\*\Trust (TrustedPublisher equivalent) - Supports -IncludeUnreferenced for belt-and-suspenders validation of all signable files in the module directory Usage: Test-PsignModule -Path ./MyModule -Policy AllSigned Test-PsignModule ./MyModule -RequireTrustedPublisher Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e cmdlets New cmdlets: - Protect-PsignModule: signs all policy-checked files in a PowerShell module in one shot (manifest-aware, skips binary .dll modules, supports all signing backends: local cert, PFX, thumbprint, Azure Key Vault, Trusted Signing) - Unprotect-PsignSignature: strips Authenticode signature blocks from script files (.ps1, .psm1, .psd1, .ps1xml, .cdxml) with encoding preservation Also adds: - Devolutions.Psign.Format.ps1xml for default formatted output of Test-PsignModule, Protect-PsignModule, and Unprotect-PsignSignature - CertStorePathHelper.LoadCertificateAndKey for thumbprint-based signing material resolution - 13 new Pester tests covering all three new cmdlets (47 total pass) Module now exports 5 cmdlets: Get-PortableSignature, Set-PortableSignature, Test-PsignModule, Protect-PsignModule, Unprotect-PsignSignature Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- PortableSignature table/list format views for Get/Set-PortableSignature output (shows path, status, type, signer, timestamp at a glance) - Argument completers in .psm1 for tab-completion of: - Thumbprint (enumerates pcert store, shows cert subjects) - StoreName, HashAlgorithm, RevocationMode, Policy - about_Devolutions.Psign help topic covering all cmdlets, trust model, signing sources, certificate store, and examples - HelpMessage attributes on all mandatory parameters Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Covers all 5 cmdlets, certificate store, trust model, signing sources, pipeline integration patterns, and module compliance workflow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ard-compat aliases
Rename the core cmdlets from Get-PortableSignature / Set-PortableSignature to
Get-PsignSignature / Set-PsignSignature for consistent project branding.
- Rename C# files and classes (GetPsignSignatureCommand, SetPsignSignatureCommand)
- Update [Cmdlet] noun to 'PsignSignature'
- Add [Alias("Get-PortableSignature")] and [Alias("Set-PortableSignature")] for backward compat
- Update CmdletsToExport and AliasesToExport in manifest
- Register argument completers for both canonical and alias names
- Update all tests, docs, and READMEs to use new canonical names
- Add alias verification test in compatibility suite
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…m completer - Add PsignSignature.ContentSigning.Tests.ps1 with 8 tests covering: - PowerShell script content sign + verify round-trip - ps1xml and psm1 content signing - Unsigned content detection (NotSigned) - Tampered content detection (HashMismatch) - Signing via -Thumbprint from portable cert store - Integration with pcert: provider - Add TimestampHashAlgorithm argument completer (Sha1, Sha256, Sha384, Sha512) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add IncludeChain argument completer (Signer, NotRoot, All) - Add content signing and pcert: integration examples to about help topic - Update TimestampHashAlgorithm and IncludeChain in TAB COMPLETION docs - Fix stale Get-PortableSignature reference in trust model section Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The legacy smoke test tried to rebuild and copy psign-core.dll even when the module was already loaded by earlier Pester test files in the same process. Now detects an already-loaded module and skips the build, avoiding the IOException from overwriting a locked DLL. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix X509Certificate2 leak: add EndProcessing to dispose pfxCertificate and storeCertificate after pipeline processing completes - Improve ECDSA error: detect ECDSA keys and report a clear unsupported message instead of the generic 'requires RSA private key' - Remove dead code: pointless null assignment in LoadPfxCertificate - Cache computed properties: PortableSignature.SignerCertificate and TimeStamperCertificate now decode only once per access - Fix CertStorePathHelper: try ECDSA key import when RSA fails, so pcert: provider can associate ECDSA private keys - Expand package.ps1 smoke test to validate all 5 exported cmdlets Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 5 new test files covering:
- Set-PsignSignature: signing material validation (16 tests)
- Error paths: no material, incomplete key pair, non-CodeSigning EKU,
missing DigitalSignature KeyUsage, multiple sources, ECDSA
- PFX signing with correct/wrong passwords
- OutputPath (single/multiple inputs)
- Force on read-only files
- HashAlgorithm variants (Sha384, Sha512)
- IncludeChain modes (Signer, All)
- Get-PsignSignature: expanded verification (11 tests)
- Wildcard path expansion
- Pipeline input (string paths, FileInfo objects)
- Error handling for non-existent files
- Content mode edge cases (bare ext, dotted, full filename)
- TrustedCertificatePath trust evaluation
- SkipTrust digest-only verification
- Unprotect-PsignSignature: signature removal (10 tests)
- Script formats (.ps1, .psm1, .psd1)
- XML formats (.ps1xml, .cdxml)
- WhatIf support
- Wildcard path processing
- Error handling and encoding preservation (UTF-8 BOM)
- Test-PsignModule: policy validation (8 tests)
- AllSigned and RemoteSigned policies
- Error handling (non-existent path, no manifest fallback)
- Tamper detection (HashMismatch)
- IncludeUnreferenced switch
- Protect-PsignModule: batch module signing (8 tests)
- CertificatePath/PrivateKeyPath and PfxPath signing
- Error handling (non-existent path, no signing material)
- IncludeUnreferenced switch
- Result properties verification
- Round-trip with Test-PsignModule
All 110 tests pass in a single session (cross-platform, no
Windows-only APIs like New-SelfSignedCertificate).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add -KeyExportPolicy Exportable to New-SelfSignedCertificate so the private key can be exported via ExportPkcs8PrivateKey() in CI - Add BeforeDiscovery/Skip gate so all Describe blocks are skipped on non-Windows platforms (the file depends on New-SelfSignedCertificate and Cert:\ provider which are Windows-only) - Cross-platform equivalents already exist in the expanded test files Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
fc47ba2 to
546624e
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
aa36d81
into
master
36 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A complete cross-platform PowerShell module (
Devolutions.Psign) for Authenticode signing, verification, and module compliance — no Windows APIs required.New cmdlets
Get-PsignSignatureSet-PsignSignatureUnprotect-PsignSignatureTest-PsignModuleProtect-PsignModuleBackward-compatible aliases
Get-PortableSignature/Set-PortableSignatureare preserved.Infrastructure
pcert:\provider — cross-platform certificate store (~/.psign/certstore/) mimicking the WindowsCert:\layout. SupportsGet-ChildItem,Get-Item,New-Item,Remove-Item.~/.psign/for portable chain building (opt-out via$env:PSIGN_NO_AUTO_TRUST=1).PortableSignature, tab-completion forThumbprint,StoreName,HashAlgorithm,IncludeChain,TimestampHashAlgorithm,RevocationMode,Policy.Test coverage
110 Pester tests passing in a single cross-platform session:
-Contentmode,-SkipTrust-WhatIf, encoding preservationQuality fixes
EndProcessing()to prevent leaksSignerCertificate/TimeStamperCertificateproperties (decode once)