Skip to content

fix: #92 clean 삭제 안전성 및 오탐 방지 강화 - #97

Merged
JeremyDev87 merged 11 commits into
masterfrom
fix/92-clean-delete-safety
Jul 15, 2026
Merged

fix: #92 clean 삭제 안전성 및 오탐 방지 강화#97
JeremyDev87 merged 11 commits into
masterfrom
fix/92-clean-delete-safety

Conversation

@JeremyDev87

@JeremyDev87 JeremyDev87 commented Jul 15, 2026

Copy link
Copy Markdown
Owner

What

  • schema v3 cleanSafety에 분석 당시 SHA-256, stable file identity, stable parent identity를 기록합니다.
  • Unix clean --apply는 검증된 candidate parent 및 <root>/.kratos/clean-quarantine/ descriptor 사이에서 renameat으로 이동합니다.
  • 이동 객체는 openat(..., O_NOFOLLOW)로 재검증하며 자동으로 물리 unlink하지 않고 owner-only quarantine에 보존합니다.
  • 복원은 pinned descriptor 간 linkat no-clobber이며 quarantine link도 보존합니다.
  • stale·재생성·symlink·중복·legacy·root escape·threshold 미달 후보는 fail-closed 처리합니다.
  • framework/script/tooling 오탐 fixture와 CLI/report/native package 회귀 검증을 추가합니다.

Why

clean은 기본 preview여야 하며, 명시적 --apply에서도 분석 후 파일이나 pathname 해석이 달라지면 unrelated/out-of-root pathname을 삭제하면 안 됩니다. Portable POSIX는 opened-file 검증과 exact directory-entry unlink를 원자적으로 결합하지 못하므로, 검증된 bytes를 retained quarantine에 남겨 final verify→unlink race를 제거했습니다.

Scope / Risk

  • Closes [v1 readiness][kratos] Clean/delete safety and false-positive fixture gate #92
  • schema current version은 3이며 v2/legacy reader 호환은 유지하되 apply는 safety evidence 부재로 fail-closed합니다.
  • successful apply는 원래 코드 경로에서 파일을 제거하지만 bytes는 .kratos/clean-quarantine/에 보존하므로 수동 정리 전까지 디스크를 사용합니다.
  • Candidate file과 invocation directory 모두 자동 unlink하지 않으며, 실패한 pre-move 시 빈 invocation directory가 남을 수 있습니다.
  • 기존 공개 CleanOutcome { deleted_files, skipped_files }와 기존 함수 반환형은 source-compatible하게 유지하고, 새 CleanApplyOutcome/*_detailed API가 확인된 quarantine 경로와 per-file failure를 제공합니다.
  • Post-move 검증 실패 시 가변 quarantine entry를 원래 코드 경로로 자동 복원하지 않습니다. 원래 경로가 비어 있으면 code-tree removal count에 포함하며, quarantine pathname을 신뢰할 수 없으면 false path 대신 unresolved failure로 보고합니다.
  • cross-filesystem quarantine rename은 copy/unlink fallback 없이 실패합니다.
  • report root 자체의 동시 relocation은 지원 threat model 밖입니다. Private quarantine 직접 조작이 발생해도 Kratos는 그 entry를 물리 unlink하지 않습니다.
  • Stable Rust에서 persistent file ID를 제공하지 않는 Windows는 schema evidence를 null로 기록해 clean --apply를 fail-closed하며, scan/preview/package는 유지합니다.
  • version/tag/publish/release/merge/auto-merge는 수행하지 않습니다.

Validation

현재 head 7a642aafb8eb65ba6059cb082bd559c148bba24e 기준:

  • cargo test --workspace — PASS
  • npm run verify — PASS (9 pass, native 환경-gated 1 skip)
  • cargo clippy --workspace --all-targets — exit 0, 기존 warning만 존재
  • cargo metadata --locked --no-deps --format-version 1 — PASS
  • cargo check -p kratos-core -p kratos-node --target x86_64-pc-windows-msvc --locked — PASS
  • git diff --check — PASS
  • focused clean race suites — 8/8 PASS
  • current-source native package smoke — 3/3 PASS; Unix code-path removal과 retained bytes 일치 검증
  • release CLI smoke — schema 3, identity/parentIdentity, preview/stale 보존, unchanged code path 제거, reapply 0 PASS
  • Windows package smoke — packaged clean --apply 후 모든 candidate bytes 보존 검증

Wiki Context Manifest

Review boundary

Draft PR입니다. Exact-head security/product ddalggak review와 current-head GitHub checks가 blocker 0일 때만 manual merge를 권고합니다.

@JeremyDev87 JeremyDev87 self-assigned this Jul 15, 2026
@JeremyDev87

Copy link
Copy Markdown
Owner Author

Review pending — current head 2eceb8f95a01655b47717c0f485737027604668f.

Scope: complete PR #97 diff for issue #92, including clean/apply filesystem safety, schema-v3 cleanSafety compatibility, false-positive fixtures, CLI/report/native packaging surfaces.

Validation: local cargo test --workspace, npm run verify, clippy, locked metadata, diff check, and current-source native packed smoke are green. GitHub CI is still pending on Node 18/24 and Rust Workspace; no approval/ready conclusion is made while checks are non-terminal.

Blocking findings: review not yet concluded; current-head review and terminal CI remain required.

Human action boundary: no merge, release, version bump, tag, or publish performed. Manual merge remains the owner gate after a blocker-free current-head verdict.

@JeremyDev87
JeremyDev87 force-pushed the fix/92-clean-delete-safety branch 3 times, most recently from 67f960d to 1f23b5e Compare July 15, 2026 16:13
@JeremyDev87

JeremyDev87 commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

PENDING — current head 7a642aafb8eb65ba6059cb082bd559c148bba24e.

  • 범위: issue [v1 readiness][kratos] Clean/delete safety and false-positive fixture gate #92 전체 diff와 prior security/product findings closure
  • architecture fix: final verify→unlink를 제거하고 verified bytes를 .kratos/clean-quarantine/에 보존
  • compatibility: legacy CleanOutcome struct literal/함수 반환형 유지; 상세 결과는 CleanApplyOutcome/*_detailed
  • Unix move: pinned directory descriptor + renameat; production linkat/unlinkat 없음, post-move mismatch 자동 복원 없음
  • accounting: post-move failure에서 원래 path 부재도 code-tree removal count에 반영; confirmed quarantine path만 노출하고 unresolved pathname은 failure로 명시
  • focused race suite: 8/8 PASS, matching quarantine hard-link retained 및 원래 path 재생성 accounting 포함
  • 로컬: workspace/npm/clippy/metadata/Windows cross-check/diff check PASS
  • current-source native package: 3/3 PASS; Unix retained bytes와 original bytes 일치 검증
  • release CLI: schema 3, preview/stale 보존, unchanged code path 제거, reapply 0 PASS
  • current-head GitHub CI run 29444526186: 10/10 PASS
  • aedaa43 security lane은 PASS였고 product lane의 유일한 Medium dry-run 문구를 수정했습니다. fresh exact-head combined review terminal result 전까지 draft/manual-merge hold

@JeremyDev87
JeremyDev87 force-pushed the fix/92-clean-delete-safety branch 2 times, most recently from 08babf7 to 49c9da0 Compare July 15, 2026 16:28
SHA-256 분석 스냅샷·파일/부모 identity를 결속하고 quarantine 재검증과 fail-closed apply를 적용합니다. schema v3 계약·CLI/native smoke·framework false-positive 회귀를 함께 갱신합니다.

Co-authored-by: Hermes
@JeremyDev87
JeremyDev87 force-pushed the fix/92-clean-delete-safety branch from 49c9da0 to 6df3d87 Compare July 15, 2026 16:33
Windows에서는 persistent file identity evidence가 null이고 clean apply가 비활성화되는 계약을 패키지 smoke에서 검증합니다.\n\nCo-authored-by: Hermes
@JeremyDev87
JeremyDev87 force-pushed the fix/92-clean-delete-safety branch from 8ce14c1 to a6d2465 Compare July 15, 2026 16:40
@JeremyDev87

Copy link
Copy Markdown
Owner Author

Independent security/spec review verdict — current HEAD

Verdict: PASS for the reviewed issue #92 scope.

Reviewed current PR head a6d24657f3d19b6b6583877f9bd226464ce83484 against the complete issue #92 diff, report schema-v3/cleanSafety contract, clean/apply call chains, and race regressions.

Blocking findings remediated

  • Candidate-parent same-inode relocation is fail-closed: source parent real containment and identity are rechecked around the quarantine rename; a relocated/symlinked parent cannot redirect the destructive path outside the report root.
  • Restore is fail-closed: original parent containment and identity are rechecked before no-clobber hard-link restoration.
  • Quarantine-directory relocation/pathname replacement is fail-closed: quarantine root containment, exact file identity, content, and the final path are rechecked immediately before deletion.
  • Windows stable Rust does not expose the required file identity API. The implementation now emits no stable identity on Windows and therefore does not permit clean/apply; the Windows package smoke contract asserts this fail-closed behavior.

Evidence

  • cargo test --workspace: PASS
  • cargo clippy --workspace --all-targets: PASS; only existing warnings remain
  • npm run verify: PASS
  • Native macOS release build + node --test test/package-smoke.test.js: 3/3 PASS
  • Windows target cross-check: cargo check -p kratos-node --release --target x86_64-pc-windows-msvc: PASS
  • git diff --check: PASS
  • GitHub CI run 29433417431 for this HEAD: all Rust, Node 18/20/22/24, and native packaging targets PASS, including Windows.

Boundaries / nonblocking note

  • Empty quarantine-directory cleanup errors remain intentionally nonblocking residue-accounting behavior; they do not introduce an additional outside-root deletion path.
  • PR remains draft. No merge, release, version bump, tag, or publish was performed.

renameat/linkat/unlinkat와 openat 기반 검증으로 parent 및 quarantine pathname 교체가 외부 파일 삭제로 이어지지 않게 합니다.

schema v3 README와 legacy fail-closed 정책도 동기화합니다.

Co-authored-by: Hermes
패키징된 Windows CLI로 실제 clean --apply를 실행하고 모든 deletion candidate의 내용이 보존되는지 확인합니다.

Co-authored-by: Hermes
Windows future-schema CLI smoke가 null identity를 허용하고 apply 보존을 검증하도록 하며 active roadmap을 schema v3로 맞춥니다.

Co-authored-by: Hermes
Portable POSIX의 final verify-to-unlink race를 제거하기 위해 검증된 파일을 .kratos/clean-quarantine 아래에 유지하고 원래 코드 경로에서만 제거합니다. CLI, localized README, 계약 문서와 package smoke가 retained quarantine semantics를 검증합니다.

Co-authored-by: Hermes
deleted_files 필드는 코드 경로에서 보존 격리된 수를 뜻하는 compatibility count로 유지하고 실제 보존 경로는 quarantined_files에 노출합니다.

Co-authored-by: Hermes
복원·건너뜀·실패 중 남은 quarantine object도 CleanOutcome과 CLI에서 last-known path로 노출하고, 코드 경로에서 이동된 수와 보존 파일 수를 분리합니다.

Co-authored-by: Hermes
Candidate file뿐 아니라 invocation directory도 자동 unlink하지 않아 same-credential namespace 교체가 unrelated entry 삭제로 이어질 마지막 pathname race를 제거합니다.

Co-authored-by: Hermes
Post-move 검증 실패 시 가변 entry를 코드 경로로 link하지 않고 residue와 실제 코드 경로 제거를 정확히 집계합니다. 기존 CleanOutcome 공개 shape는 유지하고 상세 결과는 별도 API로 제공합니다.

Co-authored-by: Hermes
Dry-run 안내와 CLI 계약 테스트를 자동 삭제가 아닌 원래 코드 경로의 보존 격리 의미로 맞춥니다.

Co-authored-by: Hermes
@JeremyDev87
JeremyDev87 marked this pull request as ready for review July 15, 2026 19:51
@JeremyDev87
JeremyDev87 merged commit 62a5e53 into master Jul 15, 2026
10 checks passed
@JeremyDev87
JeremyDev87 deleted the fix/92-clean-delete-safety branch July 15, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v1 readiness][kratos] Clean/delete safety and false-positive fixture gate

1 participant