Skip to main content

ADR-005: Platform-Only Finding Generation

Status

Accepted (2026-02-10)

Context

The Entra-ServiceNow connector had two detection modules — CrossServiceDetector and ExecutionChainDetector — totaling ~2,400 lines of detection logic. These produced findings directly in CLI reports and web dashboard output.

The platform evaluator (src/evaluator/) independently evaluates ingested entities against rules (orphaned_ownership, dormant_authority, scope_drift, privilege_justification_gap) to produce evidence-grade findings with immutable evidence packs.

This created duplicate detection: the same issue (e.g., orphaned ownership) was detected both by the connector's CrossServiceDetector and the platform's orphaned_ownership rule. The two implementations had different logic, different thresholds, and different output formats.

Problems with connector-side detection

  1. Duplication: Ownership, dormancy, and scope drift detected in two places with divergent logic
  2. No evidence packs: Connector detections were plain text strings — not evidence-grade, not immutable, not versioned
  3. No temporal tracking: Connector detections were point-in-time; platform evaluator tracks how findings change across sync versions
  4. Maintenance burden: Every new detection rule needed implementation in both the connector detector and the platform evaluator

Decision

All finding generation happens in the platform evaluator. Connectors are pure discovery + classification pipelines.

What connectors produce

  • Entity nodes with classification properties (egress_category, origin, ownership_status, risk_group, identity_binding_status)
  • Relationship edges with provenance (RUNS_AS, AUTHENTICATES_TO, OWNED_BY, TRIGGERS_ON, etc.)
  • Execution evidence nodes (sign-in data, flow execution counts)

What connectors do NOT produce

  • Findings (orphaned ownership, dormant authority, etc.)
  • Health classifications (critical, at_risk, needs_attention)
  • Detection reports with recommendations

What the platform evaluator handles

  • All finding types: orphaned_ownership, ownership_degraded, dormant_authority, scope_drift, privilege_justification_gap, unresolved_cross_system_auth
  • Evidence pack generation with immutable, timestamped evidence
  • Temporal tracking across sync versions
  • Severity classification based on entity properties and execution paths

CLI behavior change

  • CLI exit code 2 (critical issues found) is removed — detection is not the CLI's job
  • CLI reports are inventory-only: entities discovered, relationships mapped, properties classified
  • CLI exit codes: 0 (success), 1 (errors during scan)

Rationale

Why platform-only

  • Single source of truth: One implementation per detection rule, one set of thresholds, one output format
  • Evidence-grade: Platform findings have immutable evidence packs, temporal tracking, and version history
  • Testability: Detection rules are tested against the platform's entity model, not connector-specific dataclasses
  • Separation of concerns: Connectors discover and classify; the platform evaluates and explains

Gate criteria (verified before removal)

Before removing connector detectors, the following were verified:

Connector DetectorPlatform RuleStatus
CrossServiceDetector: NO_OWNER / DISABLED_OWNERorphaned_ownershipVerified — CREATED_BY fallback added
OwnershipDecayDetectorownership_degradedVerified — ownership_level property naming fixed
ExecutionChainDetector: dormant SPdormant_authorityVerified — RUNS_AS traversal in path materializer
ScopeDriftDetectorscope_driftVerified — HAS_ROLE version comparison
Shadow automation (unmatched client_id)unresolved_cross_system_authNew rule implemented with subtype guard

Accepted gap

  • INACTIVE_OWNER sign-in-based detection: The platform evaluator checks entity status (disabled, suspended) but not sign-in recency. An owner with status "active" but no sign-in for 6+ months is not detected. Documented as a known limitation for future improvement.

Consequences

  • Positive: ~2,400 lines of detection code removed from connector (detectors.py deleted entirely)
  • Positive: No more divergent detection logic between connector and platform
  • Positive: CLI pipeline is simpler — discovery, classification, transform, submit
  • Negative: CLI standalone mode loses issue detection capability (requires platform for findings)
  • Neutral: app.py (Flask dashboard) remains functional for inventory browsing but no longer shows detected issues