not->toBeFalse(); $next = strpos($source, "\n\n Section::make(", $start + strlen($heading)); return substr($source, $start, $next === false ? null : $next - $start); } } test('finding detail renders without Graph calls', function () { bindFailHardGraphClient(); [$user, $tenant] = createUserWithTenant(role: 'manager'); $baseline = createInventorySyncOperationRun($tenant, [ 'selection_hash' => hash('sha256', 'scope-detail'), 'status' => 'success', 'finished_at' => now()->subDays(2), ]); $current = createInventorySyncOperationRun($tenant, [ 'selection_hash' => $baseline->selection_hash, 'status' => 'success', 'finished_at' => now()->subDay(), ]); $finding = Finding::factory()->for($tenant)->create([ 'finding_type' => Finding::FINDING_TYPE_DRIFT, 'scope_key' => (string) $current->selection_hash, 'baseline_operation_run_id' => $baseline->getKey(), 'current_operation_run_id' => $current->getKey(), 'subject_type' => 'deviceConfiguration', 'subject_external_id' => 'policy-123', 'evidence_jsonb' => [ 'change_type' => 'modified', 'summary' => ['changed_fields' => ['assignments_hash']], ], ]); $inventoryItem = InventoryItem::factory()->for($tenant)->create([ 'external_id' => $finding->subject_external_id, 'display_name' => 'My Policy 123', ]); $this->actingAs($user) ->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant)) ->assertOk() ->assertSee('Technical identifiers') ->assertSee($finding->fingerprint) ->assertSee($inventoryItem->display_name); }); test('finding detail demotes raw technical identifiers from default sections', function (): void { $source = (string) file_get_contents(repo_path('apps/platform/app/Filament/Resources/FindingResource.php')); $artifactSourceSection = spec412FindingResourceSectionBlock($source, 'Artifact source'); $findingSection = spec412FindingResourceSectionBlock($source, 'Finding'); $technicalSection = spec412FindingResourceSectionBlock($source, 'Technical identifiers'); $evidenceSection = spec412FindingResourceSectionBlock($source, 'Evidence (Sanitized)'); expect($artifactSourceSection) ->not->toContain("TextEntry::make('artifact_detector_key')") ->not->toContain("TextEntry::make('artifact_source_target_identifier')") ->not->toContain("TextEntry::make('artifact_provider_key')") ->and($findingSection) ->not->toContain("TextEntry::make('fingerprint')") ->not->toContain("TextEntry::make('scope_key')") ->not->toContain("TextEntry::make('subject_external_id')") ->not->toContain("TextEntry::make('baseline_operation_run_id')") ->not->toContain("TextEntry::make('current_operation_run_id')") ->and($technicalSection) ->toContain("TextEntry::make('fingerprint')") ->toContain("TextEntry::make('scope_key')") ->toContain("TextEntry::make('source_fingerprint')") ->toContain("TextEntry::make('artifact_detector_key')") ->toContain("TextEntry::make('baseline_operation_run_id')") ->toContain('->collapsed()') ->and($evidenceSection) ->toContain("Section::make('Evidence (Sanitized)')") ->toContain('->collapsed()'); }); test('finding detail explains protected changes without exposing hidden values', function () { bindFailHardGraphClient(); [$user, $tenant] = createUserWithTenant(role: 'manager'); $policy = Policy::query()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'external_id' => 'policy-redaction-note', 'policy_type' => 'settingsCatalogPolicy', 'display_name' => 'Policy redaction note', 'platform' => 'windows', ]); $baseline = PolicyVersion::query()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'policy_id' => (int) $policy->getKey(), 'version_number' => 1, 'policy_type' => 'settingsCatalogPolicy', 'platform' => 'windows', 'created_by' => 'spec120@example.com', 'captured_at' => now()->subMinute(), 'snapshot' => [ 'passwordMinimumLength' => 14, ], 'assignments' => [], 'scope_tags' => [], 'metadata' => ['source' => 'spec120_capture'], 'redaction_version' => 1, 'secret_fingerprints' => [ 'snapshot' => [], 'assignments' => [], 'scope_tags' => [], ], ]); $protectedCurrent = PolicyVersion::query()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'policy_id' => (int) $policy->getKey(), 'version_number' => 2, 'policy_type' => 'settingsCatalogPolicy', 'platform' => 'windows', 'created_by' => 'spec120@example.com', 'captured_at' => now(), 'snapshot' => [ 'passwordMinimumLength' => 14, 'clientSecret' => '[REDACTED]', ], 'assignments' => [], 'scope_tags' => [], 'metadata' => ['source' => 'spec120_capture'], 'redaction_version' => 1, 'secret_fingerprints' => [ 'snapshot' => ['/clientSecret' => 'abc123'], 'assignments' => [], 'scope_tags' => [], ], ]); $finding = Finding::factory()->for($tenant)->create([ 'finding_type' => Finding::FINDING_TYPE_DRIFT, 'subject_type' => 'deviceConfiguration', 'subject_external_id' => 'policy-redaction-note', 'evidence_jsonb' => [ 'change_type' => 'modified', 'summary' => ['kind' => 'policy_snapshot'], 'baseline' => [ 'policy_version_id' => (int) $baseline->getKey(), 'redaction_version' => 1, 'secret_fingerprints' => [ 'snapshot' => [], 'assignments' => [], 'scope_tags' => [], ], ], 'current' => [ 'policy_version_id' => (int) $protectedCurrent->getKey(), 'redaction_version' => 1, 'secret_fingerprints' => $protectedCurrent->secret_fingerprints, ], ], ]); InventoryItem::factory()->for($tenant)->create([ 'external_id' => $finding->subject_external_id, 'display_name' => 'Policy redaction note', ]); $this->actingAs($user) ->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant)) ->assertOk() ->assertSee('Protected values are intentionally hidden as [REDACTED]. Secret-only changes remain detectable without revealing the value.'); });