Summary
Three defects in PSRule.Rules.Azure.psm1 compose so that Export-AzRuleData can exit successfully, silently, and with no output when the Azure context is missing or partially malformed. Downstream that becomes a clean, empty scan — a false negative on a security tool, which is the worst failure mode for this kind of check.
Verified against main (src/PSRule.Rules.Azure/PSRule.Rules.Azure.psm1) at time of filing. Line numbers are from that file.
1. Unreachable guard in FindAzureContext (line 703)
$context = @(GetAzureContext -ListAvailable:$listAvailable);
if ($Null -eq $context -and $context.Length -gt 0) {
Write-Error -Message 'Could not find an existing context. Use Connect-AzAccount to establish a PowerShell context with Azure.';
return;
}
$context is wrapped in @(...), so it is always an array and never $null. $Null -eq $context is therefore always $false, -and short-circuits, and the body is unreachable. The condition is also self-contradictory as written: it requires $context to be null and to have a positive length.
The actionable "Use Connect-AzAccount" error can never be emitted. The intent looks like:
if ($Null -eq $context -or $context.Length -eq 0) {
2. Whole filter pipeline inside one try, under Set-StrictMode -Version latest (lines 708-726)
The module sets Set-StrictMode -Version latest at line 8. FindAzureContext then does:
try {
$filteredContext = @($context | ForEach-Object -Process {
if (
($Null -eq $Tenant -or $Tenant.Length -eq 0 -or ($_.Tenant.Id -in $Tenant)) -and
($Null -eq $Subscription -or $Subscription.Length -eq 0 -or ($_.Subscription.Id -in $Subscription) -or ($_.Subscription.Name -in $Subscription))
) {
$_;
}
})
...
return $filteredContext;
}
catch {
Write-Error -Message "Failed to filter contexts. Error: $_";
}
Under StrictMode, a single context missing .Tenant or .Subscription throws on property access. Because the try spans the entire pipeline and return $filteredContext sits inside it, that one bad context causes every context to be discarded — the function falls into catch and returns nothing at all, rather than skipping the offending entry.
Filtering per-item, or narrowing the try, would let one malformed context be skipped without destroying the rest.
3. Silent bare return in Export-AzRuleData (lines 86-88)
if ($Null -eq $contextSubscriptions -or $contextSubscriptions.Length -eq 0) {
return;
}
Nothing is written to the error or warning stream. Combined with (1) and (2), the caller gets a successful exit code and an empty output directory with no diagnostic anywhere.
Combined effect
- No usable Azure context, or one malformed context present
- (2) discards all contexts and returns nothing
- (1) cannot fire, so the actionable error is never surfaced
- (3) returns silently, exit code 0, no output
Result: Export-AzRuleData reports success and exports nothing, and any downstream PSRule scan over that directory reports a clean result.
Impact
We hit this in a downstream tool that calls Export-AzRuleData and then scans the export directory. Every subscription scan came back clean and empty, with no error at any layer, which is indistinguishable from "no findings". For a security-assessment tool a silent false negative is materially worse than a loud failure.
Suggested fixes
- Correct the guard to
-or ... -eq 0 so it can actually fire
- Filter contexts per-item so one malformed entry does not discard the whole set, or use StrictMode-safe property access in the filter predicate
- Replace the bare
return with a Write-Error (or at minimum Write-Warning) so an empty export is distinguishable from a clean one
Happy to open a PR if the maintainers would like these as one change or three.
Credit
Found by Haflidi Fridthjofsson (@haflidif) while testing against live Azure tenants, and diagnosed while investigating a downstream report at martinopedal/azure-analyzer#1215.
Summary
Three defects in
PSRule.Rules.Azure.psm1compose so thatExport-AzRuleDatacan exit successfully, silently, and with no output when the Azure context is missing or partially malformed. Downstream that becomes a clean, empty scan — a false negative on a security tool, which is the worst failure mode for this kind of check.Verified against
main(src/PSRule.Rules.Azure/PSRule.Rules.Azure.psm1) at time of filing. Line numbers are from that file.1. Unreachable guard in
FindAzureContext(line 703)$contextis wrapped in@(...), so it is always an array and never$null.$Null -eq $contextis therefore always$false,-andshort-circuits, and the body is unreachable. The condition is also self-contradictory as written: it requires$contextto be null and to have a positive length.The actionable "Use Connect-AzAccount" error can never be emitted. The intent looks like:
2. Whole filter pipeline inside one
try, underSet-StrictMode -Version latest(lines 708-726)The module sets
Set-StrictMode -Version latestat line 8.FindAzureContextthen does:Under StrictMode, a single context missing
.Tenantor.Subscriptionthrows on property access. Because thetryspans the entire pipeline andreturn $filteredContextsits inside it, that one bad context causes every context to be discarded — the function falls intocatchand returns nothing at all, rather than skipping the offending entry.Filtering per-item, or narrowing the
try, would let one malformed context be skipped without destroying the rest.3. Silent bare
returninExport-AzRuleData(lines 86-88)Nothing is written to the error or warning stream. Combined with (1) and (2), the caller gets a successful exit code and an empty output directory with no diagnostic anywhere.
Combined effect
Result:
Export-AzRuleDatareports success and exports nothing, and any downstream PSRule scan over that directory reports a clean result.Impact
We hit this in a downstream tool that calls
Export-AzRuleDataand then scans the export directory. Every subscription scan came back clean and empty, with no error at any layer, which is indistinguishable from "no findings". For a security-assessment tool a silent false negative is materially worse than a loud failure.Suggested fixes
-or ... -eq 0so it can actually firereturnwith aWrite-Error(or at minimumWrite-Warning) so an empty export is distinguishable from a clean oneHappy to open a PR if the maintainers would like these as one change or three.
Credit
Found by Haflidi Fridthjofsson (@haflidif) while testing against live Azure tenants, and diagnosed while investigating a downstream report at
martinopedal/azure-analyzer#1215.