buildResolvedEvidence( policyType: (string) $version->policy_type, subjectExternalId: $subjectExternalId, platform: is_string($version->platform) ? $version->platform : null, snapshot: $version->snapshot, assignments: $version->assignments, scopeTags: $version->scope_tags, secretFingerprints: $version->secret_fingerprints, redactionVersion: $version->redaction_version, capturedAt: $version->captured_at, policyVersionId: (int) $version->getKey(), operationRunId: $version->operation_run_id, capturePurpose: $version->capture_purpose?->value, observedAt: $observedAt, observedOperationRunId: $observedOperationRunId, ); } public function resolve(Tenant $tenant, array $subjects, ?CarbonImmutable $since = null, ?int $latestInventorySyncRunId = null): array { if ($subjects === []) { return []; } $requestedKeys = $this->requestedKeys($subjects); if ($requestedKeys === []) { return []; } $policyTypes = array_values(array_unique(array_map( static fn (array $subject): string => trim((string) ($subject['policy_type'] ?? '')), $subjects, ))); $policyTypes = array_values(array_filter($policyTypes, static fn (string $value): bool => $value !== '')); $externalIds = array_values(array_unique(array_map( static fn (array $subject): string => trim((string) ($subject['subject_external_id'] ?? '')), $subjects, ))); $externalIds = array_values(array_filter($externalIds, static fn (string $value): bool => $value !== '')); if ($policyTypes === [] || $externalIds === []) { return []; } $policies = Policy::query() ->where('tenant_id', (int) $tenant->getKey()) ->whereIn('policy_type', $policyTypes) ->whereIn('external_id', $externalIds) ->get(['id', 'policy_type', 'external_id']); /** @var Collection $policies */ $policyIdToKey = []; $policyIds = []; foreach ($policies as $policy) { if (! $policy instanceof Policy) { continue; } $key = (string) $policy->policy_type.'|'.(string) $policy->external_id; if (! array_key_exists($key, $requestedKeys)) { continue; } $policyIdToKey[(int) $policy->getKey()] = $key; $policyIds[] = (int) $policy->getKey(); } if ($policyIds === []) { return []; } $baseQuery = DB::table('policy_versions') ->select([ 'policy_versions.id', 'policy_versions.operation_run_id', 'policy_versions.capture_purpose', 'policy_versions.policy_id', 'policy_versions.policy_type', 'policy_versions.platform', 'policy_versions.captured_at', 'policy_versions.snapshot', 'policy_versions.assignments', 'policy_versions.scope_tags', 'policy_versions.secret_fingerprints', 'policy_versions.redaction_version', 'policy_versions.version_number', ]) ->selectRaw('ROW_NUMBER() OVER (PARTITION BY policy_id ORDER BY captured_at DESC, version_number DESC, id DESC) as rn') ->where('tenant_id', (int) $tenant->getKey()) ->whereIn('policy_id', $policyIds) ->whereNull('deleted_at'); if ($since instanceof CarbonImmutable) { $baseQuery->where('captured_at', '>=', $since->toDateTimeString()); } $versions = DB::query() ->fromSub($baseQuery, 'ranked_policy_versions') ->where('rn', 1) ->get(); $resolved = []; foreach ($versions as $version) { $policyId = is_numeric($version->policy_id ?? null) ? (int) $version->policy_id : null; $key = $policyId !== null ? ($policyIdToKey[$policyId] ?? null) : null; if (! is_string($key) || $key === '' || ! array_key_exists($key, $requestedKeys)) { continue; } $policyType = is_string($version->policy_type ?? null) ? (string) $version->policy_type : ''; $subjectExternalId = (string) ($requestedKeys[$key] ?? ''); if ($policyType === '' || $subjectExternalId === '') { continue; } $resolved[$key] = $this->buildResolvedEvidence( policyType: $policyType, subjectExternalId: $subjectExternalId, platform: is_string($version->platform ?? null) ? (string) $version->platform : null, snapshot: $version->snapshot ?? null, assignments: $version->assignments ?? null, scopeTags: $version->scope_tags ?? null, secretFingerprints: $version->secret_fingerprints ?? null, redactionVersion: is_numeric($version->redaction_version ?? null) ? (int) $version->redaction_version : null, capturedAt: $version->captured_at ?? null, policyVersionId: is_numeric($version->id ?? null) ? (int) $version->id : null, operationRunId: is_numeric($version->operation_run_id ?? null) ? (int) $version->operation_run_id : null, capturePurpose: is_string($version->capture_purpose ?? null) ? trim((string) $version->capture_purpose) : null, ); } return $resolved; } /** * @param list $subjects * @return array */ private function requestedKeys(array $subjects): array { $keys = []; foreach ($subjects as $subject) { $policyType = trim((string) ($subject['policy_type'] ?? '')); $externalId = trim((string) ($subject['subject_external_id'] ?? '')); if ($policyType === '' || $externalId === '') { continue; } $keys[$policyType.'|'.$externalId] = $externalId; } return $keys; } private function buildResolvedEvidence( string $policyType, string $subjectExternalId, ?string $platform, mixed $snapshot, mixed $assignments, mixed $scopeTags, mixed $secretFingerprints, ?int $redactionVersion, mixed $capturedAt, ?int $policyVersionId, mixed $operationRunId, ?string $capturePurpose, ?CarbonImmutable $observedAt = null, ?int $observedOperationRunId = null, ): ResolvedEvidence { $snapshot = is_array($snapshot) ? $snapshot : (is_string($snapshot) ? json_decode($snapshot, true) : null); $snapshot = is_array($snapshot) ? $snapshot : []; $assignments = is_array($assignments) ? $assignments : (is_string($assignments) ? json_decode($assignments, true) : null); $assignments = is_array($assignments) ? $assignments : []; $scopeTags = is_array($scopeTags) ? $scopeTags : (is_string($scopeTags) ? json_decode($scopeTags, true) : null); $scopeTags = is_array($scopeTags) ? $scopeTags : []; $secretFingerprints = is_array($secretFingerprints) ? $secretFingerprints : (is_string($secretFingerprints) ? json_decode($secretFingerprints, true) : null); $secretFingerprints = is_array($secretFingerprints) ? $secretFingerprints : []; $normalized = $this->settingsNormalizer->normalizeForDiff( snapshot: $snapshot, policyType: $policyType, platform: $platform, ); $normalizedAssignments = $this->assignmentsNormalizer->normalizeForDiff($assignments); $normalizedScopeTagIds = $this->scopeTagsNormalizer->normalizeIds($scopeTags); $hash = $this->hasher->hashNormalized([ 'settings' => $normalized, 'assignments' => $normalizedAssignments, 'scope_tag_ids' => $normalizedScopeTagIds, 'secret_fingerprints' => [ 'snapshot' => $this->fingerprintBucket($secretFingerprints, 'snapshot'), 'assignments' => $this->fingerprintBucket($secretFingerprints, 'assignments'), 'scope_tags' => $this->fingerprintBucket($secretFingerprints, 'scope_tags'), ], 'redaction_version' => $redactionVersion, ]); $observedAt ??= is_string($capturedAt) ? CarbonImmutable::parse($capturedAt) : null; $observedOperationRunId ??= is_numeric($operationRunId) ? (int) $operationRunId : null; $capturePurpose = is_string($capturePurpose) ? trim($capturePurpose) : null; $capturePurpose = $capturePurpose !== '' ? $capturePurpose : null; return new ResolvedEvidence( policyType: $policyType, subjectExternalId: $subjectExternalId, hash: $hash, fidelity: EvidenceProvenance::FidelityContent, source: EvidenceProvenance::SourcePolicyVersion, observedAt: $observedAt, observedOperationRunId: $observedOperationRunId, meta: [ 'policy_version_id' => $policyVersionId, 'operation_run_id' => is_numeric($operationRunId) ? (int) $operationRunId : null, 'capture_purpose' => $capturePurpose, 'redaction_version' => $redactionVersion, ], ); } /** * @param array $secretFingerprints * @return array */ private function fingerprintBucket(array $secretFingerprints, string $bucket): array { $bucketFingerprints = $secretFingerprints[$bucket] ?? []; return is_array($bucketFingerprints) ? $bucketFingerprints : []; } }