Skip to content

feat: 006 고정 커밋 fetch 및 hostile preflight 구현 - #251

Merged
goodtu02 merged 2 commits into
devfrom
feat/250-006-hardened-fetch-preflight
Jul 24, 2026
Merged

feat: 006 고정 커밋 fetch 및 hostile preflight 구현#251
goodtu02 merged 2 commits into
devfrom
feat/250-006-hardened-fetch-preflight

Conversation

@goodtu02

@goodtu02 goodtu02 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

🎋 작업 중인 브랜치 및 이슈 - 브랜치: feat/250-006-hardened-fetch-preflight - 이슈: #250 - 선행 PR: #249 (병합 완료) ## 🔎 주요 변경 사항 - signed workload identity attestation을 tenant/repository/scan/attempt/fixed commit에 결합하고, 한 attempt당 한 번만 발급되는 단기 REPO_READ credential lease를 구현했습니다. - credential 값은 opaque 메모리 버퍼와 검증된 tmpfs GIT_ASKPASS 파일에서만 사용하고, 영속화·로그·argv·일반 env 노출 없이 fetch 종료 시 zeroize/wipe하도록 구현했습니다. - lease 예약을 RUNNING durable 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 및 .git metadata를 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와 signed ACCEPT | REJECT | RESTRICTED_ESCALATION preflight 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 --check Closes #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.

@goodtu02 goodtu02 added the ✨ feat 새로운 기능 추가 label Jul 24, 2026
@goodtu02 goodtu02 self-assigned this Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This 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.

Changes

Production SAST runtime

Layer / File(s) Summary
Contracts, configuration, and lease persistence
.env.example, apps/api/.env.example, apps/api/prisma/*, apps/api/src/config/*, packages/shared/src/*, deploy/oracle/*
Adds attestation key configuration, shared SAST and attestation types, environment validation rules, and the SastRepositoryCredentialLease schema with lifecycle constraints and relations.
Attested credential brokerage
apps/api/src/token-broker/*, apps/api/test/token-broker/*, apps/api/test/support/*
Adds workload identity attestation issuance and verification, ephemeral credentials, attempt-bound lease transitions, persistent audit retrieval, and broker lifecycle integration.
Fixed-commit repository fetch
apps/api/src/control-plane/*, apps/api/src/scan-plane/*, apps/api/test/scan-plane/repository-fetch.service.e2e-spec.ts
Adds repository target lookup, tmpfs verification, bounded Git execution, immutable shallow fetches, tree parsing, credential cleanup, and .git removal.
Hostile repository preflight
apps/api/src/scan-plane/repository-preflight*, apps/api/test/scan-plane/repository-preflight.service.e2e-spec.ts
Adds path normalization, collision and symlink checks, entry classification, decision reason codes, inventory digests, and signed preflight attestations.
Validation and runtime documentation
apps/api/test/architecture/*, apps/api/test/config/*, specs/006-production-sast-runtime-design/*
Adds architecture and configuration contract tests and records the implemented Phase 5 runtime checkpoint and completed tasks.

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
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 cover T022–T024 and the required security, attestation, metadata, and regression-test requirements from #250.
Out of Scope Changes check ✅ Passed The remaining changes to types, migrations, env examples, docs, and tests all support the stated runtime objectives.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: fixed-commit fetch and hostile preflight implementation.
✨ Finishing Touches
📝 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: 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".

Comment thread apps/api/src/scan-plane/repository-fetch.service.ts
Comment thread apps/api/src/scan-plane/repository-preflight.service.ts
Comment thread apps/api/src/token-broker/token-broker.service.ts
Comment thread apps/api/src/config/config.schema.ts

@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: 6

🧹 Nitpick comments (2)
packages/shared/src/types/production-architecture.ts (1)

371-390: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing shared ISSUER constant (see consolidated comment).

audience/version are exported as typed constants and consumed consistently elsewhere, but no equivalent constant exists for issuer, which is duplicated as a hardcoded literal in token-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 win

Non-concurrent unique index creation blocks writes on existing tables.

These two CREATE UNIQUE INDEX statements run against the already-populated RepositoryBinding and ScanRequest tables (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 CONCURRENTLY here won't work as-is: Prisma wraps multi-statement migration files in a transaction, and CREATE INDEX CONCURRENTLY cannot 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

📥 Commits

Reviewing files that changed from the base of the PR and between 60618ea and dd6245d.

📒 Files selected for processing (40)
  • .env.example
  • apps/api/.env.example
  • apps/api/prisma/migrations/20260724120000_sast_repository_credential_lease/migration.sql
  • apps/api/prisma/schema.prisma
  • apps/api/src/config/config.module.ts
  • apps/api/src/config/config.schema.ts
  • apps/api/src/config/config.types.ts
  • apps/api/src/control-plane/control-plane.service.ts
  • apps/api/src/control-plane/control-plane.types.ts
  • apps/api/src/scan-plane/credential-tmpfs-verifier.service.ts
  • apps/api/src/scan-plane/repository-fetch.service.ts
  • apps/api/src/scan-plane/repository-git-executor.ts
  • apps/api/src/scan-plane/repository-preflight-attestation.service.ts
  • apps/api/src/scan-plane/repository-preflight.service.ts
  • apps/api/src/scan-plane/scan-plane.module.ts
  • apps/api/src/token-broker/prisma-repository-credential-lease.store.ts
  • apps/api/src/token-broker/repository-credential-lease.store.ts
  • apps/api/src/token-broker/token-broker.dto.ts
  • apps/api/src/token-broker/token-broker.module.ts
  • apps/api/src/token-broker/token-broker.service.ts
  • apps/api/src/token-broker/token-broker.types.ts
  • apps/api/src/token-broker/token-credential-issuer.service.ts
  • apps/api/src/token-broker/workload-identity-attestation.service.ts
  • apps/api/test/architecture/production-architecture-contracts.e2e-spec.ts
  • apps/api/test/config/config.env-files.e2e-spec.ts
  • apps/api/test/scan-plane/repository-fetch.service.e2e-spec.ts
  • apps/api/test/scan-plane/repository-preflight.service.e2e-spec.ts
  • apps/api/test/support/in-memory-repository-credential-lease.store.ts
  • apps/api/test/token-broker/prisma-repository-credential-lease.store.e2e-spec.ts
  • apps/api/test/token-broker/token-broker.e2e-spec.ts
  • apps/api/test/token-broker/token-credential-issuer.service.e2e-spec.ts
  • deploy/oracle/.env.example
  • deploy/oracle/BOOTSTRAP.md
  • packages/shared/src/index.ts
  • packages/shared/src/types/production-architecture.ts
  • packages/shared/src/types/sast-fetch.ts
  • 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/quickstart.md
  • specs/006-production-sast-runtime-design/tasks.md

Comment thread apps/api/prisma/schema.prisma
Comment thread apps/api/src/config/config.schema.ts
Comment thread apps/api/src/scan-plane/repository-fetch.service.ts Outdated
@goodtu02
goodtu02 merged commit 02fb14b into dev Jul 24, 2026
2 checks passed
@goodtu02
goodtu02 deleted the feat/250-006-hardened-fetch-preflight branch July 24, 2026 14:34
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 고정 커밋 fetch 및 hostile preflight 구현

1 participant