feat: 006 고정 커밋 fetch 및 hostile preflight 구현 - #251
Conversation
📝 WalkthroughWalkthroughThis change adds Phase 5 production SAST runtime components: attested workload identity, durable credential leases, ephemeral credential handling, fixed-commit repository fetching, hostile repository preflight evaluation, shared contracts, configuration validation, integration wiring, tests, and runtime documentation. ChangesProduction SAST runtime
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant TokenBrokerService
participant RepositoryFetchService
participant RepositoryPreflightService
participant RepositoryCredentialLeaseStore
TokenBrokerService->>RepositoryCredentialLeaseStore: reserve and activate attempt lease
RepositoryFetchService->>TokenBrokerService: request credential handoff
RepositoryFetchService->>RepositoryPreflightService: evaluate fetched tree entries
RepositoryPreflightService-->>RepositoryFetchService: decision and inventory digest
TokenBrokerService->>RepositoryCredentialLeaseStore: record wipe or revoke state
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dd6245d57e
ℹ️ 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".
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
packages/shared/src/types/production-architecture.ts (1)
371-390: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMissing shared
ISSUERconstant (see consolidated comment).
audience/versionare exported as typed constants and consumed consistently elsewhere, but no equivalent constant exists forissuer, which is duplicated as a hardcoded literal intoken-broker.dto.ts. See consolidated comment for details.🤖 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/production-architecture.ts` around lines 371 - 390, The shared production architecture types lack an exported issuer constant. Define an issuer constant alongside WORKLOAD_IDENTITY_ATTESTATION_VERSION and WORKLOAD_IDENTITY_ATTESTATION_AUDIENCE, type WorkloadIdentityAttestationClaims.issuer with typeof that constant, and update token-broker.dto.ts to reuse it instead of a hardcoded literal.apps/api/prisma/migrations/20260724120000_sast_repository_credential_lease/migration.sql (1)
66-70: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winNon-concurrent unique index creation blocks writes on existing tables.
These two
CREATE UNIQUE INDEXstatements run against the already-populatedRepositoryBindingandScanRequesttables (not the new lease table), so they will hold write locks for the duration of index build. Squawk correctly flags this.Note that simply appending
CONCURRENTLYhere won't work as-is: Prisma wraps multi-statement migration files in a transaction, andCREATE INDEX CONCURRENTLYcannot run inside a transaction block. It would need to be split into its own single-statement migration file to avoid Prisma's transaction wrapping.🤖 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/20260724120000_sast_repository_credential_lease/migration.sql` around lines 66 - 70, Move the unique index creation for "RepositoryBinding_id_tenantId_key" and "ScanRequest_id_tenantId_repositoryBindingId_key" out of this multi-statement migration into separate single-statement migration files, and create each index with concurrent index creation. Ensure these statements execute outside Prisma’s transaction wrapper while preserving both index definitions and uniqueness constraints.Source: Linters/SAST tools
🤖 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/prisma/migrations/20260724120000_sast_repository_credential_lease/migration.sql`:
- Around line 81-85: Rename the foreign-key constraint in the migration’s ALTER
TABLE statement to an explicit name no longer than PostgreSQL’s 63-character
identifier limit. Update any corresponding references to use this exact
shortened name, while preserving the existing columns, referenced table, and
cascade behaviors.
- Around line 54-55: Replace the table-level attemptId uniqueness in the
SastRepositoryCredentialLease migration with a composite unique constraint
scoped to the tenant and the spec’s scan-attempt identity. Update the
corresponding Prisma model and in-memory lease-store reserve() logic so
duplicate attempts are rejected only for the same tenant, while different
tenants may reuse the attempt ID.
In `@apps/api/prisma/schema.prisma`:
- Around line 408-414: Update the tenant deletion flow and related Prisma
relations so Lease records are removed or explicitly validated before cascading
Tenant, RepositoryBinding, or ScanRequest deletes. Anchor the change to the
lease relations shown here, ensuring repositoryBinding and scanRequest foreign
keys no longer cause RESTRICT failures during tenant deletion.
In `@apps/api/src/config/config.schema.ts`:
- Around line 39-70: Add `.hex().lowercase()` to the `TOKEN_ENCRYPTION_KEY` Joi
schema before its value is compared by the `WORKLOAD_ATTESTATION_KEY` and
`PREFLIGHT_ATTESTATION_KEY` `Joi.ref` validations, preserving its existing
length and required/default behavior.
In `@apps/api/src/scan-plane/repository-fetch.service.ts`:
- Around line 192-206: The returned metadata in the repository-fetch flow must
derive credentialWiped from the actual destroyCredentialFiles cleanup result
instead of hardcoding true before finally executes. Update
destroyCredentialFiles and its caller to report and retain cleanup success or
failure, then populate credentialWiped from that outcome while preserving remote
cleanup behavior.
In `@apps/api/test/support/in-memory-repository-credential-lease.store.ts`:
- Around line 74-88: Update revoke() in the in-memory credential lease store so
both REVOKED and WIPED leases are treated as terminal and returned unchanged.
Only assign status to REVOKED and set revokedAt when the lease is not already in
either terminal state, matching the Prisma-backed store’s idempotent behavior.
---
Nitpick comments:
In
`@apps/api/prisma/migrations/20260724120000_sast_repository_credential_lease/migration.sql`:
- Around line 66-70: Move the unique index creation for
"RepositoryBinding_id_tenantId_key" and
"ScanRequest_id_tenantId_repositoryBindingId_key" out of this multi-statement
migration into separate single-statement migration files, and create each index
with concurrent index creation. Ensure these statements execute outside Prisma’s
transaction wrapper while preserving both index definitions and uniqueness
constraints.
In `@packages/shared/src/types/production-architecture.ts`:
- Around line 371-390: The shared production architecture types lack an exported
issuer constant. Define an issuer constant alongside
WORKLOAD_IDENTITY_ATTESTATION_VERSION and
WORKLOAD_IDENTITY_ATTESTATION_AUDIENCE, type
WorkloadIdentityAttestationClaims.issuer with typeof that constant, and update
token-broker.dto.ts to reuse it instead of a hardcoded literal.
🪄 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: 1a1c5ffc-7092-4730-9ad1-ac38b73553d3
📒 Files selected for processing (40)
.env.exampleapps/api/.env.exampleapps/api/prisma/migrations/20260724120000_sast_repository_credential_lease/migration.sqlapps/api/prisma/schema.prismaapps/api/src/config/config.module.tsapps/api/src/config/config.schema.tsapps/api/src/config/config.types.tsapps/api/src/control-plane/control-plane.service.tsapps/api/src/control-plane/control-plane.types.tsapps/api/src/scan-plane/credential-tmpfs-verifier.service.tsapps/api/src/scan-plane/repository-fetch.service.tsapps/api/src/scan-plane/repository-git-executor.tsapps/api/src/scan-plane/repository-preflight-attestation.service.tsapps/api/src/scan-plane/repository-preflight.service.tsapps/api/src/scan-plane/scan-plane.module.tsapps/api/src/token-broker/prisma-repository-credential-lease.store.tsapps/api/src/token-broker/repository-credential-lease.store.tsapps/api/src/token-broker/token-broker.dto.tsapps/api/src/token-broker/token-broker.module.tsapps/api/src/token-broker/token-broker.service.tsapps/api/src/token-broker/token-broker.types.tsapps/api/src/token-broker/token-credential-issuer.service.tsapps/api/src/token-broker/workload-identity-attestation.service.tsapps/api/test/architecture/production-architecture-contracts.e2e-spec.tsapps/api/test/config/config.env-files.e2e-spec.tsapps/api/test/scan-plane/repository-fetch.service.e2e-spec.tsapps/api/test/scan-plane/repository-preflight.service.e2e-spec.tsapps/api/test/support/in-memory-repository-credential-lease.store.tsapps/api/test/token-broker/prisma-repository-credential-lease.store.e2e-spec.tsapps/api/test/token-broker/token-broker.e2e-spec.tsapps/api/test/token-broker/token-credential-issuer.service.e2e-spec.tsdeploy/oracle/.env.exampledeploy/oracle/BOOTSTRAP.mdpackages/shared/src/index.tspackages/shared/src/types/production-architecture.tspackages/shared/src/types/sast-fetch.tsspecs/006-production-sast-runtime-design/contracts/sast-runtime.mdspecs/006-production-sast-runtime-design/data-model.mdspecs/006-production-sast-runtime-design/quickstart.mdspecs/006-production-sast-runtime-design/tasks.md
🎋 작업 중인 브랜치 및 이슈 - 브랜치:
feat/250-006-hardened-fetch-preflight- 이슈: #250 - 선행 PR: #249 (병합 완료) ## 🔎 주요 변경 사항 - signed workload identity attestation을 tenant/repository/scan/attempt/fixed commit에 결합하고, 한 attempt당 한 번만 발급되는 단기REPO_READcredential lease를 구현했습니다. - credential 값은 opaque 메모리 버퍼와 검증된 tmpfsGIT_ASKPASS파일에서만 사용하고, 영속화·로그·argv·일반 env 노출 없이 fetch 종료 시 zeroize/wipe하도록 구현했습니다. - lease 예약을RUNNINGdurable scan에 한정하고, tenant/repository/scan 조합을 복합 FK로 강제하며, attested HTTP 완료와 주기적 expiry reconciliation까지 포함한 원자적 terminal lifecycle을 구현했습니다. - workload/preflight signing key를 서로 및 token encryption key와 분리하고, production에서 provider-backed GitHub App/GitLab 발급기가 없으면 fail closed 하도록 구성했습니다. - 활성 durable repository binding에서만 GitHub/GitLab remote를 생성하고, full fixed SHA fetch 후 Git tree/object 기반 profile limit을 checkout 전에 강제한 뒤 no-tag, no-submodule, LFS smudge disabled detached checkout을 수행하도록 구현했습니다. - Git 호출을 shell 없는 argv 배열과 allowlisted 환경으로 제한하고, credential/remote 및.gitmetadata를 scanner handoff 전에 제거하도록 구현했습니다. - scratch/workspace/tmpfs의 realpath·symlink 경계를 검증하고, credential 파일 권한·bounded timeout/output·bounded tree enrichment를 적용했습니다. - Git tree를 NUL/binary-safe하게 파싱하고 object ID·byte size·mode·symlink target·LFS pointer metadata만 수집하도록 구현했습니다. - path separator/NFC 정규화, UTF-8/control/bidi/absolute/drive/UNC/traversal 거절, duplicate/case/Unicode collision과 symlink escape/cycle 검사를 구현했습니다. - repository/file/single-file/depth 제한과 generated/vendor/fixture/hidden/LFS/submodule/archive 분류를 적용하고, Fast changed/context allowlist만 selected bytes에 합산해 selection 자체를 inventory digest에 결합했습니다. - Git object ID까지 포함한 bytewise length-prefixed inventory digest와 signedACCEPT | REJECT | RESTRICTED_ESCALATIONpreflight attestation을 구현했습니다. - credential replay/scope/tamper/expiry, fetch 고정성·비밀값 비노출, hostile path/symlink/collision/limit corpus를 포함한 회귀 테스트를 추가했습니다. - shared 계약, tenant/attempt lease uniqueness, cascade FK와 online composite index migration, 환경 예시, 006 quickstart/data model/runtime contract/tasks를 T022–T024 기준으로 동기화했습니다. ## ✅ 컨벤션 확인 - [x] 브랜치명이type/issue-number-short-feature형식을 따르나요? - [x] 이슈 제목과 PR 제목을 동일하게 작성했나요? - [x] 커밋 메시지가<type>: <description>형식을 따르나요? ## Check List - [x] Assignees 등록을 하였나요? - [x] 라벨(Label) 등록을 하였나요? - [x] PR 머지 전 반드시 CI가 정상적으로 작동하는지 확인했나요? ## 기준 관계 - 선행 구현 PR #249는dev에 병합되었습니다. - 현재 PR은 최신dev기준이며 T022–T024를 완료합니다. - 실제 provider-backed credential minting과 microVM scanner wrapper 실행은 T025–T028/live rollout 범위이며, 그 전 production issuer는 의도적으로 fail closed 합니다. ## 검증 -corepack pnpm lint-corepack pnpm --filter @aegisai/shared test— 37개 통과 -corepack pnpm --filter @aegisai/ai test— 17개 통과 -corepack pnpm --filter @aegisai/web test— 54개 통과 -corepack pnpm --filter @aegisai/api exec jest --config ./test/jest-e2e.json --runInBand— 69개 스위트, 277개 통과 -node --test test/github-actions/*.test.mjs— 19개 통과 -corepack pnpm typecheck-corepack pnpm build-corepack pnpm --filter @aegisai/api prisma:validate-node --test test/runtime/*.test.mjs-git diff --checkCloses #250 ## Summary by CodeRabbit * New Features * Added secure SAST repository fetching at a fixed commit with shallow, detached checkouts and cleanup of credentials and repository metadata. * Added repository preflight checks for unsafe paths, symlinks, file types, size limits, and deterministic inventory results. * Added workload and preflight attestations to validate scan scope and prevent replay or tampering. * Added secure, expiring repository credential handoff with wipe and revocation tracking. * Documentation * Updated configuration examples and deployment guidance for the new attestation keys. * Tests * Added coverage for repository fetching, preflight decisions, credential lifecycle, and attestation validation.