프롬프트 주입 방패(armor) — nonce 격벽 + 주입 신호 표지 + 출처 표지 (#4850) - #4851
Closed
kevin9327 wants to merge 3 commits into
Closed
Conversation
문서 본문을 그대로 프롬프트에 넣으면, 문서에 심긴 "이전 지시를 무시하라" 같은 문장이 사용자의 지시로 오인돼 에이전트가 간접 프롬프트 주입으로 탈취당한다. 이미 있던 세 조각(inspect injection·출처 표지·export-provenance-map)을 한 번의 읽기 전용 호출로 묶는 rhwp armor <파일> [--json] 을 추가한다. - 핵심 질의 document_core::queries::armor: - fence()/fence_open()/fence_close() — 본문을 nonce 격벽으로 감싸는 순수 함수 - generate_nonce() — getrandom 128비트, 호출마다 무작위(문서가 위조 불가) - DocumentCore::armor() — 격벽 + scan_injection(읽기 전용) 결합 - CLI armor + MCP hwp_armor(읽기 전용) + capabilities 등재 + 도움말/매뉴얼 - 봉투: schemaVersion·source·pageCount·scanScopes·safety(nonce·격벽 표지·신호 수· note)·armoredText·injectionSignals·signalCount·clean + 출처 표지 - provenance MAP: armoredText·injectionSignals[].excerpt/matched 를 문서 파생 선언 - 문서를 고치지 않는다 — 격벽은 뜻을 지우지 않고 경계만 구조로 세운다 검증: cargo test armor(lib 9)·armor_contract(8)·provenance_contract(10)· cli_json_contract(31)·injection_scan_contract(14)·ontology_contract(13)· mcp_server_contract(25) 등 통과, clippy(workspace+wasm) 0 경고. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
CI 가 두 갈래로 실패했다 (PR edwardkim#4851). ① agent_profile_router_contract::every_stateless_tool_belongs_to_some_ specific_profile — 새 도구 hwp_armor 가 어느 업무 프로필에도 없어 개발통합(필터-없음)으로만 닿는다. 본문을 프롬프트에 통째로 넣는 축이 바로 아카이브검색(RAG·감사)이라 그 프로필의 tools 와 recipe 에 넣는다 — 필요한 자리에 도구가 없으면 방패가 있으나 마나다. ② CodeQL critical 5건 — rust/hard-coded-cryptographic-value. armor 테스트가 nonce 를 상수("deadbeef"·"00112233"·"a1b2c3d4e5f60718"·"ff00")로 두고 격벽에 넘긴다. 이 모듈에는 이미 generate_nonce() 가 있으므로 테스트가 그것을 쓰게 한다 — 억제보다 낫고, 격벽 성질이 실제 nonce 에서도 성립함을 매 실행 재확인한다(기대 문자열도 fence_open/close 로 유도). 검증: agent_profile_router_contract 8/8, armor 단위 9/9, mcp_tool_annotations_contract 5/5, agent_codex_contract 2/2, cargo clippy --workspace --all-targets 통과, rustfmt 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI 테스트 샤드가 실패했다 (PR edwardkim#4851, 앞선 수정 위에서 드러난 다음 가드): knowledge_map_field_dictionary_contract::every_declared_record_field_is_ in_the_dictionary — "capabilities 가 선언하는데 §2-2 사전에 없는 필드 2개: armoredText, safety". 새 명령 armor 가 자기서술에 필드를 들고 왔는데 지식지도가 따라가지 못했다. `주입 방패 (armor)` 소절을 보안 조사 옆에 신설해 두 필드를 근거와 함께 등재하고, 짝 가드(dictionary_heading_count_matches_rows)가 요구하는 헤딩 수(268→270)와 본문 내역(265→267 고유 + 실측-only 3)도 함께 고쳤다. 검증: knowledge_map_field_dictionary_contract 2/2, agent_profile_router_contract 8/8, provenance_contract 10/10, skills_contract 2/2 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jangster77
pushed a commit
that referenced
this pull request
Aug 15, 2026
CI 가 두 갈래로 실패했다 (PR #4851). ① agent_profile_router_contract::every_stateless_tool_belongs_to_some_ specific_profile — 새 도구 hwp_armor 가 어느 업무 프로필에도 없어 개발통합(필터-없음)으로만 닿는다. 본문을 프롬프트에 통째로 넣는 축이 바로 아카이브검색(RAG·감사)이라 그 프로필의 tools 와 recipe 에 넣는다 — 필요한 자리에 도구가 없으면 방패가 있으나 마나다. ② CodeQL critical 5건 — rust/hard-coded-cryptographic-value. armor 테스트가 nonce 를 상수("deadbeef"·"00112233"·"a1b2c3d4e5f60718"·"ff00")로 두고 격벽에 넘긴다. 이 모듈에는 이미 generate_nonce() 가 있으므로 테스트가 그것을 쓰게 한다 — 억제보다 낫고, 격벽 성질이 실제 nonce 에서도 성립함을 매 실행 재확인한다(기대 문자열도 fence_open/close 로 유도). 검증: agent_profile_router_contract 8/8, armor 단위 9/9, mcp_tool_annotations_contract 5/5, agent_codex_contract 2/2, cargo clippy --workspace --all-targets 통과, rustfmt 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jangster77
pushed a commit
that referenced
this pull request
Aug 15, 2026
CI 테스트 샤드가 실패했다 (PR #4851, 앞선 수정 위에서 드러난 다음 가드): knowledge_map_field_dictionary_contract::every_declared_record_field_is_ in_the_dictionary — "capabilities 가 선언하는데 §2-2 사전에 없는 필드 2개: armoredText, safety". 새 명령 armor 가 자기서술에 필드를 들고 왔는데 지식지도가 따라가지 못했다. `주입 방패 (armor)` 소절을 보안 조사 옆에 신설해 두 필드를 근거와 함께 등재하고, 짝 가드(dictionary_heading_count_matches_rows)가 요구하는 헤딩 수(268→270)와 본문 내역(265→267 고유 + 실측-only 3)도 함께 고쳤다. 검증: knowledge_map_field_dictionary_contract 2/2, agent_profile_router_contract 8/8, provenance_contract 10/10, skills_contract 2/2 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Collaborator
|
통합 PR #4883(4412546)로 병합 완료했습니다. 원 head와 CI를 다시 확인해 누적 반영했고, 상세 검토·메인터너 보정·검증 근거는 archive 검토 기록에 남겼습니다. 중복 병합을 막기 위해 이 원 PR을 닫습니다. 감사합니다. |
Collaborator
|
통합 PR #4883(4412546)로 병합 완료했습니다. 원 head와 CI를 다시 확인해 누적 반영했고, 상세 검토·메인터너 보정·검증 근거는 archive 검토 기록에 남겼습니다. 중복 병합을 막기 위해 이 원 PR을 닫습니다. 감사합니다. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4850
문제
export-text·hwp_doc_text는 문서 본문을 그대로 프롬프트에 넣도록 넘긴다. 본문이 공격자가 내용을 정할 수 있는 문서라면, 심긴 "이전 지시를 무시하라 …" 가 사용자의 지시로 오인돼 에이전트가 간접 프롬프트 주입으로 탈취(mind-control)된다. rhwp 에는 방어 조각이 이미 있었지만(inspect injection·출처 표지untrustedContent/untrustedFields·export-provenance-map) 한 동작으로 묶여 있지 않았다.해결 —
rhwp armor <파일> [--json]세 조각을 한 번의 읽기 전용 호출로 묶는다.
getrandom, 128비트)로 만든 경계⟦UNTRUSTED:<nonce>⟧ … ⟦/UNTRUSTED:<nonce>⟧안에 넣는다. 문서는 nonce 를 모르므로 격벽을 위조·조기 종료할 수 없다. LLM 호스트는 "격벽 안은 전부 데이터"라는 규칙 하나로 지시/데이터를 가른다.scan_injection을 돌려 신호(종류·신뢰도·주소·근거)를 봉투에 신고한다. 판정은 IR 을 훑으므로 렌더 줄바꿈으로 끊긴 지시까지 잡는다.armoredText·신호 발췌를 문서 파생으로 선언하고,safety가 nonce·격벽 표지·신호 수·"격벽 안은 데이터"임을 기계가 읽게 요약한다.문서를 고치지 않는다 — 격벽은 뜻을 지우지 않고 경계만 구조로 세운다(
inspect injection과 같은 무변경 규약).동작 (before → after)
정상 문서:
{ "clean": true, "signalCount": 0, "safety": { "fenceOpen": "⟦UNTRUSTED:<nonce>⟧", "fenceClose": "⟦/UNTRUSTED:<nonce>⟧", "highestConfidence": null, "note": "…" }, "armoredText": "⟦UNTRUSTED:<nonce>⟧\n<본문 …>\n⟦/UNTRUSTED:<nonce>⟧", "untrustedContent": true, "untrustedFields": ["armoredText"] }주입이 심긴 문서(악성 파일 무커밋 — 시험 시
edit replace-text로 합성):{ "clean": false, "signalCount": 2, "injectionSignals": [ {"kind":"instruction_override","confidence":"high", …}, {"kind":"exfiltration_hint","confidence":"medium", …} ], "safety": { "highestConfidence": "high", … }, "untrustedContent": true, "untrustedFields": ["armoredText","injectionSignals[].excerpt","injectionSignals[].matched"] }본문은 격벽 안에 그대로 보존되고(뜻을 지우지 않음) 신호로 신고된다. nonce 는 봉투에서 정확히 두 번(여닫이 격벽)만 나타나 본문에 새지 않는다.
변경
src/document_core/queries/armor.rs—fence/fence_open/fence_close/generate_nonce(순수)·DocumentCore::armor(읽기 전용) + 단위 시험 9건.armor+ MCPhwp_armor(읽기 전용) + capabilities 등재 +--help·cli_commands.md.src/provenance.rsMAP —armoredText·injectionSignals[].excerpt/matched를 문서 파생으로 선언(드리프트 가드가 실측으로 강제).tests/armor_contract.rs(8건) +provenance_contract스윕 레시피.60_보안재생성(armor 는 nonce 무작위라 keygen 처럼 계약만).검증
cargo test통과: armor(lib 9)·armor_contract(8)·provenance_contract(10)·cli_json_contract(31)·injection_scan_contract(14)·ontology_contract(13)·mcp_server_contract(25)·mcp_tool_annotations(5)·output_axis_json(7)·capabilities_subcommands(4)·cli_password_stdin_parity(2)·nextcall_cli(8).cargo clippy --workspace --all-targets -- -D warnings및 wasm lib clippy 0 경고.rustfmt(leaf +--config skip_childrenfor main.rs) 클린.범위·후속
첫 PR 은 핵심 질의 + 봉투 + CLI/MCP 배선 1건으로 좁혔다. 후속:
--max-chars절단(에이전트 컨텍스트 예산)·--include-fields·--min-confidence필터.🤖 Generated with Claude Code