Skip to content

test(persistence): cover sensitive-arg guard on lazily-loaded extension types (swamp-club#484)#1471

Merged
stack72 merged 1 commit into
mainfrom
worktree-484
May 29, 2026
Merged

test(persistence): cover sensitive-arg guard on lazily-loaded extension types (swamp-club#484)#1471
stack72 merged 1 commit into
mainfrom
worktree-484

Conversation

@stack72
Copy link
Copy Markdown
Contributor

@stack72 stack72 commented May 29, 2026

Summary

Test-only follow-up to swamp-club#480 (PR #1469). That fix added a chokepoint in YamlDefinitionRepository.save() that refuses a literal value for a { sensitive: true } global argument. It resolves the type schema via the model registry — and for an extension type the registry only lazy-registers it. model edit never pre-loads the registry, so modelRegistry.get(type) returned undefined, the guard was silently skipped, and a literal secret leaked to disk. The fix promotes the lazy type (ensureTypeLoaded + an ensureLoaded fallback) before the guard.

No automated test covered that path. Existing unit/integration tests register types via defineModel, which eagerly populates the registry, so get() succeeds and the lazy-resolution fallback is never exercised. The defect surfaced only in manual clean-room verification.

This PR closes the gap:

  • integration/model_edit_sensitive_lazy_test.ts — source-mounts a real extension model (loaded lazily via the bundle catalog, not defineModel) with a sensitive global argument, seeds a definition holding a safe vault.get() expression, then drives model edit via stdin to swap in a literal secret. Asserts the write is refused with the specific sensitive-arg error and that nothing leaks to disk.
  • integration/test_helpers.tsrunCliCommand gains an optional stdin? parameter (additive; existing callers unaffected) so the test can drive model edit.

No production code changes.

Test Plan

  • New test passes; test-the-test: reverting the fix(models): refuse literal sensitive global arguments at rest (swamp-club#480) #1469 fix makes it fail (edit exits 0, the literal leaks), confirming it is a genuine regression guard.
  • Existing runCliCommand consumer (source_extension_rebundle_test.ts) still passes — helper change is backward-compatible.
  • deno check, deno lint, deno fmt --check, license-headers all clean.
  • Full suite: 6457 passed, 0 failed.

🤖 Generated with Claude Code

…on types (swamp-club#484)

Follow-up to swamp-club#480 (PR #1469). That fix added a
chokepoint in YamlDefinitionRepository.save() that refuses a literal value
for a `{ sensitive: true }` global argument, resolving the type schema via
the model registry. For an extension type the registry only lazy-registers
it, and `model edit` never pre-loads the registry, so modelRegistry.get(type)
returned undefined, the guard was silently skipped, and a literal secret
leaked to disk. The fix promotes the lazy type (ensureTypeLoaded + an
ensureLoaded fallback) before the guard — but no automated test covered it:
existing tests register types via defineModel, which eagerly populates the
registry, so the lazy-resolution fallback was never exercised.

Add an integration test that source-mounts a real extension model (loaded
lazily via the bundle catalog, not defineModel) with a sensitive global
argument, seeds a definition holding a safe vault.get() expression, then
drives `model edit` via stdin to swap in a literal secret — asserting the
write is refused with the specific sensitive-arg error and nothing leaks to
disk. Reverting the #1469 fix makes this test fail (edit exits 0, literal
leaks), confirming it is a genuine regression guard.

Also add optional stdin support to the runCliCommand integration helper
(additive; existing callers unaffected) so the test can drive `model edit`.

No production code changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Code Review

Clean, well-structured test-only PR that closes an important coverage gap for the secrets-at-rest guard on lazily-loaded extension types. No production code changes.

Blocking Issues

None.

Suggestions

  1. Minor refactor outside task scope (integration/test_helpers.ts): Hoisting TextDecoder into a shared const decoder also touches the non-stdin code path (lines 120–121). CLAUDE.md asks to keep blast radius small. This is harmless and arguably cleaner, but worth noting it slightly exceeds "only touch what's necessary."

  2. Large docblock (integration/model_edit_sensitive_lazy_test.ts:21–45): The module-level JSDoc is ~25 lines. CLAUDE.md prefers minimal comments ("one short line max"). That said, this matches the established pattern in sibling tests (model_create_sensitive_test.ts, lazy_registry_promotion_test.ts), and the context it provides for a security regression test is genuinely useful — so this is fine in practice.

Overall: the test design is solid — source-mounting a real extension (not defineModel) forces genuine lazy resolution, the precondition check (line 131–136) confirms the extension actually loaded, and the leak scan (readAllFiles over the entire repo dir) is thorough. The runCliCommand stdin addition is backward-compatible and well-documented. Approving.

@stack72 stack72 merged commit a24cef5 into main May 29, 2026
11 checks passed
@stack72 stack72 deleted the worktree-484 branch May 29, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant