Azure / Azure/PSRule.Rules.Azure

Export-AzRuleData can exit silently with no output: unreachable context guard, StrictMode filter aborts all contexts, bare return

Open
#3,880 0 comments 1 reaction 0 assignees View on GitHub
bug feature: in-flight-export
Dominant language
PowerShell
Stars
447
Forks
109
Avg merge
2d 19h
Merged PRs (30d)
23

Description

## 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)

```powershell
$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:

```powershell
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:

```powershell
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)

```powershell
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

1. No usable Azure context, or one malformed context present
2. (2) discards all contexts and returns nothing
3. (1) cannot fire, so the actionable error is never surfaced
4. (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 @haflidif while testing against live Azure tenants, and diagnosed while investigating a downstream report at `martinopedal/azure-analyzer#1215`.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.