Skip to content

feat: 006 authority-aware cross-tool correlation 구현 - #273

Merged
goodtu02 merged 5 commits into
devfrom
feat/272-006-finding-correlation
Aug 2, 2026
Merged

feat: 006 authority-aware cross-tool correlation 구현#273
goodtu02 merged 5 commits into
devfrom
feat/272-006-finding-correlation

Conversation

@goodtu02

@goodtu02 goodtu02 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🎋 작업 중인 브랜치 및 이슈

🔎 주요 변경 사항

  • sast-finding-correlation-v1 shared 계약과 canonical source-set/batch/basis/provenance/edge/result/rejection digest 및 exact shape validator를 추가했습니다.
  • canonical T037 observation result 전체를 검증하고, 현재 attempt의 zero-finding batch를 포함한 durable observation/occurrence set과 정확히 일치할 때만 correlation을 진행합니다.
  • T037 replay flag/result digest와 독립적인 durable source binding으로 source-set identity를 고정하고 target/lane/profile/plan까지 deterministic correlation batch에 바인딩합니다.
  • OpenGrep/Trivy scanner responsibility와 active profile required/optional capability를 재검증하여 AUTHORITATIVE | SUPPORTING_ONLY 권한을 platform-owned 방식으로 결정합니다.
  • EXACT_FINGERPRINT, canonical ecosystem/package/installed-version/CVE 기반 SAME_DEPENDENCY_CVE, cross-capability CVE/same-file CWE 기반 SUPPORTING_EVIDENCE | POSSIBLE_OVERLAP만 허용합니다.
  • exact/dependency 및 cross-capability group을 deterministic star edge로 연결하여 quadratic all-pairs 확장을 피하고, 25,000 occurrence/100,000 edge 상한과 64개 단위 cooperative yield를 적용했습니다.
  • 모든 edge에 canonical pair, sorted basis digests, fixed confidence, immutable no-merge/no-inheritance safety와 source/target 두 provenance row를 보존합니다.
  • provenance를 durable occurrence의 scanner/run/capability/severity/fingerprint/rule/artifact/database 정보와 다시 대조하여 self-consistent forged ledger도 저장 전에 거부합니다.
  • serializable transaction, bounded P2034/P2002 retry, chunked writes, complete-ledger replay equality, late T037 batch fence를 구현했습니다.
  • rolling-safe Prisma migration에 correlation batch/source/edge/provenance 모델과 tenant/attempt composite FK, unique, check, index를 추가하고 기존 occurrence scope FK는 online-schema 단계에서 설치합니다.
  • ScanPlaneModule은 T037을 내부화하고 T038 correlation service만 T039에 export하며 coverage/evidence/policy/publication/AI authority와 사용자 route는 계속 차단합니다.
  • 006 contracts/data-model/research/threat-model/quality-gates/spec/plan/quickstart/tasks 및 active-feature guard를 T038 완료와 T039 다음 진입점으로 동기화했습니다.

✅ 컨벤션 확인

  • 브랜치명이 type/issue-number-short-feature 형식을 따르나요?
  • 이슈 제목과 PR 제목을 동일하게 작성했나요?
  • 커밋 메시지가 <type>: <description> 형식을 따르나요?

Check List

  • Assignees 등록을 하였나요?
  • 라벨(Label) 등록을 하였나요?
  • PR 머지 전 반드시 CI가 정상적으로 작동하는지 확인했나요?

검증

  • corepack pnpm lint
  • corepack pnpm test (API 91 suites / 561 tests 포함 전체 workspace 통과)
  • corepack pnpm typecheck
  • corepack pnpm build
  • corepack pnpm --filter @aegisai/api prisma:validate
  • node --test test/runtime/*.test.mjs
  • focused T038 shared contract 80 tests
  • focused T038 API correlation 20 tests
  • active-feature guard 16 tests
  • git diff --check
  • GitHub Actions CI #310 (CI) 성공
  • 자동 리뷰 inline 2개 스레드 모두 수정·회귀 검증 후 해결; review-body 품질 제안 8개 반영, 2개 비적용 근거 기록

006 진행 상태

  • T038 완료
  • 다음 작업: T039 fail-closed scanner/capability coverage and external-publication denial

Closes #272

Summary by CodeRabbit

  • New Features

    • Added deterministic SAST finding correlation across supported scanners and finding types.
    • Preserves source identity, severity, authority, and provenance for every correlation.
    • Supports safe replay, conflict detection, durable persistence, and audit records.
    • Adds validation for complete observation sets, correlation limits, and scanner authority rules.
  • Documentation

    • Documented correlation behavior, safety boundaries, persistence rules, and production quality gates.
  • Tests

    • Added comprehensive coverage for correlation, replay protection, provenance, persistence, validation, and transaction safety.

@goodtu02 goodtu02 self-assigned this Aug 2, 2026
@goodtu02 goodtu02 added the ✨ feat 새로운 기능 추가 label Aug 2, 2026 — with ChatGPT Codex Connector
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds authority-aware SAST finding correlation. It defines shared contracts, persists scoped correlation ledgers with provenance, validates durable observation context, creates deterministic edges, supports exact replay, fences late batches, and exposes only the correlation service.

Changes

SAST finding correlation

Layer / File(s) Summary
Correlation contracts and validation
packages/shared/src/types/sast-finding-correlation.ts, packages/shared/test/*, apps/api/src/scan-plane/sast-finding-correlation.store.ts
Adds versioned correlation types, canonical digests, authority and safety validation, rejection errors, and persistence interfaces.
Durable correlation schema
apps/api/prisma/migrations/..., apps/api/prisma/schema.prisma, apps/api/scripts/apply-online-sast-runtime-schema.mjs
Adds batch, source, edge, and provenance entities with scoped foreign keys, checks, uniqueness constraints, indexes, and cascading actions.
Deterministic correlation service
apps/api/src/scan-plane/sast-finding-correlation.service.ts, apps/api/test/scan-plane/sast-finding-correlation.e2e-spec.ts, apps/api/test/support/*
Validates observations and authority, creates bounded exact/dependency/overlap relationships, preserves provenance, yields during processing, and maps failures to canonical outcomes.
Transactional persistence and replay fencing
apps/api/src/scan-plane/prisma-sast-finding-correlation.store.ts, apps/api/src/scan-plane/prisma-sast-finding-lineage.store.ts, apps/api/test/scan-plane/prisma-sast-finding-correlation.store.e2e-spec.ts
Adds durable context checks, serializable retries, atomic writes, exact replay comparison, count verification, and rejection of late observation batches.
Module boundary and completion checks
apps/api/src/scan-plane/scan-plane.module.ts, specs/006-production-sast-runtime-design/*, test/github-actions/active-feature.test.mjs
Registers the correlation service and store, exports only the correlation service, and updates T038 specifications, milestones, and integration guards.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Observations
  participant SastFindingCorrelationService
  participant PrismaSastFindingCorrelationStore
  participant Prisma
  Observations->>SastFindingCorrelationService: Submit observation results
  SastFindingCorrelationService->>PrismaSastFindingCorrelationStore: Load and validate durable context
  SastFindingCorrelationService->>SastFindingCorrelationService: Build deterministic edges and provenance
  PrismaSastFindingCorrelationStore->>Prisma: Persist batch, sources, edges, provenance, and audit event
  Prisma-->>PrismaSastFindingCorrelationStore: Return persisted counts or replay state
  PrismaSastFindingCorrelationStore-->>SastFindingCorrelationService: Return correlated or rejected outcome
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement the authority-aware correlation ledger, validation, persistence, replay safeguards, tests, module boundary, and T038 documentation required by [#272].
Out of Scope Changes check ✅ Passed The schema, services, tests, module exports, feature guards, and T038 documentation directly support the objectives in [#272].
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the implementation of the authority-aware cross-tool correlation feature described by the pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d13250bb4d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/api/src/scan-plane/sast-finding-correlation.service.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (9)
apps/api/prisma/migrations/20260802120000_sast_finding_correlation/migration.sql (1)

298-311: 🗄️ Data Integrity & Integration | 🔵 Trivial

Document the mandatory online-schema step for the occurrence foreign keys.

This migration creates the batch-scope and edge-scope foreign keys, but not the occurrence foreign keys. apps/api/scripts/apply-online-sast-runtime-schema.mjs adds SastFindingCorrelationEdge_source_occurrence_scope_fkey, SastFindingCorrelationEdge_target_occurrence_scope_fkey, and SastFindingCorrelationProvenance_occurrence_scope_fkey at lines 546-566, because they depend on SastFindingOccurrence_correlation_scope_key, which the same script creates concurrently at lines 89-94.

Until that script runs, correlation edges and provenance rows have no referential integrity against SastFindingOccurrence. Add a comment at the top of this migration that names the required follow-up script, so an operator who applies migrations alone sees the dependency.

📝 Proposed comment header
+-- The correlation occurrence foreign keys are NOT created here. They depend on
+-- "SastFindingOccurrence_correlation_scope_key", which is built concurrently by
+-- apps/api/scripts/apply-online-sast-runtime-schema.mjs. Run that script after
+-- this migration to complete referential integrity for correlation edges and
+-- correlation provenance rows.
 CREATE TYPE "SastFindingCorrelationKind" AS ENUM (
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/api/prisma/migrations/20260802120000_sast_finding_correlation/migration.sql`
around lines 298 - 311, Add a header comment at the top of this migration
documenting that applying migrations alone does not create the occurrence
foreign keys. Name apply-online-sast-runtime-schema.mjs as the mandatory
follow-up script, and mention that it creates the SastFindingOccurrence
correlation key and the three occurrence-scope foreign keys for
SastFindingCorrelationEdge and SastFindingCorrelationProvenance.
packages/shared/src/types/sast-finding-correlation.ts (1)

948-961: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Precompute the correlation-ID patterns instead of building a RegExp per call.

isCorrelationId compiles a new RegExp on every invocation. The validators call it several times per edge and per provenance record, and SAST_FINDING_CORRELATION_LIMITS.maximumEdges allows 100,000 edges. A frozen lookup table removes the repeated compilation.

The prefix parameter is a closed literal union and the function is not exported, so the ast-grep ReDoS hint is a false positive. The precomputed table also removes that hint.

♻️ Proposed refactor
+const CORRELATION_ID_PATTERNS = Object.freeze({
+  'finding-observation': /^finding-observation:\/\/[a-f0-9]{64}$/u,
+  'finding-occurrence': /^finding-occurrence:\/\/[a-f0-9]{64}$/u,
+  'finding-lineage': /^finding-lineage:\/\/[a-f0-9]{64}$/u,
+  'normalized-finding': /^normalized-finding:\/\/[a-f0-9]{64}$/u,
+  'finding-correlation': /^finding-correlation:\/\/[a-f0-9]{64}$/u
+});
+
 function isCorrelationId(
   value: unknown,
-  prefix:
-    | 'finding-observation'
-    | 'finding-occurrence'
-    | 'finding-lineage'
-    | 'normalized-finding'
-    | 'finding-correlation'
+  prefix: keyof typeof CORRELATION_ID_PATTERNS
 ): value is string {
   return (
     typeof value === 'string' &&
-    new RegExp(`^${prefix}://[a-f0-9]{64}$`, 'u').test(value)
+    CORRELATION_ID_PATTERNS[prefix].test(value)
   );
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared/src/types/sast-finding-correlation.ts` around lines 948 -
961, Precompute the correlation-ID regular expressions in a frozen lookup table
keyed by the literal prefixes, then update isCorrelationId to select the cached
pattern instead of constructing a new RegExp per call. Preserve the existing
string and 64-character hexadecimal validation behavior and the current prefix
union.

Source: Linters/SAST tools

packages/shared/test/sast-finding-correlation.test.mjs (1)

112-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a safety-only negative case that keeps the valid edgeDigest.

The current negative case changes safety and edgeDigest together. The forged digest alone makes isSastFindingCorrelationEdgeShapeValid return false, so the assertion does not prove that safety tampering is rejected.

This matters because canonicalizeSastFindingCorrelationEdge substitutes canonicalSafety() and ignores the caller's safety object. A tampered safety field therefore does not change the recomputed digest. Only isSafetyValid rejects it, and that path is currently untested.

💚 Proposed additional assertion
   assert.equal(
     isSastFindingCorrelationEdgeShapeValid(
       {
         ...edge,
         safety: { ...edge.safety, severityInheritanceAllowed: true },
         edgeDigest: digest('forged')
       },
       digest
     ),
     false
   );
+  assert.equal(
+    isSastFindingCorrelationEdgeShapeValid(
+      {
+        ...edge,
+        safety: { ...edge.safety, severityInheritanceAllowed: true }
+      },
+      digest
+    ),
+    false
+  );
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared/test/sast-finding-correlation.test.mjs` around lines 112 -
122, Update the negative test for isSastFindingCorrelationEdgeShapeValid to
tamper only with safety.severityInheritanceAllowed while preserving the original
valid edgeDigest. Keep the assertion expecting false, ensuring the test
exercises safety validation rather than digest mismatch.
apps/api/src/scan-plane/prisma-sast-finding-correlation.store.ts (2)

208-215: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Avoid the double cast on this.prisma.

as unknown as CorrelationReader removes all structural checking. PrismaService extends PrismaClient, which already supplies the four delegates in CorrelationReader. If a model is renamed in schema.prisma, this cast hides the break until runtime.

Pass this.prisma directly, or use a single as CorrelationReader cast so the compiler still verifies the shape.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/scan-plane/prisma-sast-finding-correlation.store.ts` around
lines 208 - 215, Update loadContext to pass this.prisma directly to readContext,
removing the as unknown as CorrelationReader double cast; if a cast is required,
use only as CorrelationReader so structural delegate compatibility remains
compiler-checked.

563-580: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Bound the occurrence read with take.

expectedOccurrenceCount is checked against SAST_FINDING_CORRELATION_LIMITS.maximumOccurrences on Lines 557-562. That check uses the findingCount columns of the observation batches. It does not bound the number of sastFindingOccurrence rows the query returns. If the durable table holds more occurrence rows than the batch columns declare, this query materializes all of them inside a serializable transaction before Line 578 rejects.

Add take: expectedOccurrenceCount + 1. The count check on Line 578 then still rejects the mismatch, and the read stays bounded.

♻️ Proposed change
         select: OCCURRENCE_SELECT,
         orderBy: [
           { observationBatchId: 'asc' },
           { ordinal: 'asc' }
-        ]
+        ],
+        take: expectedOccurrenceCount + 1
       });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/scan-plane/prisma-sast-finding-correlation.store.ts` around
lines 563 - 580, Bound the occurrence query in the correlation flow by adding a
take limit of expectedOccurrenceCount + 1 to the findMany call on
sastFindingOccurrence. Keep the existing ordering and length-mismatch rejection
unchanged so valid counts continue to work while oversized results are detected
without materializing all rows.
apps/api/src/scan-plane/sast-finding-correlation.service.ts (1)

322-324: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log unmapped errors before converting them to a rejection.

The catch converts every error into a rejection reason code. mapStoreError returns FINDING_CORRELATION_PERSISTENCE_FAILED for any error it does not recognize. A programming defect, such as a TypeError, therefore becomes a silent, well-formed rejection. Persistence and correlation defects then produce no signal for operators.

Inject a Logger and record the unrecognized error before you map it. Keep the rejection payload free of identifiers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/scan-plane/sast-finding-correlation.service.ts` around lines 322
- 324, Update the catch block in the finding-correlation service to inject and
use a Logger, logging errors that mapStoreError cannot recognize before
returning the rejection. Preserve the existing mapped rejection behavior and
ensure log messages contain no identifiers or sensitive correlation data.
apps/api/test/scan-plane/sast-finding-correlation.e2e-spec.ts (1)

309-347: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the occurrence count from SAST_FINDING_CORRELATION_LIMITS.yieldOccurrenceInterval.

The literal 65 appears in the test name and in four assertions. The expectation yieldCount === 1 holds only while yieldOccurrenceInterval equals 64. If the shared limit changes, this test fails without indicating the cause.

Import the limit and compute the count as yieldOccurrenceInterval + 1. Compute the expected exactFingerprintCount as yieldOccurrenceInterval.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/test/scan-plane/sast-finding-correlation.e2e-spec.ts` around lines
309 - 347, The test case around YieldCountingCorrelationService hardcodes the
bounded occurrence count and expected exact fingerprint count. Import
SAST_FINDING_CORRELATION_LIMITS, derive the fixture, source/observation counts,
and occurrence array length from yieldOccurrenceInterval + 1, and derive
exactFingerprintCount from yieldOccurrenceInterval while preserving the
yieldCount assertion.
apps/api/test/scan-plane/prisma-sast-finding-correlation.store.e2e-spec.ts (1)

309-323: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add coverage for readContext and the in-transaction re-read.

mockCorrelationContext stubs the private readContext method. correlateInTransaction calls readContext a second time inside the transaction and compares the result with input.context through sameContext (apps/api/src/scan-plane/prisma-sast-finding-correlation.store.ts, Lines 230-236). Stubbing the method disables both the durable read and that TOCTOU fence.

As a result, no test in this file exercises readContext (Lines 426-640). That function enforces the plan-digest binding, the attempt stage and scan-request status checks, the exact observation-set completeness check, and verifySourceCapabilities. The linked issue requires these authority checks.

Add at least one test that supplies the observation, occurrence, and attempt delegates on the transaction double and lets readContext run. Add one negative case where the in-transaction re-read returns a changed context, and assert SastFindingCorrelationDurableScopeError.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/test/scan-plane/prisma-sast-finding-correlation.store.e2e-spec.ts`
around lines 309 - 323, Add tests in the correlation store suite that avoid
stubbing readContext, provide observation, occurrence, and attempt delegates on
the transaction double, and exercise its authority checks. Cover a successful
durable read and a negative correlateInTransaction case where the in-transaction
re-read differs from input.context, asserting
SastFindingCorrelationDurableScopeError; retain mockCorrelationContext only for
tests that do not require this coverage.
apps/api/test/support/sast-finding-correlation-fixtures.ts (1)

149-179: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the profile capabilities and the capability order from the shared contracts.

Two constants duplicate shared source data:

  • Lines 169-179 hardcode requiredCapabilities per profile. contextAuthorityMatchesProfile in apps/api/src/scan-plane/sast-finding-correlation.service.ts (Lines 658-670) compares this list for exact ordered equality against SAST_SCAN_PROFILES[profileId].requiredCapabilities with SBOM removed. If a profile definition changes, these tests fail with FINDING_CORRELATION_AUTHORITY_INVALID instead of a clear fixture error.
  • Lines 407-415 re-declare the capability order. SAST_CAPABILITIES already defines it, and isCanonicalCapabilities in apps/api/src/scan-plane/prisma-sast-finding-correlation.store.ts (Lines 1322-1339) derives the order from that constant. indexOf also returns -1 for an unknown capability, which sorts it first without an error.

Derive both from the shared constants.

♻️ Proposed refactor
+const FINDING_CAPABILITY_ORDER = SAST_CAPABILITIES.filter(
+  (capability) => capability !== 'SBOM'
+);
+
 function capabilityOrder(left: string, right: string): number {
-  const order = [
-    'SAST',
-    'DEPENDENCY_VULNERABILITY',
-    'SECRET_DETECTION',
-    'IAC_MISCONFIGURATION'
-  ];
-  return order.indexOf(left) - order.indexOf(right);
+  const leftIndex = FINDING_CAPABILITY_ORDER.indexOf(left as never);
+  const rightIndex = FINDING_CAPABILITY_ORDER.indexOf(right as never);
+  if (leftIndex < 0 || rightIndex < 0) {
+    throw new Error(`Unknown correlation fixture capability.`);
+  }
+  return leftIndex - rightIndex;
 }

Apply the same approach to requiredCapabilities:

-      requiredCapabilities: options.supportingOpenGrep
-        ? [
-            'DEPENDENCY_VULNERABILITY',
-            'SECRET_DETECTION',
-            'IAC_MISCONFIGURATION'
-          ]
-        : [
-            'SAST',
-            'DEPENDENCY_VULNERABILITY',
-            'SECRET_DETECTION'
-          ],
+      requiredCapabilities: SAST_SCAN_PROFILES[
+        profileId
+      ].requiredCapabilities.filter(
+        (capability) => capability !== 'SBOM'
+      ),

Also applies to: 407-415

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/test/support/sast-finding-correlation-fixtures.ts` around lines 149
- 179, Update the fixture’s profile setup to derive requiredCapabilities
directly from SAST_SCAN_PROFILES[profileId].requiredCapabilities, removing SBOM
as required by the correlation contract, instead of hardcoding per-profile
arrays. Update the capability ordering logic near the fixture’s canonical
capability handling to reuse SAST_CAPABILITIES and validate unknown capabilities
rather than sorting indexOf(-1) values first; preserve the shared contract’s
exact ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/src/scan-plane/prisma-sast-finding-lineage.store.ts`:
- Around line 246-263: The correlation fence in the transaction flow must fail
closed when transaction.sastFindingCorrelationBatch is unavailable. Update the
correlationDelegate guard to throw SastFindingLineageReplayConflictError when
the delegate is absent; when present, continue querying findFirst with the
existing scope filters and throw the same error when a matching record exists.

---

Nitpick comments:
In
`@apps/api/prisma/migrations/20260802120000_sast_finding_correlation/migration.sql`:
- Around line 298-311: Add a header comment at the top of this migration
documenting that applying migrations alone does not create the occurrence
foreign keys. Name apply-online-sast-runtime-schema.mjs as the mandatory
follow-up script, and mention that it creates the SastFindingOccurrence
correlation key and the three occurrence-scope foreign keys for
SastFindingCorrelationEdge and SastFindingCorrelationProvenance.

In `@apps/api/src/scan-plane/prisma-sast-finding-correlation.store.ts`:
- Around line 208-215: Update loadContext to pass this.prisma directly to
readContext, removing the as unknown as CorrelationReader double cast; if a cast
is required, use only as CorrelationReader so structural delegate compatibility
remains compiler-checked.
- Around line 563-580: Bound the occurrence query in the correlation flow by
adding a take limit of expectedOccurrenceCount + 1 to the findMany call on
sastFindingOccurrence. Keep the existing ordering and length-mismatch rejection
unchanged so valid counts continue to work while oversized results are detected
without materializing all rows.

In `@apps/api/src/scan-plane/sast-finding-correlation.service.ts`:
- Around line 322-324: Update the catch block in the finding-correlation service
to inject and use a Logger, logging errors that mapStoreError cannot recognize
before returning the rejection. Preserve the existing mapped rejection behavior
and ensure log messages contain no identifiers or sensitive correlation data.

In `@apps/api/test/scan-plane/prisma-sast-finding-correlation.store.e2e-spec.ts`:
- Around line 309-323: Add tests in the correlation store suite that avoid
stubbing readContext, provide observation, occurrence, and attempt delegates on
the transaction double, and exercise its authority checks. Cover a successful
durable read and a negative correlateInTransaction case where the in-transaction
re-read differs from input.context, asserting
SastFindingCorrelationDurableScopeError; retain mockCorrelationContext only for
tests that do not require this coverage.

In `@apps/api/test/scan-plane/sast-finding-correlation.e2e-spec.ts`:
- Around line 309-347: The test case around YieldCountingCorrelationService
hardcodes the bounded occurrence count and expected exact fingerprint count.
Import SAST_FINDING_CORRELATION_LIMITS, derive the fixture, source/observation
counts, and occurrence array length from yieldOccurrenceInterval + 1, and derive
exactFingerprintCount from yieldOccurrenceInterval while preserving the
yieldCount assertion.

In `@apps/api/test/support/sast-finding-correlation-fixtures.ts`:
- Around line 149-179: Update the fixture’s profile setup to derive
requiredCapabilities directly from
SAST_SCAN_PROFILES[profileId].requiredCapabilities, removing SBOM as required by
the correlation contract, instead of hardcoding per-profile arrays. Update the
capability ordering logic near the fixture’s canonical capability handling to
reuse SAST_CAPABILITIES and validate unknown capabilities rather than sorting
indexOf(-1) values first; preserve the shared contract’s exact ordering.

In `@packages/shared/src/types/sast-finding-correlation.ts`:
- Around line 948-961: Precompute the correlation-ID regular expressions in a
frozen lookup table keyed by the literal prefixes, then update isCorrelationId
to select the cached pattern instead of constructing a new RegExp per call.
Preserve the existing string and 64-character hexadecimal validation behavior
and the current prefix union.

In `@packages/shared/test/sast-finding-correlation.test.mjs`:
- Around line 112-122: Update the negative test for
isSastFindingCorrelationEdgeShapeValid to tamper only with
safety.severityInheritanceAllowed while preserving the original valid
edgeDigest. Keep the assertion expecting false, ensuring the test exercises
safety validation rather than digest mismatch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f3b5df04-3be0-49cd-b609-d27bed345698

📥 Commits

Reviewing files that changed from the base of the PR and between a017d4c and d13250b.

📒 Files selected for processing (26)
  • apps/api/prisma/migrations/20260802120000_sast_finding_correlation/migration.sql
  • apps/api/prisma/schema.prisma
  • apps/api/scripts/apply-online-sast-runtime-schema.mjs
  • apps/api/src/scan-plane/prisma-sast-finding-correlation.store.ts
  • apps/api/src/scan-plane/prisma-sast-finding-lineage.store.ts
  • apps/api/src/scan-plane/sast-finding-correlation.service.ts
  • apps/api/src/scan-plane/sast-finding-correlation.store.ts
  • apps/api/src/scan-plane/scan-plane.module.ts
  • apps/api/test/scan-plane/prisma-sast-finding-correlation.store.e2e-spec.ts
  • apps/api/test/scan-plane/sast-finding-correlation-persistence.e2e-spec.ts
  • apps/api/test/scan-plane/sast-finding-correlation.e2e-spec.ts
  • apps/api/test/scan-plane/sast-finding-lineage-persistence.e2e-spec.ts
  • apps/api/test/support/sast-finding-correlation-fixtures.ts
  • packages/shared/src/index.ts
  • packages/shared/src/types/sast-finding-correlation.ts
  • packages/shared/test/sast-finding-correlation.test.mjs
  • specs/006-production-sast-runtime-design/contracts/sast-runtime.md
  • specs/006-production-sast-runtime-design/data-model.md
  • specs/006-production-sast-runtime-design/plan.md
  • specs/006-production-sast-runtime-design/quality-gates.md
  • specs/006-production-sast-runtime-design/quickstart.md
  • specs/006-production-sast-runtime-design/research.md
  • specs/006-production-sast-runtime-design/spec.md
  • specs/006-production-sast-runtime-design/tasks.md
  • specs/006-production-sast-runtime-design/threat-model.md
  • test/github-actions/active-feature.test.mjs

Comment thread apps/api/src/scan-plane/prisma-sast-finding-lineage.store.ts

goodtu02 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

CodeRabbit 리뷰 본문의 nitpick을 현재 계약과 대조해 처리했습니다.

적용한 유효 지적 8건 (7b37388):

  • migration에 필수 online-schema 후속 단계와 occurrence-scope FK를 명시
  • PrismaService double cast 제거 및 구조적 타입 검사 복원
  • occurrence 조회를 expectedOccurrenceCount + 1로 제한
  • 실제 durable reader와 serializable TOCTOU re-read 회귀 테스트 추가
  • yield 테스트를 shared limit에서 유도
  • fixture profile/capability 순서를 shared contract에서 유도
  • correlation ID 정규식을 사전 생성
  • 유효한 edge digest를 유지한 safety-only 음성 테스트 추가

적용하지 않은 제안:

  • correlation service Logger 추가: 실행 가능한 보안 가드가 Loggerconsole을 명시적으로 금지하며, zero-payload rejection 경계를 유지해야 합니다. CI #309에서 이 충돌을 확인했고 4df264b에서 Logger 변경을 제거했습니다.
  • docstring coverage 경고: 저장소의 CI/quickstart 완료 게이트나 기존 스타일이 아니며 이번 기능 범위를 벗어납니다.

검증: workspace lint/test/typecheck/build, Prisma validate, runtime layout, git diff --check 통과. API 91 suites / 561 tests 및 active-feature guard 16 tests 통과.

@goodtu02
goodtu02 merged commit 20aa5e2 into dev Aug 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 006 authority-aware cross-tool correlation 구현

1 participant