Skip to content

fix(azure): case-insensitive resource-ID parsing in panther_azureactivity_helpers - #2034

Merged
arielkr256 merged 1 commit into
panther-labs:developfrom
wired33:fix-azure-helper-case-sensitivity
May 1, 2026
Merged

arielkr256 merged 1 commit into
panther-labs:developfrom
wired33:fix-azure-helper-case-sensitivity

Conversation

@wired33

@wired33 wired33 commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Upstream PR: fix(azure): case-insensitive resource-ID parsing in panther_azureactivity_helpers

Target repo: panther-labs/panther-analysis
Base branch: develop
Feature branch: fix-azure-helper-case-sensitivity
Local commit: 5cab9323 (in panther-work/panther-analysis/)
File changed: global_helpers/panther_azureactivity_helpers.py
Lines: +13 / -9 (1 file)

Operator action to submit:

cd ~/work/scratch/claude/operations/team-dco/panther-work/panther-analysis
# Fork panther-labs/panther-analysis to your GitHub account, then:
git remote add fork git@github.com:<your-gh-user>/panther-analysis.git
git push -u fork fix-azure-helper-case-sensitivity
# Open PR via GitHub UI or `gh pr create` using the title and body below.

PR Title

fix(azure): case-insensitive resource-ID parsing in panther_azureactivity_helpers

PR Body

Summary

extract_resource_name_from_id() in global_helpers/panther_azureactivity_helpers.py uses parts.index(resource_type) to locate a named segment in an Azure resourceId path. list.index() is case-sensitive, but Azure Monitor Activity logs frequently deliver resourceId fully uppercased. For those events the helper silently returns the default (<UNKNOWN>) for every resource type it looks up — storageAccounts, resourceGroups, vaults, runbooks, and any other caller.

This breaks downstream enrichment (alert context, dedup keys, lookup-based suppression) on any detection that uses this helper against real-world uppercased resourceIds.

Evidence

Observed in production Azure Monitor Activity logs during our 2026-04-20 weekly detection review. Representative resourceId delivered by Azure:

/SUBSCRIPTIONS/12345678-1234-1234-1234-123456789abc/RESOURCEGROUPS/PROD-RG/PROVIDERS/MICROSOFT.STORAGE/STORAGEACCOUNTS/MYSTORAGE

Current behavior:

>>> extract_resource_name_from_id(uppercased_id, "storageAccounts")
'<UNKNOWN>'  # because "storageAccounts" != "STORAGEACCOUNTS"
>>> extract_resource_name_from_id(uppercased_id, "resourceGroups")
'<UNKNOWN>'  # because "resourceGroups" != "RESOURCEGROUPS"

Expected behavior:

>>> extract_resource_name_from_id(uppercased_id, "storageAccounts")
'MYSTORAGE'
>>> extract_resource_name_from_id(uppercased_id, "resourceGroups")
'PROD-RG'

Fix

Compare each path segment's .lower() against resource_type.lower() instead of relying on list.index(). The returned name is still the original (unmodified) value at parts[idx + 1], so downstream correlation against authoritative resource names continues to match the caller's expectations for case.

Contract compatibility

  • Same signature, same default behavior, same return type.
  • Mixed-case resourceIds (the common documented case) continue to work unchanged.
  • Previously-broken uppercased resourceIds now extract correctly.
  • Returned values preserve the source casing from resource_id (the fix is in the marker comparison, not the return).

Test plan

  • pipenv run panther_analysis_tool test --path global_helpers/ — existing global-helper tests still pass.
  • Add unit test with uppercased resourceId → extracts correctly.
  • Add unit test with mixed-case resourceId → still extracts correctly (regression guard).
  • Grep for callers of extract_resource_name_from_id — all now benefit without code change.

Downstream impact (reason we found it)

In our fork, we'd patched this helper locally and several Azure.MonitorActivity detections were consuming the patched version. Upstreaming this means we can eventually drop the fork of the helper itself. We still carry downstream-specific customizations of a few detection rules that will remain in our fork; this PR is just for the helper.

Related

  • Separate follow-up: the NSGModifiedOrDeleted detection has its own inline case-sensitive split("/resourceGroups/") (not using this helper). Fixed locally in our repo — may file a separate upstream PR if the detection is upstream-hosted.
…vity_helpers

Azure Monitor Activity logs frequently deliver resourceId fully uppercased,
e.g. "/SUBSCRIPTIONS/<id>/RESOURCEGROUPS/RG/PROVIDERS/MICROSOFT.STORAGE/
STORAGEACCOUNTS/ACCOUNT". The current implementation uses
`parts.index(resource_type)` which is case-sensitive: for uppercase segment
markers the function silently returns `<UNKNOWN>` for storageAccounts,
resourceGroups, vaults, runbooks, and every other resource_type it looks up.

This breaks downstream alert context enrichment, lookup-based suppression,
and dedup keys for any detection that relies on this helper.

Fix: iterate parts once comparing each segment's `.lower()` against
`resource_type.lower()`. The returned name is still the original (unlowered)
value at parts[idx+1], so downstream correlation against authoritative
resource names continues to match.

Contract-compatible: same signature, same default behavior, same return type.
Mixed-case resourceIds (the common case) continue to work unchanged; previously-
broken uppercased resourceIds now extract correctly.
@wired33
wired33 requested a review from a team as a code owner April 20, 2026 22:13
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@cursor

cursor Bot commented Apr 20, 2026 •

Copy link
Copy Markdown

PR Summary

Low Risk
Low risk: small, localized change to string parsing that only broadens matching for uppercased Azure resourceId paths while preserving returned casing.

Overview
Fixes extract_resource_name_from_id() to locate resource_type path segments case-insensitively, addressing Azure Activity Logs that emit fully uppercased resourceId values.

The helper now scans segments using lowercase comparisons (instead of list.index) while still returning the next segment with its original casing, and updates docstrings to document the behavior.

Reviewed by Cursor Bugbot for commit 5cab932. Bugbot is set up for automated code reviews on this repo. Configure here.

@arielkr256 arielkr256 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice catch, thank you!

@arielkr256
arielkr256 added this pull request to the merge queue May 1, 2026
@arielkr256 arielkr256 added the bug Something isn't working label May 1, 2026
Merged via the queue into panther-labs:develop with commit 48eb7cc May 1, 2026
12 of 14 checks passed
@arielkr256

Copy link
Copy Markdown
Contributor

@wired33 could you please sign the CLA? #2034 (comment)

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

Labels

bug Something isn't working

4 participants