Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@
`Thumbprint` the relaxation is a fallback rather than a blanket bypass: an
unexpired certificate is still preferred whenever one exists, an expired one
is selected only when no unexpired one was found, and a warning is emitted
when that happens. `Thumbprint` still matches the requested thumbprint, and a
private key is still required in every case, because a certificate without one
cannot sign.
when that happens. `Thumbprint` still matches the requested thumbprint, and
both store-backed sources still require a private key, because that is part of
how they select a certificate rather than a check layered on afterwards. Note
that `EnvVar` and `PfxFile` are different: the setting skips every check for
them, the private key check included, so a certificate exported without its
key is accepted here and fails later in `Set-AuthenticodeSignature` instead.

- [**#191**](https://github.com/psake/PowerShellBuild/issues/191)
The README's psake and Invoke-Build examples assigned
Expand Down Expand Up @@ -188,7 +191,7 @@
that passed before may now correctly fail.
- [**#96**](https://github.com/psake/PowerShellBuild/issues/96)
`Test-PSBuildScriptAnalysis` no longer fails with a path-resolution error
when `SettingsPath` is not supplied. An unsupplied path was forwarded to

Check warning on line 194 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (unsupplied) Suggestions: (unapplied, unsullied, unspoiled, unstapled, unsupported)
PSScriptAnalyzer as `-Settings ''`, which resolved against the current
directory and threw before any analysis ran, so the function's own
documented example could not run as written.
Expand Down
9 changes: 5 additions & 4 deletions PowerShellBuild/Public/Get-PSBuildCertificate.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function Get-PSBuildCertificate {
function Get-PSBuildCertificate {
<#
.SYNOPSIS
Resolves a code-signing X509Certificate2 from one of several common sources.
Expand Down Expand Up @@ -42,10 +42,10 @@
.PARAMETER CertificateEnvVar
Name of the environment variable holding the Base64-encoded PFX certificate.
Used by the EnvVar source and by Auto as the presence-detection key.
Default: SIGNCERTIFICATE.

Check warning on line 45 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (SIGNCERTIFICATE)
.PARAMETER CertificatePasswordEnvVar
Name of the environment variable holding the PFX password. Used by EnvVar source.
Default: CERTIFICATEPASSWORD.

Check warning on line 48 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (CERTIFICATEPASSWORD)
.PARAMETER PfxFilePath
File system path to a PFX/P12 certificate file. Required when CertificateSource is PfxFile.
.PARAMETER PfxFilePassword
Expand All @@ -56,16 +56,17 @@
checks outright. For the Store and Thumbprint sources, which select one certificate out
of many, an unexpired certificate is still preferred whenever one exists; an expired
certificate is returned only when no unexpired one was found, and a warning is emitted
when that happens. A private key is required in every case, because a certificate
without one cannot sign. Use with caution; invalid certificates will fail during actual
signing operations with less descriptive errors.
when that happens. Those two sources always require a private key, because it is part of
how the certificate is selected rather than a check layered on afterwards; EnvVar and
PfxFile do not, since this skips their checks entirely. Use with caution; invalid
certificates will fail during actual signing operations with less descriptive errors.
.OUTPUTS
System.Security.Cryptography.X509Certificates.X509Certificate2
Returns the resolved certificate, or $null if none was found (Store/Thumbprint sources).
.EXAMPLE
PS> $cert = Get-PSBuildCertificate

Resolve automatically: use the SIGNCERTIFICATE env var when present, otherwise search

Check warning on line 69 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (SIGNCERTIFICATE)
the current user's certificate store.
.EXAMPLE
PS> $cert = Get-PSBuildCertificate -CertificateSource Store
Expand All @@ -82,7 +83,7 @@
Decode a PFX certificate stored in a CI/CD secret environment variable.
.EXAMPLE
PS> $pass = Read-Host -Prompt 'Certificate password' -AsSecureString
PS> $cert = Get-PSBuildCertificate -CertificateSource PfxFile -PfxFilePath './codesign.pfx' -PfxFilePassword $pass

Check warning on line 86 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (codesign) Suggestions: (codein, cosign, consign, cohesion, codebig)

Load a code-signing certificate from a PFX file on disk.
#>
Expand All @@ -101,13 +102,13 @@

[string]$Thumbprint,

[string]$CertificateEnvVar = 'SIGNCERTIFICATE',

Check warning on line 105 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (SIGNCERTIFICATE)

[string]$CertificatePasswordEnvVar = 'CERTIFICATEPASSWORD',

Check warning on line 107 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (CERTIFICATEPASSWORD)

[string]$PfxFilePath,

[securestring]$PfxFilePassword,

Check warning on line 111 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (securestring) Suggestions: (secreting, sequestering)

[switch]$SkipValidation
)
Expand Down
19 changes: 11 additions & 8 deletions PowerShellBuild/build.properties.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# spell-checker:ignore PSGALLERY BHPS MAML
# spell-checker:ignore PSGALLERY BHPS MAML
BuildHelpers\Set-BuildEnvironment -Force

$outDir = [IO.Path]::Combine($env:BHProjectPath, 'Output')
Expand Down Expand Up @@ -54,7 +54,8 @@

# Specifies an output file path to send to Invoke-Pester's -OutputFile parameter.
# This is typically used to write out test results so that they can be sent to a CI system
# This path is relative to the directory containing Pester tests
# The default below is absolute. A relative path resolves against the directory
# containing Pester tests, because Test-PSBuildPester pushes into it before running.
OutputFile = [IO.Path]::Combine($env:BHProjectPath, 'testResults.xml')

# Specifies the test output format to use when the TestOutputFile property is given
Expand Down Expand Up @@ -126,7 +127,7 @@
# Whether to overwrite existing markdown files and use comment based help as the source of truth
Overwrite = $false

# Exclude the parameters marked with `DontShow` in the parameter attribute from the help content.

Check warning on line 130 in PowerShellBuild/build.properties.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (Dont) Suggestions: (dent, dint, doit, dolt, dona)
# Value passed to New-MarkdownCommandHelp.
ExcludeDontShow = $false

Expand Down Expand Up @@ -170,7 +171,7 @@

# Name of the environment variable that holds the Base64-encoded PFX certificate.
# Used by the EnvVar source and as the presence-detection key for Auto.
CertificateEnvVar = 'SIGNCERTIFICATE'

Check warning on line 174 in PowerShellBuild/build.properties.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (SIGNCERTIFICATE)

# Name of the environment variable that holds the PFX password (EnvVar source).
CertificatePasswordEnvVar = 'CERTIFICATEPASSWORD'
Expand All @@ -190,18 +191,20 @@
# recommended for production use but can be useful in CI environments
# where certificates are frequently renewed and rotated.
#
# The EnvVar and PfxFile sources load exactly one certificate, so the
# expiration and Code Signing EKU checks are skipped outright for them.
# The EnvVar and PfxFile sources load exactly one certificate, so every
# check is skipped outright for them -- the private key check included.
# A certificate without a private key cannot sign, so setting this for
# those two sources defers that failure to Set-AuthenticodeSignature,
# which reports it far less clearly.
#
# The Store and Thumbprint sources select one certificate out of many,
# so the relaxation is a fallback rather than a blanket bypass: an
# unexpired certificate is preferred whenever one exists, and an expired
# one is returned only when no unexpired certificate was found. A
# warning is emitted when that happens, and the Thumbprint source still
# matches the requested thumbprint.
#
# A private key is required in every case, because a certificate without
# one cannot sign.
# matches the requested thumbprint. These two sources always require a
# private key, because it is part of how the certificate is selected
# rather than a check layered on afterwards.
SkipCertificateValidation = $false

# RFC 3161 timestamp server URI embedded in Authenticode signatures.
Expand Down
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ match your environment.
| $PSBPreference.Sign.PfxFilePath | `$null` | File system path to a PFX/P12 certificate file. Required by the `PfxFile` certificate source. |
| $PSBPreference.Sign.PfxFilePassword | `$null` | Password for the PFX/P12 file as a `SecureString`. Used by the `PfxFile` certificate source. |
| $PSBPreference.Sign.Certificate | `$null` | A pre-resolved `X509Certificate2` object to sign with. When set, `CertificateSource` is ignored, which suits Azure Key Vault, an HSM, or another custom provider. |
| $PSBPreference.Sign.SkipCertificateValidation | `$false` | Skip the private key, expiration, and Code Signing EKU checks made on certificates loaded by the `EnvVar` and `PfxFile` sources. Not recommended in production. |
| $PSBPreference.Sign.SkipCertificateValidation | `$false` | Relax certificate validity checking. Skips every check for `EnvVar` and `PfxFile`, private key included. See [Code signing](#code-signing). |
| $PSBPreference.Sign.TimestampServer | `http://timestamp.digicert.com` | RFC 3161 timestamp server URI embedded in the signature so that it stays valid after the certificate expires. |
| $PSBPreference.Sign.HashAlgorithm | `SHA256` | Authenticode hash algorithm. Valid values are `SHA256`, `SHA384`, `SHA512`, and `SHA1`. `SHA1` is deprecated. |
| $PSBPreference.Sign.FilesToSign | `@('*.psd1', '*.psm1', '*.ps1')` | Glob patterns of file names to sign, searched recursively under the module output directory. |
Expand Down Expand Up @@ -184,10 +184,14 @@ Where the code-signing certificate comes from is controlled by
`$PSBPreference.Sign.CertificateSource`:

- `Store` selects the first valid, unexpired code-signing certificate that has a
private key from `$PSBPreference.Sign.CertStoreLocation`.
private key from `$PSBPreference.Sign.CertStoreLocation`. With
`$PSBPreference.Sign.SkipCertificateValidation` set, an expired certificate is
used as a fallback when no unexpired one is found, and a warning says so; a
private key is always required, because it is part of the selection.
- `Thumbprint` selects a specific certificate from that same store by
`$PSBPreference.Sign.Thumbprint`, which is what you want when more than one
code-signing certificate is installed.
code-signing certificate is installed. The same expired-certificate fallback
applies, and the requested thumbprint is still matched.
- `EnvVar` decodes a Base64-encoded PFX from the environment variable named by
`$PSBPreference.Sign.CertificateEnvVar`, optionally decrypting it with the
password in the variable named by
Expand All @@ -196,6 +200,13 @@ Where the code-signing certificate comes from is controlled by
held as a masked secret.
- `PfxFile` loads a PFX/P12 file from `$PSBPreference.Sign.PfxFilePath` using
`$PSBPreference.Sign.PfxFilePassword`.

`EnvVar` and `PfxFile` load exactly one certificate rather than choosing from
many, so `$PSBPreference.Sign.SkipCertificateValidation` skips their checks
outright — expiry, Code Signing EKU, **and the private key**. A certificate
exported without its private key is accepted here and fails later in
`Set-AuthenticodeSignature`, with a much less helpful message. Prefer leaving
validation on for these two sources.
- `Auto`, the default, uses `EnvVar` when the certificate environment variable
is populated and falls back to `Store` when it is not. One build script can
therefore sign with the developer's own certificate locally and with the
Expand Down
7 changes: 4 additions & 3 deletions instructions/repository-specific.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ pattern — keep new public functions consistent with this):
Private helpers in `PowerShellBuild/Private/`:

- `Remove-ExcludedItem` — filters file system items by regular expression patterns during builds
- `Get-PSBuildHelpLocale` — resolves the locale used for help generation
- `Get-PSBuildHelpLocale` — classifies which docs subdirectories are help locales

### Invoke-Build alias

Expand Down Expand Up @@ -258,7 +258,7 @@ own floor is **Pester 6.0.0** as of psake/PowerShellBuild#172, matching what CI
| `Clear-PSBuildOutputFolder.tests.ps1` | Output directory removal |
| `Fixtures.tests.ps1` | The shared test fixture helpers themselves |
| `Get-PSBuildCertificate.tests.ps1` | Signing certificate resolution |
| `Get-PSBuildHelpLocale.tests.ps1` | Help locale resolution |
| `Get-PSBuildHelpLocale.tests.ps1` | Which docs subdirectories count as help locales |
| `Help.tests.ps1` | Help documentation completeness |
| `IBTasks.tests.ps1` | Invoke-Build task definitions and the settings they reference |
| `Initialize-PSBuild.tests.ps1` | Build environment initialization |
Expand All @@ -280,7 +280,8 @@ Supporting files: `tests/MetaFixers.psm1` (helpers for `Meta.tests.ps1`) and `te
- The workflow itself defines no jobs of its own — it delegates to the psake organization's
shared workflow, `psake/.github/.github/workflows/ModuleCI.yml@main`. Change the shared
workflow, not `test.yml`, to change what CI runs
- The shared workflow lints (cspell plus PSScriptAnalyzer fix suggestions), then runs
- The shared workflow lints (cspell plus PSScriptAnalyzer fix suggestions) and, in
parallel, runs
`./build.ps1 -Task Test -Bootstrap` across a `ubuntu-latest`, `windows-latest`, `macOS-latest`
matrix on PowerShell 7 and again on Windows PowerShell 5.1, and publishes the test results

Expand Down
Loading