withCredential()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), 'scopes_granted' => ['Policy.Read.All'], ]); Spec425::createResourceWithEvidence($environment, $connection, 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change')); Spec425::createResourceWithEvidence($environment, $connection, 'securityDefaults', Spec425::fixture('security-defaults', 'no-change')); bindFailHardGraphClient(); $result = assertNoOutboundHttp(fn (): EntraCertifiedComparePackResult => app(EntraCertifiedComparePackEvaluator::class) ->evaluate($environment, $connection)); expect($result->state())->toBe(EntraCertifiedComparePackResult::PASSED) ->and($result->certified())->toBeTrue() ->and($result->denominator())->toBe(['conditionalAccessPolicy', 'securityDefaults']) ->and($result->blockers())->toBe([]) ->and(json_encode($result->toArray(), JSON_THROW_ON_ERROR)) ->not->toContain('raw_payload') ->not->toContain('permission_context') ->not->toContain('Policy.Read.All'); }); it('Spec425 blocks certification when either denominator item is missing and does not fallback to wrong-scope evidence', function (): void { Spec425::syncDefaults(); [, $environment] = createMinimalUserWithTenant(role: 'owner'); [, $foreignEnvironment] = createMinimalUserWithTenant(role: 'owner'); $connection = ProviderConnection::factory()->withCredential()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), ]); $foreignConnection = ProviderConnection::factory()->withCredential()->create([ 'workspace_id' => (int) $foreignEnvironment->workspace_id, 'managed_environment_id' => (int) $foreignEnvironment->getKey(), ]); Spec425::createResourceWithEvidence($environment, $connection, 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change')); Spec425::createResourceWithEvidence($foreignEnvironment, $foreignConnection, 'securityDefaults', Spec425::fixture('security-defaults', 'no-change')); $result = app(EntraCertifiedComparePackEvaluator::class)->evaluate($environment, $connection); expect($result->state())->toBe(EntraCertifiedComparePackResult::BLOCKED_MISSING_EVIDENCE) ->and($result->certified())->toBeFalse() ->and($result->blockers())->toContain(EntraCertifiedComparePackResult::BLOCKED_MISSING_EVIDENCE); }); it('Spec425 blocks stale or superseded latest evidence instead of falling back to first or latest implicitly', function (): void { Spec425::syncDefaults(); [, $environment] = createMinimalUserWithTenant(role: 'owner'); $connection = ProviderConnection::factory()->withCredential()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), ]); $resource = Spec425::createResourceWithEvidence($environment, $connection, 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change')); Spec425::createResourceWithEvidence($environment, $connection, 'securityDefaults', Spec425::fixture('security-defaults', 'no-change')); $latestEvidence = $resource->latestEvidence()->firstOrFail(); TenantConfigurationResourceEvidence::factory()->create([ 'resource_id' => (int) $resource->getKey(), 'workspace_id' => (int) $resource->workspace_id, 'managed_environment_id' => (int) $resource->managed_environment_id, 'provider_connection_id' => (int) $resource->provider_connection_id, 'resource_type_id' => (int) $resource->resource_type_id, 'operation_run_id' => (int) $latestEvidence->operation_run_id, 'source_contract_key' => 'conditionalAccessPolicy', 'source_endpoint' => '/identity/conditionalAccess/policies', 'source_version' => 'v1.0', 'raw_payload' => Spec425::fixture('conditional-access', 'state-change'), 'normalized_payload' => Spec425::normalizedPayload(Spec425::fixture('conditional-access', 'state-change')), 'payload_hash' => Spec425::payloadHash(Spec425::normalizedPayload(Spec425::fixture('conditional-access', 'state-change'))), 'evidence_state' => EvidenceState::ContentBacked->value, 'coverage_level' => CoverageLevel::Renderable->value, 'capture_outcome' => CaptureOutcome::Captured->value, 'captured_at' => $latestEvidence->captured_at->copy()->addMinute(), ]); $result = app(EntraCertifiedComparePackEvaluator::class)->evaluate($environment, $connection); $conditionalAccess = collect($result->resourceResults())->firstWhere('canonical_type', 'conditionalAccessPolicy'); $firstResource = collect($conditionalAccess['resources'])->first(); expect($result->state())->toBe(EntraCertifiedComparePackResult::BLOCKED_MISSING_EVIDENCE) ->and($firstResource['reasons'])->toContain('latest_evidence_not_current'); }); it('Spec425 blocks identity states that are not stable', function (): void { Spec425::syncDefaults(); [, $environment] = createMinimalUserWithTenant(role: 'owner'); $connection = ProviderConnection::factory()->withCredential()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), ]); Spec425::createResourceWithEvidence( $environment, $connection, 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change'), ['latest_identity_state' => IdentityState::Derived->value], ); Spec425::createResourceWithEvidence($environment, $connection, 'securityDefaults', Spec425::fixture('security-defaults', 'no-change')); $result = app(EntraCertifiedComparePackEvaluator::class)->evaluate($environment, $connection); expect($result->state())->toBe(EntraCertifiedComparePackResult::BLOCKED_IDENTITY) ->and($result->blockers())->toContain(EntraCertifiedComparePackResult::BLOCKED_IDENTITY); }); it('Spec425 blocks unsupported fields because they make certification ambiguous', function (): void { Spec425::syncDefaults(); [, $environment] = createMinimalUserWithTenant(role: 'owner'); $connection = ProviderConnection::factory()->withCredential()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), ]); Spec425::createResourceWithEvidence($environment, $connection, 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'unsupported-field')); Spec425::createResourceWithEvidence($environment, $connection, 'securityDefaults', Spec425::fixture('security-defaults', 'no-change')); $result = app(EntraCertifiedComparePackEvaluator::class)->evaluate($environment, $connection); expect($result->state())->toBe(EntraCertifiedComparePackResult::BLOCKED_COMPARE) ->and($result->blockers())->toContain(EntraCertifiedComparePackResult::BLOCKED_COMPARE); }); it('Spec425 blocks non-renderable denominator evidence', function (): void { Spec425::syncDefaults(); [, $environment] = createMinimalUserWithTenant(role: 'owner'); $connection = ProviderConnection::factory()->withCredential()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), ]); Spec425::createResourceWithEvidence( $environment, $connection, 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change'), evidenceOverrides: ['coverage_level' => CoverageLevel::ContentBacked->value], ); Spec425::createResourceWithEvidence($environment, $connection, 'securityDefaults', Spec425::fixture('security-defaults', 'no-change')); $result = app(EntraCertifiedComparePackEvaluator::class)->evaluate($environment, $connection); expect($result->state())->toBe(EntraCertifiedComparePackResult::BLOCKED_RENDER) ->and($result->blockers())->toContain(EntraCertifiedComparePackResult::BLOCKED_RENDER); }); it('Spec425 blocks certification output when redaction would leak a sensitive value', function (): void { Spec425::syncDefaults(); [, $environment] = createMinimalUserWithTenant(role: 'owner'); $connection = ProviderConnection::factory()->withCredential()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), ]); $leakingPayload = array_replace(Spec425::fixture('security-defaults', 'no-change'), [ 'clientSecret' => 'spec425-redaction-leak', ]); $leakingNormalized = array_replace(Spec425::fixture('security-defaults', 'no-change'), [ 'displayName' => 'spec425-redaction-leak', ]); Spec425::createResourceWithEvidence($environment, $connection, 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change')); Spec425::createResourceWithEvidence( $environment, $connection, 'securityDefaults', $leakingPayload, evidenceOverrides: [ 'normalized_payload' => $leakingNormalized, 'payload_hash' => Spec425::payloadHash($leakingNormalized), ], ); $result = app(EntraCertifiedComparePackEvaluator::class)->evaluate($environment, $connection); expect($result->state())->toBe(EntraCertifiedComparePackResult::BLOCKED_REDACTION) ->and($result->blockers())->toContain(EntraCertifiedComparePackResult::BLOCKED_REDACTION); }); it('Spec425 rejects provider connections outside the managed environment scope', function (): void { Spec425::syncDefaults(); [, $environment] = createMinimalUserWithTenant(role: 'owner'); [, $foreignEnvironment] = createMinimalUserWithTenant(role: 'owner'); $foreignConnection = ProviderConnection::factory()->withCredential()->create([ 'workspace_id' => (int) $foreignEnvironment->workspace_id, 'managed_environment_id' => (int) $foreignEnvironment->getKey(), ]); expect(fn () => app(EntraCertifiedComparePackEvaluator::class)->evaluate($environment, $foreignConnection)) ->toThrow(InvalidArgumentException::class, 'Provider connection scope mismatch'); });