Skip to content

feat(identity-protection): surface compromised/weak password risk#65

Open
leet-c1 wants to merge 3 commits into
leet/shadow-mcp-detectionfrom
leet/idp-compromised-password
Open

feat(identity-protection): surface compromised/weak password risk#65
leet-c1 wants to merge 3 commits into
leet/shadow-mcp-detectionfrom
leet/idp-compromised-password

Conversation

@leet-c1

@leet-c1 leet-c1 commented Jul 22, 2026

Copy link
Copy Markdown

Summary

Surfaces per-account password risk from CrowdStrike Falcon Identity Protection as risk factors on the existing security_insight resource. Directly answers the "flag users with compromised passwords" use case.

Stacked on #64 (leet/shadow-mcp-detection) — base branch is #64, not main. Review/merge #64 first.

What it does

  • Extends the Identity Protection GraphQL entities query to request passwordAttributes { exposed strength } on each user account descriptor fragment.
  • Captures it as PasswordAttributes on AccountData.
  • Maps it to risk factors on the account's security insight:
    • exposed (compromised/breached) → EXPOSED_PASSWORD (HIGH)
    • strength == WEAKWEAK_PASSWORD (MEDIUM)
  • Entity-level risk factors and the normalized risk score are unchanged; password factors are additive, per account.

Why the field names differ from the ticket

The referenced doc suggested assessment { compromisedPassword } and securityAssessmentTypes:[COMPROMISED_PASSWORD]. Those don't exist in the live Identity Protection GraphQL schema (verified via introspection — the Entity type has no such field, and there is no COMPROMISED_PASSWORD enum value). The real signal is passwordAttributes.exposed (Boolean) + passwordAttributes.strength (PasswordStrength: UNKNOWN/WEAK/STRONG) on the account descriptors. I built against the introspected schema and confirmed the field is live and populated against a licensed tenant.

Scope / representation

  • No new scopes — rides the same Identity Protection GraphQL query (Identity Protection Entities: Read + GraphQL: Write, already documented).
  • No new resource type — enriches the existing security_insight, so no capability-metadata change (verified: make generate produces no diff).
  • Docs: added a note to the "Enable risk score ingestion" section of docs/connector.mdx.

Validation

  • go build ./..., go test ./... pass; new TestPasswordRiskFactors (mapping table) + TestSecurityInsightResourcePasswordSignal (asserts EXPOSED_PASSWORD lands on the built insight).
  • golangci-lint (v2.11.4, matching CI): 0 issues.
  • GraphQL query change live-verified against the tenant (parses on all four descriptor fragments; passwordAttributes populated).

🤖 Generated with Claude Code

@leet-c1
leet-c1 force-pushed the leet/idp-compromised-password branch 4 times, most recently from a17bdec to e45f5d9 Compare July 22, 2026 22:13
leet-c1 and others added 3 commits July 22, 2026 22:18
Extend the Identity Protection GraphQL query and security_insight mapping to
surface per-account password risk from CrowdStrike Falcon Identity Protection:

- Query: request `passwordAttributes { exposed strength }` on each user account
  descriptor fragment in the entities query.
- Model: capture it as PasswordAttributes on AccountData.
- Map: an exposed (compromised/breached) password becomes a high-severity
  EXPOSED_PASSWORD risk factor on the account's security insight; a weak
  password becomes a medium-severity WEAK_PASSWORD factor. Entity-level risk
  factors are unchanged; password factors are additive per account.

Verified against the live Identity Protection GraphQL schema (introspection):
the doc-suggested `assessment { compromisedPassword }` / COMPROMISED_PASSWORD
do not exist; the real signal is `passwordAttributes.exposed` (Boolean) +
`strength` (PasswordStrength: UNKNOWN|WEAK|STRONG) on the account descriptors,
confirmed populated live. No new scopes (same Identity Protection GraphQL
query) and no new resource type — enriches the existing security_insight.

Tests: passwordRiskFactors mapping (exposed/weak/none/case-insensitive) and a
resource-level assertion that EXPOSED_PASSWORD lands on the built insight.

Stacked on #64 (leet/shadow-mcp-detection).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Password risk ingestion is now gated behind a new config flag,
`crowdstrike-ingest-password-risk` (bool, default false):

- When OFF (default): the entities query does NOT request passwordAttributes,
  so no password data is fetched and no EXPOSED_PASSWORD/WEAK_PASSWORD risk
  factors are emitted — behavior is identical to before this capability.
- When ON: the passwordAttributes selection is injected into each account
  descriptor fragment and the factors are surfaced on the security insight.

Implementation: the query is built from a template with a single injection
point per fragment (buildIdentityRiskQuery), threaded from config →
Connector → securityInsightBuilder → IdentityProtectionClient. The mapping
(passwordRiskFactors) is already nil-safe, so gating the query alone gates
emission. Shadow-MCP identity resolution (newMCPSource) passes false — it
never needs password risk.

Docs updated to describe the opt-in setting; conf.gen.go regenerated.
Adds TestBuildIdentityRiskQuery (asserts the flag includes/excludes the
selection). Lint (golangci-lint v2.11.4, CI-matched): 0 issues.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The new crowdstrike-ingest-password-risk config field changes the connector's
config schema; regenerate the committed metadata (validate_metadata Check 1).
baton_capabilities.json is unchanged (no new resource type/capability).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@leet-c1
leet-c1 force-pushed the leet/idp-compromised-password branch from e45f5d9 to 1919973 Compare July 22, 2026 22:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant