Skip to content

feat(tool-capability): admit external Claude plugins fail-closed - #560

Merged
seonghobae merged 116 commits into
mainfrom
feat/tool-capability-plugin-admission-545
Sep 9, 2026
Merged

feat(tool-capability): admit external Claude plugins fail-closed#560
seonghobae merged 116 commits into
mainfrom
feat/tool-capability-plugin-admission-545

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Why

Issue #545 is a customer-facing Tool / Capability gap: wholesale marketplace installation would turn third-party prompts, hooks, MCP servers, and network access into implicit runtime authority. Anthropic catalog/review metadata is discovery evidence, not CWL approval. Noema owns external-extension admission, product/role/time Policy / Approval, activation and invocation authority. AppGuardrail and quarantine-sandbox-runtime remain scanner/analysis/isolation owners, EgressWeave remains outbound owner, Keyverse remains identity/secret-handle owner, and provider/model routing remains contextual-orchestrator.

Retained RED → causal repair evidence

The branch preserves hosted hostile-case REDs before minimum causal fixes for immutable catalog identity, forged admission/activation/receipt authority, live scan/policy revocation, Policy / Approval scope issuance, activation chronology/runtime expiry, exact-admission provenance, replay equality/data retention, cryptographic-provider ownership, hostile accessor stability, independently owned scanner/quarantine evidence, invocation-time owner-evidence TOCTOU, publication-time authority drift across the asynchronous Web Crypto boundary, UTF-8 input bounding, and retained core edge-case coverage.

Recent authority evidence:

  • b52f5f10b6e757be45e1cab2a2dc1e8c38eb482c produced hosted RED for owner-evidence profile TOCTOU. Production ef46c8703957d9a06759717d9d391ccefe2c0bf2 moved the second owner-evidence read before the narrower core and passed one immutable validated snapshot; e4cd6d023e7f9a58bbce6e5c0ef5039bfc294889 restored exact-head application coverage.
  • Test-only fe2e8d4d786f534182ba3875cba498dec7e77e68 produced hosted application RED 34284153988, job 102255685361: exact checkout/live-base/lockfile/install/typecheck passed, then test/external-extension-publication-time-authority.test.ts failed with 1 failed / 617 passed files and 4 failed / 4,229 passed tests because Policy / Approval revocation, catalog drift, AppGuardrail profile drift, and runtime expiry across Web Crypto still published accepted receipts. Minimum production repair c3786306bd42485c3db78dfb1b4ca5bd0ef8b586 revalidates those live authorities immediately after the async digest boundary; a37304cca6cc14d18ea5018cf9f20b70ef3668da carries the review/test cleanup.
  • Fresh review of a37304c... found that the exported invocation seam accepted unbounded instruction / observed_content, then canonicalized and UTF-8 encoded both before SHA-256. Test-only 3fdd0f8c9b8a1972f2a483fe2800d128c8c60d24 materialized the hosted application RED on CI 34287093432: the 8,193-byte multibyte hostile cases reached the boundary without the required synchronous rejection. Production 737f8700a66f01d1f52bf42bd48db5be39abf6bc adds the smallest Tool / Capability boundary repair: both fields are bounded to 8,192 UTF-8 bytes before replay-digest/Web Crypto work, with exact-boundary positive cases preserved.
  • Production 737f8700... then produced a distinct hosted coverage-only RED in application CI 34288116426, job 102268290127. Exact checkout, live-base verification, deterministic lock/install and typecheck passed, and all 618 files / 4,235 tests passed, but the mandatory 100% gate reported only the retained internal core observed_content non-string rejection (external-extension-admission-core.ts:978-979) as uncovered. Current test-only 5aab7c098f3478069127f34e398326415ec599a4 adds one direct core hostile case requiring observed_content must be a string; production behavior, Policy / Approval, foreign-owner boundaries and thresholds are unchanged.

Earlier retained hosted RED→repair generations for runtime-time authority, activation/invocation provenance, replay semantics, Web Crypto ownership, Policy / Approval accessor TOCTOU, activation-request TOCTOU, future-event chronology, stale fixtures, explicit owner-profile separation, and repository-minted policy fallback remain in branch history and review discussion.

Ownership and current authority

Current exact is test-only 5aab7c098f3478069127f34e398326415ec599a4, an ordinary non-force descendant of GitHub-verified protected main@36e5cf957ee20a8bb3e19ff50fea6c97771d2ba1. ADR 0015 remains Proposed. Protected .github/reviewer source is untouched, and docs/product-technical-gap-baseline.md remains solely owned by #559.

context-graph-contracts#27 remains the future shared external-capability contract owner and currently has no immutable release. appguardrail#1099 remains scanner/provenance owner. AppGuardrail/quarantine evidence is not Noema approval; EgressWeave remains outbound owner; Keyverse remains identity/secret-handle owner; contextual-orchestrator remains provider/model-routing owner.

Current exact gate generation is application CI 34289599257 SUCCESS, reviewer-ci 34289599291 SUCCESS, required Security 34289599289 SUCCESS, and patch-validator-image 34289599248 IN_PROGRESS. The image job is assigned and currently building the exact-head patch-validator image; it is not passing evidence yet. Predecessor GREEN does not transfer.

Two current review threads cover owner-evidence snapshot mediation and publication-time revalidation across the Web Crypto boundary. Their production repairs are present in this exact head, but they remain unresolved deliberately until this unchanged head reaches all four terminal-success gates and a fresh source/review/ancestry sweep confirms no new valid finding.

Keep Draft until the unchanged exact head has all four applicable gates terminal SUCCESS, those addressed threads are resolved only after fresh verification, review authority is clean, and ancestry still contains the live protected main. Source integration does not claim live plugin installation, immutable shared-contract consumption, released AppGuardrail/quarantine policy artifacts, measured pilot completion, immutable Noema release, rollback rehearsal, deployment, durable lifecycle persistence (#561), or buyer completion.

Summary by CodeRabbit

  • 새로운 기능

    • 외부 Claude 커뮤니티 플러그인을 위한 로컬 fail-closed 승인 절차를 추가했습니다.
    • 정확한 소스 식별자, 독립 정책 승인, 보안·격리 증거를 검증합니다.
    • 활성화 및 호출 시 정책·카탈로그·유효 기간을 다시 확인하고, 중복 호출을 안전하게 처리합니다.
    • 제품 런타임에서 플러그인 실행은 허용하지 않습니다.
  • 문서

    • 외부 확장 승인, 롤백, 위협 대응 및 운영 절차를 관련 문서에 반영했습니다.
  • 테스트

    • 변조, 만료, 권한 변경, 카탈로그 변동 및 재생 충돌에 대한 검증을 추가했습니다.

Add a local Tool / Capability port for issue #545 so marketplace
metadata, mutable refs, Anthropic review, and plugin instructions
cannot become runtime authority. Exact commit/path/digest identity,
pinned AppGuardrail and quarantine receipts, product/role scope,
expiry/rollback, catalog-drift refusal, and idempotent
activation/invocation receipts fail closed. Product-runtime Claude
plugin wrappers are rejected. Keep a local ACL/test double until
context-graph-contracts publishes an immutable shared contract.
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 7dafaa43-9afc-498b-9e1f-3fd876bfeefa

📥 Commits

Reviewing files that changed from the base of the PR and between 36e5cf9 and 5aab7c0.

📒 Files selected for processing (32)
  • ARCHITECTURE.md
  • CHANGELOG.md
  • docs/CONTEXT_MAP.md
  • docs/OPERABILITY.md
  • docs/PRD.md
  • docs/TEST_STRATEGY.md
  • docs/TRACEABILITY.md
  • docs/TRD.md
  • docs/UML.md
  • docs/adr/0015-external-extension-admission.md
  • docs/adr/README.md
  • docs/threat-model.md
  • src/tool-capability/external-extension-admission.ts
  • src/tool-capability/internal/external-extension-admission-core.ts
  • src/tool-capability/internal/external-extension-invocation-digest.ts
  • test/external-extension-activation-forgery.test.ts
  • test/external-extension-activation-policy-snapshot.test.ts
  • test/external-extension-admission-provenance.test.ts
  • test/external-extension-admission.test.ts
  • test/external-extension-catalog-drift.test.ts
  • test/external-extension-core-window-contract.test.ts
  • test/external-extension-cross-admission-activation.test.ts
  • test/external-extension-invocation-digest-retention.test.ts
  • test/external-extension-owner-evidence-coverage.test.ts
  • test/external-extension-owner-evidence-separation.test.ts
  • test/external-extension-policy-approval-port.test.ts
  • test/external-extension-policy-authority.test.ts
  • test/external-extension-public-boundary-normalization.test.ts
  • test/external-extension-publication-time-authority.test.ts
  • test/external-extension-runtime-time-authority.test.ts
  • test/external-extension-sha256-provider-contract.test.ts
  • test/runtime-bounded-context-fitness.test.ts

📝 Walkthrough

Walkthrough

외부 Claude 커뮤니티 플러그인을 위한 로컬 fail-closed admission 포트를 추가했습니다. 정확한 source, catalog, Policy / Approval, AppGuardrail 및 quarantine 증거를 검증합니다. 활성화와 호출에서 authority drift, runtime window, replay 충돌을 재검증합니다.

Changes

외부 확장 admission

Layer / File(s) Summary
계약과 기본 검증
ARCHITECTURE.md, docs/adr/*, docs/PRD.md, docs/TRD.md, src/tool-capability/internal/external-extension-admission-core.ts
외부 확장 상태, descriptor, catalog, scan receipt, invocation receipt와 fail-closed 입력 검증을 추가했습니다. 관련 아키텍처, ADR, PRD, TRD, 위협 모델, 추적성 문서도 갱신했습니다.
Authority와 admission 연결
src/tool-capability/external-extension-admission.ts, src/tool-capability/internal/external-extension-admission-core.ts
Policy / Approval, AppGuardrail, quarantine receipt와 catalog를 admission에 결합했습니다. admission 결과에 authority를 바인딩하고 정책 및 소유자 증거를 스냅샷합니다.
Activation, invocation과 replay
src/tool-capability/external-extension-admission.ts, src/tool-capability/internal/external-extension-admission-core.ts, src/tool-capability/internal/external-extension-invocation-digest.ts
활성화와 호출에서 live authority, catalog, receipt, runtime window를 재검증합니다. 호출 envelope는 고정 순서로 직렬화하고 Web Crypto SHA-256 digest를 사용해 idempotent replay를 처리합니다.
검증과 경계 테스트
test/external-extension-*.test.ts, test/runtime-bounded-context-fitness.test.ts, docs/TEST_STRATEGY.md, docs/OPERABILITY.md
정책 및 catalog drift, provenance 위조, owner evidence 분리, 시간 권위, digest 보존, hostile 입력, product runtime 거부와 fail-closed 동작을 검증합니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant ExternalExtensionAdmission
  participant PolicyAuthority
  participant CatalogAndReceipts
  participant WebCrypto
  Caller->>ExternalExtensionAdmission: admitExternalExtension
  ExternalExtensionAdmission->>PolicyAuthority: resolvePolicyApproval
  ExternalExtensionAdmission->>CatalogAndReceipts: resolveCatalog and resolveScanReceipt
  ExternalExtensionAdmission-->>Caller: AdmittedExternalExtension
  Caller->>ExternalExtensionAdmission: activateExternalExtension
  ExternalExtensionAdmission->>PolicyAuthority: revalidate policy
  ExternalExtensionAdmission->>CatalogAndReceipts: revalidate owner evidence
  ExternalExtensionAdmission-->>Caller: ExternalExtensionActivation
  Caller->>ExternalExtensionAdmission: invokeExternalExtension
  ExternalExtensionAdmission->>WebCrypto: digest invocation envelope
  ExternalExtensionAdmission->>PolicyAuthority: revalidate live authority
  ExternalExtensionAdmission->>CatalogAndReceipts: revalidate catalog and receipts
  ExternalExtensionAdmission-->>Caller: invocation receipt or replay
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tool-capability-plugin-admission-545

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.

@seonghobae seonghobae added enhancement New feature or request priority: high High-priority or P1 work labels Sep 8, 2026 — with ChatGPT Codex Connector

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

유효 finding: admission은 external_extension_id, upstream_repository, upstream_commit_sha, upstream_path, artifact_sha256, marketplace_entry_sha256를 모두 pin하지만 invocation-time live catalog drift 검증은 현재 artifact digest와 commit SHA 두 필드만 비교합니다. 따라서 repository/path/marketplace digest 또는 catalog entry identity가 바뀌어도 invocation이 통과할 수 있어 PR의 “catalog drift cannot silently update an admitted extension” 계약을 완전히 만족하지 않습니다. Test-only exact 58f8bbadd6a4a3863d642883e40f4753f6dc291f에 네 누락 identity drift 회귀를 추가했습니다. Hosted RED가 실제 test execution까지 materialize된 뒤 full pinned-catalog identity를 최소 범위로 revalidate하고 exact-head GREEN을 다시 받기 전에는 merge authority가 없습니다.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fresh Tool / Capability boundary review found one additional authority defect independent of the current catalog-drift RED. Recording it without mutating the test-only head so the hosted RED generation can finish first.

Comment thread src/tool-capability/external-extension-admission.ts Outdated

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A second runtime-authenticity defect remains after the activation finding: the exported AdmittedExternalExtension is also a structurally constructible interface. Recording this separately so the current hostile activation RED generation is not rewritten while queued.

Comment thread src/tool-capability/external-extension-admission.ts Outdated
Comment thread src/tool-capability/external-extension-admission.ts Fixed

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fresh Tool / Capability + Policy / Approval boundary review found one remaining authority gap. admitExternalExtension() authenticates six-field source identity and AppGuardrail/quarantine scan receipts, but every product grant is still taken from the untrusted ExternalExtensionDescriptor: approval_status, allowed_product_repositories, allowed_execution_roles, the validity window and egress/isolation grant are not bound to an independently trusted Noema Policy / Approval issuance. With an otherwise valid catalog entry and scan receipts, a caller can self-assert approval_status: "active" and broaden the allowed product/role set; the module-private WeakSet then proves only that this self-asserted descriptor passed admission, and activateExternalExtension() treats those fields as approval authority. That violates ADR-0015's invariant that one product cannot use another product's approval and that upstream/scanner evidence is not CWL approval authority. Add a hostile public admission→activation regression using unchanged source/catalog/scan pins but a self-broadened product grant, then bind the grant fields to a separately pinned Noema Policy / Approval authority/receipt. Do not overload Anthropic catalog metadata or AppGuardrail/quarantine receipts as product-approval truth.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fresh exact-head review found one additional publication-time authority race after the existing owner-evidence repair. The finding is isolated to the async replay-digest boundary; no gate or foreign-owner boundary change is proposed.

Comment thread src/tool-capability/external-extension-admission.ts
Comment thread test/external-extension-publication-time-authority.test.ts Fixed

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fresh review finding on exact a37304cca6cc14d18ea5018cf9f20b70ef3668da: ExternalExtensionInvocationRequest.instruction and observed_content are untrusted text, but invokeBoundary() validates only type/non-empty/policy phrases and imposes no UTF-8 byte ceiling. The public boundary then snapshots both strings and digestExternalExtensionInvocationEnvelope() serializes and TextEncoder-encodes the entire envelope before Web Crypto SHA-256. Because this API is exported and can be called independently of the already-bounded /exchange HTTP body path, an oversized invocation can force avoidable Worker heap/CPU work before rejection. This is directly inconsistent with the repository threat model's 128 MB isolate / oversized-input DoS boundary.

Add a hostile test first that uses multibyte UTF-8 text to exceed the chosen bounded invocation-text contract while staying below it in JavaScript character count, and prove rejection occurs before the digest provider is reached. Also keep an exact-boundary positive case so the limit is byte-based rather than UTF-16-code-unit based. Then add the smallest fail-closed byte bound at the Tool / Capability boundary; do not solve this only at one HTTP adapter, and do not weaken the existing Policy / Approval, owner-evidence, replay, or publication-time checks.

Copy link
Copy Markdown
Contributor Author

Current-head hosted RCA advanced. Exact 737f8700a66f01d1f52bf42bd48db5be39abf6bc passed lockfile control, install, typecheck and all 618 files / 4,235 tests, but application CI 34288116426 failed the enforced 100% coverage gate at one retained core branch: external-extension-admission-core.ts:978-979, the fail-closed observed_content runtime type rejection. That is a real missing edge-case test, not a production regression. Minimal test-only repair 5aab7c098f3478069127f34e398326415ec599a4 exercises a non-string observed-content value directly at the internal core boundary and requires observed_content must be a string; production behavior and gates are unchanged. Fresh exact-head evidence is now authoritative.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T01:03:45.022807Z 5aab7c0 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@seonghobae
seonghobae merged commit e3aa77c into main Sep 9, 2026
17 of 18 checks passed
@seonghobae
seonghobae deleted the feat/tool-capability-plugin-admission-545 branch September 9, 2026 01:02
seonghobae added a commit that referenced this pull request Sep 9, 2026
Preserve #559 documentation delta while normally incorporating protected main e3aa77c. Authority wording remains to be refreshed on this branch before merge; predecessor workflow GREEN does not transfer.
seonghobae added a commit that referenced this pull request Sep 9, 2026
Preserve the #562 lockstep httpx2/httpcore2 2.12.0 delta and isolated pip-check regression while normally incorporating protected main e3aa77c. Predecessor workflow GREEN does not transfer.
seonghobae added a commit that referenced this pull request Sep 9, 2026
* test(docs): expose post-547 commercial authority drift

* test(docs): require current active commercial lanes

* docs: refresh commercial gap authority after #547

* test: advance patch-validator candidate authority

* test: reject superseded patch-validator candidate

* docs: refresh patch-validator exact authority

* test: require current #535 commercial authority

* docs: repair current commercial authority after hosted RED

* test(docs): require live post-558 commercial authority

Require the documentation lane to reflect protected #558 integration, the newly converged #535 exact head, and the newly observed #556 stacked head before production baseline text is repaired.

* docs(gap): repair post-558 live commercial authority

Bring #559's owned baseline in line with protected #558 integration, exact #535 convergence, and the newly observed #556 stack while preserving canonical owner boundaries and release-evidence discipline.

* test(docs): require latest observed #556 authority

Advance the documentation contract to the live #556 successor after its hosted release-test RED, while rejecting the superseded observation. Production baseline text follows in the causal repair commit.

* docs(gap): record live #556 successor and hosted RED

Update #559's sole documentation authority to the latest #556 exact head, preserve the observed hosted release-test failure as historical evidence, and keep the downstream stack non-authorizing until #535 reaches protected main.

* test(docs): require current #535 endpoint-repair authority

* docs: track current #535 gateway endpoint repair

* test(docs): require #535 coverage-repair authority

* docs: track #535 coverage-gate repair

* test(docs): require current central control-plane snapshot

* docs: refresh central control-plane snapshot

* test(docs): retire stale commercial authority assertions

* docs: refresh protected and claim-evidence authority

* test(docs): track protected #535 integration

* test(docs): track current claim-evidence head

* test(docs): bind post-535 protected authority

* test(docs): refresh current candidate contract

* test(docs): align claim-binding authority wording

* docs: refresh claim-evidence candidate authority

* test(docs): match hosted run authority casing

* docs: refresh live Noema commercial authority

* test(docs): track current Noema feature authority

* test(docs): bind commercial gap to live feature heads

* test(docs): require settled external-extension authority

* docs: converge commercial gap to current plugin admission

* test(docs): track active policy-approval RED

* docs: bind commercial gap to policy-approval RED

* docs: restore durable commercial gap owners

* docs: align external-extension authority after restack

* test: bind documentation authority to restacked #560

* test(d(docs): require complete gap authority schema

* docs: bind gap status to architecture authorities

* test(docs): reject stale tool-capability candidate

* docs: refresh current tool-capability evidence

* docs: refresh active extension authority

* docs: bind extension event chronology evidence

* docs: refresh extension chronology authority

* docs: bind gap baseline to hostile admission repair

* test(docs): bind live candidate to admission repair

* test(docs): preserve candidate ADR authority wording

* docs: refresh external-extension runtime-time authority

* test(docs): bind current runtime-time candidate authority

* test(docs): refresh live external-extension candidate authority

* docs: bind gap authority to invocation replay repair

* docs: converge on public replay authority repair

* docs: converge on activation revocation repair

* docs: converge on admission-bound invocation authority

* docs: bind commercial gap to exact admission provenance

* test(docs): require exact-admission candidate authority

* test(docs): bind candidate contract to exact admission

* docs: track crypto-provider RED authority

* test(docs): bind external-extension authority to Web Crypto repair

* docs: converge external-extension Web Crypto authority

* test(docs): follow current external-extension authority

* test(docs): assert public receipt binding authority

* test(docs): match activation revocation authority wording

* docs: converge baseline after #560 protected integration

* test: bind documentation authority to #560 integration

* test: treat external-extension admission as protected history

* test: extend protected integration history through #560

* test: move external-extension gap to lifecycle successor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request priority: high High-priority or P1 work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant