8000 BED-5752 PIM Roles AZRoleApprover Post Integration & Testing by mvlipka · Pull Request #1615 · SpecterOps/BloodHound · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

BED-5752 PIM Roles AZRoleApprover Post Integration & Testing #1615

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 27, 2025

Conversation

mvlipka
Copy link
Contributor
@mvlipka mvlipka commented Jun 26, 2025

Description

This creates the test harness for post processing on the AZRoleApprover edge as well as calls the post processing function in azureAnalysis
During integration testing, we found a bug with the existing DAWGs query when the approvers slices wasn't set, that was also fixed

Motivation and Context

Resolves: BED-5752

How Has This Been Tested?

Integration tests were written to cover the edge creation

Screenshots (optional):

The harness was tested and written against the provided images:
image
image
image
image

Types of changes

  • Chore (a change that does not modify the application functionality)

Checklist:

Summary by CodeRabbit

  • New Features

    • Added comprehensive integration tests and test harness for Azure Privileged Identity Management (PIM) roles, including role approver relationships.
    • Introduced a new JSON configuration for visualizing and testing Azure PIM role eligibility and approval hierarchies.
    • Extended post-processing to include creation of Azure role approver edges and integrated their statistics.
  • Bug Fixes

    • Improved the logic for identifying Azure roles requiring approval, ensuring roles with non-null approver properties are correctly considered.
  • Chores

    • Enhanced test utilities with methods for creating Azure base entities and expanded test harness details for integration testing.

… slices in the graphdb, and call PIM Roles post
@mvlipka mvlipka self-assigned this Jun 26, 2025
@mvlipka mvlipka added the enhancement New feature or request label Jun 26, 2025
@mvlipka mvlipka marked this pull request as ready for review June 26, 2025 20:19
Copy link
Contributor
coderabbitai bot commented Jun 26, 2025

"""

Walkthrough

This update introduces the creation and testing of Azure Privileged Identity Management (PIM) role approver edges in the analysis pipeline. It adds new integration tests, a test harness, supporting JSON configuration, and modifies the post-processing logic to include the creation of AZRoleApprover edges. Query logic for approver edge creation is also simplified.

Changes

File(s) Change Summary
cmd/api/src/analysis/azure/pimroles_integration_test.go Added a new integration test for validating AZRoleApprover edge creation in Azure PIM roles.
cmd/api/src/analysis/azure/post.go Modified post-processing to call CreateAZRoleApproverEdge and merge its stats into the aggregate result.
cmd/api/src/test/integration/graph.go Added NewAzureBase method for creating Azure base nodes in test contexts.
cmd/api/src/test/integration/harnesses.go Introduced AZPIMRolesHarness struct and setup logic for Azure PIM role/approver integration tests.
cmd/api/src/test/integration/harnesses/AZPIMRolesHarness.json Added JSON configuration modeling Azure PIM roles and their approver relationships for integration testing.
packages/go/analysis/azure/role_approver.go Simplified query logic: now only checks for non-null approver properties, not for empty lists.
packages/go/analysis/azure/post.go Added AZRoleApprover to post-processed relationship kinds.
packages/go/ein/azure.go Added TODO comment about verifying edge creation logic for user approvers.

Sequence Diagram(s)

sequenceDiagram
    participant PostFunction as Post()
    participant Hybrid as hybrid.PostHybrid
    participant AzureAnalysis as azureAnalysis.CreateAZRoleApproverEdge
    participant Stats as AggregateStats

    PostFunction->>Hybrid: PostHybrid(...)
    Hybrid-->>PostFunction: HybridStats, err
    alt No error
        PostFunction->>AzureAnalysis: CreateAZRoleApproverEdge(...)
        AzureAnalysis-->>PostFunction: ApproverStats, err
        alt No error
            PostFunction->>Stats: Merge ApproverStats
            PostFunction-->>Caller: AggregateStats, nil
        else Error
            PostFunction-->>Caller: AggregateStats, err
        end
    else Error
        PostFunction-->>Caller: HybridStats, err
    end
Loadi 8000 ng

Possibly related PRs

  • SpecterOps/BloodHound#1561: Implements the initial creation of AZRoleApprover edges during post-processing, which this PR extends with additional tests and integration logic.

Suggested labels

api

Poem

In Azure clouds where roles reside,
Approvers now stand side by side.
With harness, test, and JSON bright,
Edges connect in graphs of light.
Queries trimmed, the logic flows—
The rabbit hops where PIM now grows!
🐇✨
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a68331d and 0771692.

📒 Files selected for processing (2)
  • packages/go/analysis/azure/post.go (1 hunks)
  • packages/go/ein/azure.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/go/ein/azure.go
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/go/analysis/azure/post.go (1)
packages/go/graphschema/azure/azure.go (1)
  • AZRoleApprover (80-80)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: build-ui
  • GitHub Check: run-analysis
  • GitHub Check: run-tests
🔇 Additional comments (1)
packages/go/analysis/azure/post.go (1)

135-135: LGTM! Correct addition of new relationship kind.

The addition of azure.AZRoleApprover to the post-processed relationships list follows the established pattern and is consistent with the other Azure relationship kinds.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor
@neumachen neumachen left a comment

Choose a reason for hiding this comment

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

The implementation looks good. I verified the implementation and the test as I paired with @mvlipka on this implementation and we went through the different test cases and they all were successful.

10000
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
cmd/api/src/analysis/azure/pimroles_integration_test.go (1)

21-79: Comprehensive integration test with detailed relationship verification.

The test properly validates the CreateAZRoleApproverEdge functionality with appropriate setup, execution, and verification phases. The specific assertions ensure the correct relationships are created between expected nodes.

Consider adding a helper function to centralize the expected mappings for better maintainability:

+// expectedMappings defines the expected start->end node relationships
+var expectedMappings = map[string][]string{
+	"AZBase n5":  {"PIMTestRole"},
+	"AZBase n7":  {"PIMTestRole", "PIMTestRole3"},
+	"AZBase n10": {"PIMTestRole3"},
+	// ... other mappings
+}

This would make the test more maintainable if the harness data changes in the future.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 839d2a0 and f5dabb8.

📒 Files selected for processing (6)
  • cmd/api/src/analysis/azure/pimroles_integration_test.go (1 hunks)
  • cmd/api/src/analysis/azure/post.go (1 hunks)
  • cmd/api/src/test/integration/graph.go (2 hunks)
  • cmd/api/src/test/integration/harnesses.go (2 hunks)
  • cmd/api/src/test/integration/harnesses/AZPIMRolesHarness.json (1 hunks)
  • packages/go/analysis/azure/role_approver.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/go/analysis/azure/role_approver.go (1)
packages/go/graphschema/azure/azure.go (2)
  • EndUserAssignmentUserApprovers (121-121)
  • EndUserAssignmentGroupApprovers (122-122)
cmd/api/src/test/integration/graph.go (4)
cmd/api/src/test/integration/harnesses/utils.go (1)
  • Node (38-44)
packages/go/graphschema/common/common.go (2)
  • Name (37-37)
  • ObjectID (36-36)
packages/go/graphschema/azure/azure.go (3)
  • TenantID (91-91)
  • License (109-109)
  • Entity (12-12)
packages/go/graphschema/ad/ad.go (1)
  • Entity (12-12)
cmd/api/src/analysis/azure/post.go (1)
packages/go/analysis/azure/post.go (1)
  • CreateAZRoleApproverEdge (955-977)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
🔇 Additional comments (8)
packages/go/analysis/azure/role_approver.go (1)

67-68: Simplified query logic for approver property checks.

The removal of empty list checks broadens the selection to include roles with non-null but potentially empty approver properties. This aligns with the bug fix mentioned in the PR objectives regarding DAWGs query issues when approver slices were not set.

cmd/api/src/test/integration/graph.go (2)

334-342: Well-implemented test utility method.

The NewAzureBase method follows the established pattern of other node creation methods and correctly maps the required properties for Azure base entities.


582-582: Minor formatting improvements for consistency.

Adding spaces after comment slashes improves readability and maintains consistent comment formatting throughout the file.

Also applies to: 585-585, 588-588

cmd/api/src/analysis/azure/post.go (1)

42-44: Proper integration into post-processing pipeline.

The addition of CreateAZRoleApproverEdge follows the established pattern for post-processing steps with appropriate error handling and statistics merging.

Also applies to: 50-50

cmd/api/src/test/integration/harnesses.go (3)

9866-9887: LGTM! Well-structured test harness definition.

The AZPIMRolesHarness struct is well-organized with clear separation between tenant, base nodes, and role nodes. The naming convention is consistent and descriptive.


9889-9965: LGTM! Comprehensive test data setup with good PIM configuration coverage.

The Setup method creates a thorough test scenario covering various PIM configurations:

  • Different approval requirements
  • Mixed user and group approvers
  • Various MFA, justification, and ticket requirements
  • Proper tenant-role relationships

This provides excellent test coverage for the AZRoleApprover edge creation functionality.


10078-10078: LGTM! Proper integration with harness system.

The harness is correctly added to the HarnessDetails struct, enabling its use in integration tests.

cmd/api/src/test/integration/harnesses/AZPIMRolesHarness.json (1)

140-149: Mixed list-vs-string encoding for approver fields – verify downstream parser

EndUserAssignmentUserApprovers / …GroupApprovers appear in three different shapes:

  1. Plain GUID string – e.g. line 144
  2. Comma-separated list without brackets – none in this file
  3. Bracketed comma-separated list stored as a single string – lines 278-279 & 332-333

Unless the post-processing code normalises these patterns, only one representation will be interpreted correctly. Please confirm what azureAnalysis/post.go expects; if it demands an array, emit JSON arrays instead:

-        "EndUserAssignmentUserApprovers": "[8793f86f-aa8c-459b-968d-617e4d1e7db8, … ]"
+        "EndUserAssignmentUserApprovers": [
+          "8793f86f-aa8c-459b-968d-617e4d1e7db8",
+          "7c48ffef-4f6e-4646-b261-9b41d3761127",
+          "b7a61883-d1d4-4197-8c40-cb258de60f58"
+        ]

This keeps the harness strictly valid JSON and avoids brittle string-splitting downstream.

Also applies to: 274-281, 328-336

Comment on lines +57 to +80
"id": "n0",
"position": {
"x": 320.152171827136,
"y": 368.5809253615741
},
"caption": "",
"style": {
"outside-position": "right"
},
"labels": [
"AZRole"
],
"properties": {
"name": "Global Administrator",
"objectid": "62e90394-69f5-4237-9190-012177145e10@6c12b0b0-b2cc-4a73-8252-0b94bfca2145",
"EndUserAssignmentRequiresApproval": "False",
"EndUserAssignmentRequiresCAPAuthenticationContext": "False",
"EndUserAssignmentUserApprovers": "",
"EndUserAssignmentGroupApprovers": "",
"EndUserAssignmentRequiresMFA": "True",
"EndUserAssignmentRequiresJustification": "True",
"EndUserAssignmentRequiresTicketInformation": "False"
}
},
Copy link
Contributor
@coderabbitai coderabbitai bot Jun 26, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Relationship IDs collide with node IDs – must be unique

Node n0 (lines 57-80) is re-used as the ID of the first relationship (lines 372-378).
Most loaders—including the BloodHound test harness helpers—expect the id field to be globally unique across all nodes and relationships. Re-using the same identifier can silently drop elements or blow up with “duplicate key”/“already exists” errors when the harness is ingested.

-  "id": "n0",               // relationship
+  "id": "r0",               // relationship – make all rel IDs distinct, e.g. r0…r13

Update every relationship block ("id": "n0" … "id": "n14") to use a prefix that cannot clash with node IDs.

Also applies to: 372-378

🤖 Prompt for AI Agents
In cmd/api/src/test/integration/harnesses/AZPIMRolesHarness.json between lines
57-80 and 372-378, the "id" field for nodes and relationships is duplicated,
causing conflicts. To fix this, update the "id" values for all relationship
blocks by adding a unique prefix (e.g., "rel_") to ensure they do not clash with
node IDs, making all IDs globally unique across nodes and relationships.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can be ignored as the json is just used as a reference for writing the Go test harness

Copy link
Contributor

Choose a reason for hiding this comment

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

@mvlipka, understood! Thank you for the clarification. You're absolutely right - this aligns with the existing knowledge that JSON files in the cmd/api/src/test/integration/harnesses/ directory are reference documents for writing Go test harnesses, not actual data files that get parsed by the application code. Since these files serve as visual/structural references rather than being ingested directly, the duplicate ID issue doesn't affect functionality.

I'll note this for future reviews of similar reference files in the harnesses directory.


🧠 Learnings used
Learnt from: mvlipka
PR: SpecterOps/BloodHound#1615
File: cmd/api/src/test/integration/harnesses/AZPIMRolesHarness.json:190-194
Timestamp: 2025-06-26T20:50:03.654Z
Learning: The JSON files in cmd/api/src/test/integration/harnesses/ directory (like AZPIMRolesHarness.json) are used as reference documents for writing Go test harnesses, not as actual data files that get parsed by the application code. Minor formatting issues like trailing spaces in these reference files don't affect functionality.

Learnt from: elikmiller
PR: SpecterOps/BloodHound#1563
File: packages/go/graphschema/azure/azure.go:24-24
Timestamp: 2025-06-06T23:12:14.181Z
Learning: In BloodHound, files in packages/go/graphschema/*/`*.go` are generated from CUE schemas. When `just prepare-for-codereview` is run, it triggers code generation that may automatically add import aliases or other formatting changes. These changes are legitimate outputs of the generation process, not manual edits that would be overwritten.

Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/upload/upload.go:36-67
Timestamp: 2025-05-27T17:30:55.548Z
Learning: In the BloodHound codebase, SaveIngestFile function should only handle file operations (save + validate) and should not populate RequestID/JobID fields, as these are properly handled by the API handler layer which has access to request context and URL parameters.

Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.

Learnt from: superlinkx
PR: SpecterOps/BloodHound#1606
File: .golangci.json:3-5
Timestamp: 2025-06-25T18:23:53.103Z
Learning: In the BloodHound project's .golangci.json, errcheck linter is intentionally disabled because it was too noisy, but the severity override settings are preserved for when it gets re-enabled in the future.

Comment on lines +190 to +194
"AZBase"
],
"properties": {
"objectid": "2c1b9e24-af0b-42e3-ac11-f70fe1f58e71 "
}
Copy link
Contributor
@coderabbitai coderabbitai bot Jun 26, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Trailing space in objectid will break look-ups

The objectid of node n7 ends with a space:

"objectid": "2c1b9e24-af0b-42e3-ac11-f70fe1f58e71 "

Many parsers do string-literal comparisons when mapping approvers ⇒ bases. That stray space makes the value unequal to the same GUID used elsewhere and will cause the approver edge (n5n14) to be skipped.

-        "objectid": "2c1b9e24-af0b-42e3-ac11-f70fe1f58e71 "
+        "objectid": "2c1b9e24-af0b-42e3-ac11-f70fe1f58e71"
🤖 Prompt for AI Agents
In cmd/api/src/test/integration/harnesses/AZPIMRolesHarness.json around lines
190 to 194, the "objectid" value has a trailing space that will cause string
comparison mismatches and break look-ups. Remove the trailing space from the
"objectid" string so it exactly matches the GUID used elsewhere, ensuring proper
mapping of approvers to bases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can be ignored as the json is just used as a reference for writing the Go test harness

Copy link
Contributor

Choose a reason for hiding this comment

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

@mvlipka Thanks for the clarification! I understand now that this JSON file serves as a reference document for writing the Go test harness rather than being parsed directly by the application code. That makes sense - the trailing space won't cause any issues since it's not being used for actual string lookups or comparisons.


✏️ Learnings added
Learnt from: mvlipka
PR: SpecterOps/BloodHound#1615
File: cmd/api/src/test/integration/harnesses/AZPIMRolesHarness.json:190-194
Timestamp: 2025-06-26T20:50:03.654Z
Learning: The JSON files in cmd/api/src/test/integration/harnesses/ directory (like AZPIMRolesHarness.json) are used as reference documents for writing Go test harnesses, not as actual data files that get parsed by the application code. Minor formatting issues like trailing spaces in these reference files don't affect functionality.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.

Learnt from: superlinkx
PR: SpecterOps/BloodHound#1606
File: .golangci.json:3-5
Timestamp: 2025-06-25T18:23:53.103Z
Learning: In the BloodHound project's .golangci.json, errcheck linter is intentionally disabled because it was too noisy, but the severity override settings are preserved for when it gets re-enabled in the future.

@mvlipka mvlipka merged commit 24fdc05 into main Jun 27, 2025
9 checks passed
@mvlipka mvlipka deleted the BED-5752/PIMRoles-AZRoleApprover-Integration branch June 27, 2025 17:03
@github-actions github-actions bot locked and limited conversation to collaborators Jun 27, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0