Skip to content

feat(scanner): migrate storage rules AZ-STOR-001 to 010 to evaluate() (#370) - #382

Open
parthrohit22 wants to merge 1 commit into
feat/369-evaluate-contract-foundationfrom
feat/370-evaluate-storage-rules
Open

parthrohit22 wants to merge 1 commit into
feat/369-evaluate-contract-foundationfrom
feat/370-evaluate-storage-rules

Conversation

@parthrohit22

Copy link
Copy Markdown
Collaborator

What does this PR do?

Migrates the ten storage rules AZ-STOR-001 to AZ-STOR-010 to the evaluate() coverage contract, so each reports a status for every resource it checks, PASS included, instead of only listing violations.

Stacked on #381. This PR targets feat/369-evaluate-contract-foundation, so the diff shows only the storage changes. Once #381 merges, I'll rebase onto dev and retarget.

What changes per rule

Rule Evaluated per Paths that were silently skipped and now get an explicit status
AZ-STOR-001 public blob access account unset value → UNKNOWN / MISSING_PROPERTIES
AZ-STOR-002 HTTPS only account field missing → UNKNOWN / MISSING_PROPERTIES (None still fails, as before)
AZ-STOR-003 lifecycle policy account policy unreadable → UNKNOWN / EVIDENCE_UNAVAILABLE; no ID / name / resource group → UNKNOWN / MISSING_PROPERTIES
AZ-STOR-004 diagnostic logging blob / queue / table sub-service logging unreadable → UNKNOWN / EVIDENCE_UNAVAILABLE
AZ-STOR-005 geo-redundancy account no SKU → UNKNOWN / MISSING_PROPERTIES
AZ-STOR-006 shared key account None still fails (documented Azure default)
AZ-STOR-007 minimum TLS account unrecognised value → UNKNOWN / UNRECOGNIZED_TLS_VERSION
AZ-STOR-008 customer-managed key account untagged → NOT_APPLICABLE / POLICY_NOT_REQUIRED; exception → APPROVED_EXCEPTION; no key source → UNKNOWN; unknown source → UNKNOWN / UNRECOGNIZED_KEY_SOURCE
AZ-STOR-009 blob immutability blob container not required → POLICY_NOT_REQUIRED / APPROVED_EXCEPTION; containers unreadable → UNKNOWN; no containers → NOT_APPLICABLE / NO_RESOURCES_FOUND
AZ-STOR-010 private endpoint account public access disabled → NOT_APPLICABLE / PUBLIC_ACCESS_DISABLED; connections unavailable → UNKNOWN / EVIDENCE_UNAVAILABLE

Every rule also:

  • reports ERROR / INVENTORY_UNAVAILABLE when the storage account list call fails;
  • reports NOT_APPLICABLE / NO_RESOURCES_FOUND when the list is empty;
  • records the inspected value as evidence;
  • keeps scan() as a thin wrapper over the FAIL evaluations.

Shared helpers

Findings are unchanged, with one intentional exception

I recorded every rule's scan() output before and after the change over a fixture of nine accounts covering every branch: SDK enums, None and missing fields, a missing resource group, exception tags, nested container properties, and empty and failed container lists. The output is identical and in the same order, with one exception:

  • AZ-STOR-006 used to emit a finding with resource_id: "" for an account returned without an ID. Such a finding cannot be remediated and collides with every other ID-less finding. That account is now UNKNOWN / MISSING_PROPERTIES with no finding. Azure always returns an ID, so this only affects malformed input.

Type of change

  • New scan rule
  • Remediation playbook
  • Bug fix
  • Dashboard/front-end work
  • API endpoint
  • Documentation
  • Compliance mapping

Also a scanner change: storage rules migrated to the evaluation contract.

Rule details (if applicable)

  • Rule ID: AZ-STOR-001 to AZ-STOR-010
  • Severity: unchanged
  • Category: Storage
  • Frameworks mapped: unchanged

Testing

  • Tested against a real Azure free trial subscription (not needed: detection logic unchanged; verified offline with before/after findings comparison)
  • Returns correct JSON output
  • All seven CI checks pass (pending CI)
  • No hardcoded credentials or secrets

Local results:

  • All 28 existing storage tests pass unmodified.
  • 12 new branch tests in tests/test_rules_storage.py.
  • All ten rules are registered in tests/test_rule_evaluation_contract.py, which checks inventory failure, empty inventory, finding shape and scan() parity.
  • CI's rule-structure validation passes for all 144 rules.
  • ruff check . and ruff format --check . are clean.
  • Full suite: no new failures compared with dev. The local failures are the Postgres-backed tests CI runs against its ephemeral database.

Related issue

Closes #370

Part of #380. Depends on #381.

Checklist

  • Every commit includes a DCO Signed-off-by trailer (git commit -s; see docs/dco.md)
  • My code follows the rule template in CONTRIBUTING.md
  • I added or updated the matching CLI playbook (not applicable: no remediation change)
  • I added or updated all four compliance framework mappings (not applicable: mappings unchanged)
  • I have not committed any real Azure credentials
  • My branch name follows the convention: feat/description

@parthrohit22 parthrohit22 added enhancement New feature or request core Core team ownership not for students roadmap Planned feature track, not a current bug python Pull requests that update python code labels Oct 4, 2026
@parthrohit22 parthrohit22 self-assigned this Oct 4, 2026

@TFT444 TFT444 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storage rules migration looks clean. scan() parity confirmed for all 10 rules, the intentional AZ-STOR-006 empty-resource-id change is correctly handled and tested, contract test and branch tests cover every UNKNOWN/MISSING_PROPERTIES boundary, and there are no security concerns.

Important: this PR is stacked on #381 and must NOT merge before #381 merges. Please rebase onto dev and retarget to dev once #381 lands. Approving now so the review is on record.

@parthrohit22
parthrohit22 force-pushed the feat/370-evaluate-storage-rules branch 2 times, most recently from dd9a4be to 737164a Compare October 4, 2026 02:12
…#370)

Each storage rule now reports PASS, FAIL, UNKNOWN, ERROR or
NOT_APPLICABLE for every account (per sub-service for AZ-STOR-004 and
per blob container for AZ-STOR-009), records the inspected value as
evidence, and reports a failed storage inventory as ERROR. Paths that
scan() skipped silently now carry explicit reason codes.

Findings are unchanged, except that AZ-STOR-006 no longer emits a
finding with an empty resource_id for an account returned without an
ID; that account is reported as UNKNOWN.

Add policy_exemption() and missing_resource_id() helpers and register
the storage rules in the evaluation contract test.

Signed-off-by: parthrohit22 <parthrohit60@gmail.com>
@parthrohit22
parthrohit22 force-pushed the feat/370-evaluate-storage-rules branch from 737164a to adf8938 Compare October 4, 2026 09:57

@ritiksah141 ritiksah141 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes on process, not on the code. The storage migration itself looks solid: I reviewed the full diff at head adf8938 locally and ran the suites (contract and storage tests 94/94, full suite 1598 passed / 93 skipped / 0 failed, ruff check and format clean). Findings parity holds for all ten rules, the previously silent skip paths now report explicit statuses, the per-sub-service (AZ-STOR-004) and per-container (AZ-STOR-009) evaluations are correct, and the policy_exemption() refactor preserves the old policy_required() semantics exactly.

Blockers:

  1. This PR targets feat/369-evaluate-contract-foundation and #381 has not merged. Merging #382 as-is would push the storage commits into #381's branch and expand that PR beyond what was reviewed and approved there. The PR body itself promises a rebase onto dev and a retarget once #381 merges; please hold until then (#381 still has an outstanding CHANGELOG fix requested in review).

  2. There is no CI on this PR. The pull_request trigger in ci.yml only fires when the base is dev or main, so the current head has zero automated validation on GitHub. Retargeting onto dev fixes this; please have the checks green before re-requesting review.

  3. The existing approval is stale: it points at commit 3cce17d, the head from before the 2026-10-04 rebase, so adf8938 has not actually been reviewed. After the retarget it needs a fresh approval.

Minor items to fold into the same pass, none blocking on their own:

  • The CHANGELOG entry says findings are unchanged "except that AZ-STOR-006 no longer emits a finding with an empty resource_id", but the same change applies to AZ-STOR-001, 007, 008 and 010, whose old scan() paths also used account.id directly in findings. Suggest rewording to say any account returned without an ID is now reported as UNKNOWN/MISSING_PROPERTIES instead of producing an empty-resource_id finding.
  • In az_stor_003, az_stor_004 and az_stor_005 the UNKNOWN branches do not attach the evidence dict they compute (for example {"lifecycle_policy": None}), while the PASS and FAIL branches do. Consider recording the inspected value on UNKNOWN rows too, for consistency with the evidence contract.
  • UNRECOGNIZED_TLS_VERSION, UNRECOGNIZED_KEY_SOURCE and PUBLIC_ACCESS_DISABLED are inline string literals. Fine for rule-specific codes, but module-level constants would keep them greppable next to the shared codes in scanner/evaluation.py.

Once #381 lands and this is retargeted onto dev with CI green and a fresh review, this should merge quickly.

@SHAURYAKSHARMA24 SHAURYAKSHARMA24 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran this head (adf8938) locally. With the storage SDK client raising a 403, a 429 or an expired-credential error, all ten storage rules come back as ERROR/INVENTORY_UNAVAILABLE and show up in failed_rule_ids, and an empty subscription gives NOT_APPLICABLE/NO_RESOURCES_FOUND. In a mixed run (Key Vault fine, storage 403) the Key Vault FAIL still comes through next to the storage errors, which is what we want. Full suite matches dev apart from the same two Windows-only failures.

Agree with ritiksah141 on the process side: this should wait for #381, get retargeted to dev so CI actually runs, and then needs a fresh approval on the final head. I'll take another look after the retarget.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core team ownership not for students enhancement New feature or request python Pull requests that update python code roadmap Planned feature track, not a current bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants