TenantAtlas/specs/424-security-defaults-content-backed-comparable-support/implementation-report.md
ahmido 2cd512915a feat: complete spec 424 security defaults content-backed comparable support (#491)
Implements spec 424 with comparable renderable capture/readiness changes and supporting tests/spec artifacts.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #491
2026-07-01 14:41:24 +00:00

15 KiB

Implementation Report: Spec 424 - Security Defaults Content-Backed Comparable Support

Preflight

  • Active spec: specs/424-security-defaults-content-backed-comparable-support/
  • Implementation start: 2026-06-30 19:22:46 CEST
  • Branch: 424-security-defaults-content-backed-comparable-support
  • HEAD: c49784b3 feat: complete spec 423 security compliance readiness pack (#490)
  • Initial dirty state: untracked active spec directory only.
  • Activated skills: spec-kit-implementation-loop, pest-testing, .agent/workflows/spec-readiness-gate, .agent/repo-contracts/workspace-scope-safety, .agent/repo-contracts/rbac-action-safety, .agent/repo-contracts/operation-run-truth, .agent/repo-contracts/evidence-anchor-contract, .agent/repo-contracts/provider-freshness-semantics, .agent/repo-contracts/product-surface-gate.
  • Hard-gate stop conditions checked: no unrelated dirty files; no completed-spec rewrite; no new schema/table/persisted enum/status family; no new route/navigation/dashboard/action/customer output; no restore/apply/certification/Review Pack/report/export scope; no direct HTTP, Graph SDK bypass, runtime docs fetch, or broad singleton framework; no tenant_id ownership path. Existing fresh-install seed alignment is allowed only for the already-identified one-row securityDefaults entry in 2026_06_26_000419_expand_tenant_configuration_workloads.php.

Completed-Spec Guardrail

Specs 414, 415, 417, 418, 419, 420, 421, and 423 are read-only dependency context. No completed historical spec files are edited.

Preflight Evidence

  • securityDefaults currently exists as a registry-only/out-of-scope TCM planning row in ResourceTypeRegistry and the fresh-install workload expansion migration.
  • CoverageSourceContractResolver has no securityDefaults mapping before this spec.
  • config/graph_contracts.php has no Security Defaults contract before this spec.
  • CoverageIdentityStrategyRegistry has no securityDefaults identity strategy before this spec.
  • EntraComparablePayloadNormalizer supports Conditional Access before this spec and not Security Defaults.
  • GenericContentEvidenceCaptureService already accepts singleton Graph payloads that contain an id, so no broad singleton capture framework is planned.
  • Official Microsoft Graph documentation confirms the Security Defaults read endpoint as GET /policies/identitySecurityDefaultsEnforcementPolicy, the resource fields id, displayName, description, and isEnabled, and least-privileged read permission Policy.Read.All.

Implementation Summary

  • Added securityDefaults as an explicit Coverage v2 source contract in apps/platform/config/graph_contracts.php using /policies/identitySecurityDefaultsEnforcementPolicy, graph_version = v1.0, response_shape = singleton, safe select fields, singleton-safe volatile fields, and Policy.Read.All.
  • Added securityDefaults to CoverageSourceContractResolver explicit mappings; missing contract resource still blocks as capture_blocked_missing_contract.
  • Added request-local Graph version handling in MicrosoftGraphClient::listPolicies() so this contract calls the v1.0 endpoint without changing global Graph defaults or bypassing GraphClientInterface.
  • Updated MicrosoftGraphClient, Security Defaults capture, and graph:contract:check singleton probes so they use the contract-local v1.0 endpoint and do not send $top to /policies/identitySecurityDefaultsEnforcementPolicy; the endpoint remains constrained to the contract $select.
  • Promoted only the securityDefaults registry row to active graph_v1_fallback / fallback_supported / internal-only / non-restorable support in ResourceTypeRegistry, and aligned the one existing fresh-install migration seed row allowed by the amended plan.
  • Added the idempotent tenant-configuration:sync-defaults deploy command to run the existing resource-type and supported-scope default sync paths on already-migrated environments.
  • Added a bounded identity strategy requiring a stable Graph id; display-name-only and sourceId-only Security Defaults payloads remain missing_external_id.
  • Extended existing Entra typed helpers for Security Defaults normalization, critical enabled-state compare, and safe render summaries. No Blade, Filament Resource/Page/Widget, route, navigation, dashboard, action, export, report, Review Pack, restore, certify, or customer output was added.
  • Hardened ClaimGuard so Security Defaults-specific claims are treated as workload claims: selected internal/operator comparable/renderable wording is allowed, while certification, restore, customer-ready, full, 100 percent, M365 certified, and Review Pack wording is blocked.

Source Contract And Capture Matrix

Area Result
Contract key securityDefaults
Endpoint /policies/identitySecurityDefaultsEnforcementPolicy
Version v1.0 via contract-local graph_version
Response shape singleton; GraphClientInterface list calls, capture, and live contract probes use v1.0 and omit $top
Permission proof Policy.Read.All recorded as the least-privileged read permission
Capture path Existing GenericContentEvidenceCaptureService; singleton payloads with id are already captured without new framework code
Missing contract Blocks as capture_blocked_missing_contract / missing_graph_contract_resource; no fake evidence
Permission block 403 response maps to capture_blocked_permission; no fake evidence
Scope safety Provider connection and OperationRun target-scope mismatches fail before provider work
Registry posture one active graph_v1_fallback row; old TCM planning row is inactive after ResourceTypeRegistry::syncDefaults()
Fresh-install seed Existing migration 2026_06_26_000419_expand_tenant_configuration_workloads.php aligned for only the securityDefaults row
Existing DB sync run cd apps/platform && php artisan tenant-configuration:sync-defaults after deployment and before Security Defaults capture

Identity, Normalize, Compare, And Render Matrix

Area Result
Identity graph.security_defaults.v1, stable graph_object_id only when id is present
Missing identity display-name-only and sourceId-only payloads resolve to missing_external_id; no stable claim
Normalized fields display_name, enabled, enabled_state, source_identity.id, diagnostics
Volatile fields @odata.context and @odata.etag ignored as volatile
Compare enabled and enabled-state changes are critical material changes; redacted/unsupported diagnostics are informational
Render existing Coverage v2 inspector shows Security Defaults display name, enabled state, evidence state, identity state, claim state, last captured, compare summary, and redaction diagnostics
Read model Existing Entra dispatch path supports Security Defaults; no new generic registry/framework

Claim Guard, Redaction, And Scope Proof

  • Claim Guard allows only selected internal/operator Security Defaults comparable/renderable/ready-for-operator-review wording.
  • Claim Guard blocks Security Defaults certified, restore-ready, customer-ready, Review Pack, all/full, 100 percent, and M365 certification wording.
  • CoveragePayloadRedactor was reused; no redactor extension was required. Tests prove secret values are redacted from normalized payloads, render summaries, and compare results.
  • Existing Coverage v2 owner/workspace/managed-environment/provider connection scoping remains authoritative; no tenant_id ownership path was added.
  • SupportedScopeResolver was not changed. No certified, restore-ready, customer-ready, full Entra, or broad M365 scope names were added.

Product Surface Close-Out

  • No-legacy posture: no legacy path or compatibility exception; this is a canonical Coverage v2 extension.
  • Product Surface Impact: existing Coverage v2 readiness/inspect surface can render Security Defaults summaries when renderable content-backed evidence exists.
  • UI Surface Impact: no route, navigation item, dashboard, action, table, Filament Resource/Page/Widget, provider registration, or Blade file changed. Existing inspector content changes only for Security Defaults renderable evidence.
  • Page archetype: Technical Annex / internal operator evidence inspection.
  • Surface budgets: decision-first status remains Coverage/Evidence/Identity/Claim badges; Security Defaults summary adds compact summary_fields; diagnostics remain secondary; raw/support details remain collapsed under existing technical details.
  • Technical Annex / deep-link demotion: source endpoint, contract key, schema hash, canonical key, operation link, and source class remain in existing technical details, not default-visible summary fields.
  • Canonical status vocabulary: existing Coverage v2 states only (renderable, content_backed, stable, internal_only, blockers where applicable).
  • Product Surface exceptions: none.
  • Focused browser proof: php artisan test --compact tests/Browser/Spec424SecurityDefaultsComparableRenderableOperatorSurfaceSmokeTest.php passed, proving rendered Security Defaults inspect output, compare summary, redaction badges, no raw/secrets/source endpoint default exposure, no restore/certified/customer-ready wording, no new high-impact action, no remote Graph/TCM/provider resource calls, and no JavaScript/console errors.
  • Human Product Sanity result: pass. An internal operator can see that Security Defaults is enabled and materially changed versus prior evidence without seeing raw payloads, secrets, source endpoints, or overclaim wording.
  • Visible complexity outcome: neutral. The existing inspector hierarchy is reused; no nested cards, new actions, new navigation, or new page structure.

Filament v5 Output Contract

  • Livewire v4.0+ compliance: unchanged; platform remains Filament v5 on Livewire v4 and the focused browser smoke exercises the existing Filament surface.
  • Provider registration location: unchanged; Laravel 12 provider registration remains in apps/platform/bootstrap/providers.php.
  • Global search: unchanged; no Filament Resource was added or made globally searchable.
  • Destructive/high-impact actions: none added or changed. No restore/apply/capture-start/export/certify action was introduced.
  • Asset strategy: no assets registered and no new frontend bundle or filament:assets deployment requirement introduced.
  • Testing plan coverage: unit tests cover contract, graph version URL, singleton no-$top probes, identity, normalization, compare, render, and claims; feature tests cover registry, deploy sync command, capture, permission/scope/RBAC/no-remote/no-mini-platform; browser smoke covers the existing rendered inspector.

Validation

  • Sail attempts:
    • cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec424 -> interrupted after about two minutes with no output because Sail/Docker exec did not progress in this session.
    • cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration/Spec424SecurityDefaultsSourceContractTest.php tests/Unit/Support/TenantConfiguration/Spec424SecurityDefaultsTypedSemanticsTest.php tests/Feature/TenantConfiguration/Spec424SecurityDefaultsCaptureReadinessTest.php -> interrupted after about one minute with no output for the same reason.
  • Local fallback validation:
    • cd apps/platform && php artisan list --raw | rg '^tenant-configuration:sync-defaults' -> passed; command is registered.
    • cd apps/platform && php artisan test --compact tests/Unit/Support/TenantConfiguration/Spec424SecurityDefaultsSourceContractTest.php tests/Unit/Support/TenantConfiguration/Spec424SecurityDefaultsTypedSemanticsTest.php tests/Feature/TenantConfiguration/Spec424SecurityDefaultsCaptureReadinessTest.php -> passed, 32 tests / 206 assertions.
    • cd apps/platform && php artisan test --compact tests/Browser/Spec424SecurityDefaultsComparableRenderableOperatorSurfaceSmokeTest.php -> passed, 1 test / 46 assertions.
    • cd apps/platform && php artisan test --compact tests/Feature/TenantConfiguration/Spec421EntraCoverageLevelPromotionTest.php tests/Feature/TenantConfiguration/Spec419M365RegistryExpansionTest.php tests/Unit/Support/TenantConfiguration/Spec419M365WorkloadRegistryTest.php -> passed, 11 tests / 814 assertions, 1 skipped.
    • cd apps/platform && php artisan test --compact --filter=ClaimGuard -> passed, 110 tests / 123 assertions.
    • cd apps/platform && php artisan test --compact tests/Feature/TenantConfiguration/Spec420M365GenericEvidenceCaptureTest.php tests/Feature/TenantConfiguration/Spec421EntraComparableRenderableTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraConditionalAccessNormalizerTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraComparableDiffTest.php tests/Unit/Support/TenantConfiguration/Spec421EntraRenderableSummaryTest.php -> passed, 14 tests / 120 assertions.
    • cd apps/platform && php artisan test --compact tests/Feature/TenantConfiguration/Spec420M365NoLegacyTest.php tests/Feature/TenantConfiguration/Spec415NoLegacyNoUiActivationTest.php tests/Feature/TenantConfiguration/TenantConfigurationKernelSchemaTest.php -> passed, 5 tests / 41 assertions, 1 skipped.
    • cd apps/platform && ./vendor/bin/pint --dirty --format agent -> passed and formatted apps/platform/app/Services/TenantConfiguration/GenericContentEvidenceCaptureService.php.
    • git diff --check -> passed.

Deployment Impact

  • Schema migrations: none added.
  • Migration seed alignment: one existing fresh-install seed row was aligned for securityDefaults only, as allowed by the amended plan. No other existing migration seed was changed.
  • Existing databases: already-migrated staging/production databases must run cd apps/platform && php artisan tenant-configuration:sync-defaults after deployment and before Security Defaults capture is attempted. The command is idempotent, syncs Coverage v2 resource-type and supported-scope defaults, and deactivates any stale active Security Defaults TCM planning row.
  • Environment variables: none.
  • Queues / scheduler / workers: no new jobs, queues, scheduler entries, or worker requirements. Existing capture queue behavior applies.
  • Storage / volumes: none.
  • Runtime assets: none.
  • Provider registration: none.
  • External services: no new live Microsoft Graph, TCM, provider, Microsoft docs, or remote network calls in render/compare paths. Capture uses the existing provider gateway and Graph client abstraction.
  • Staging / production: validate registry sync/default state and the focused Coverage v2 inspect path in Staging before Production promotion.

Residual Risks / Follow-Up Candidates

  • This spec supports only securityDefaults. Other Entra types remain deferred.
  • This is internal/operator content-backed comparable/renderable support only. It is not restore readiness, certification, legal/regulatory attestation, customer proof, Review Pack output, or full Entra/M365 coverage.
  • Existing long-lived environments need the documented tenant-configuration:sync-defaults deployment step before Security Defaults capture; no schema migration handles that automatically.