Skip to content

[AAASM-4951] 🐛 (sdk): Redact URL userinfo + wrap sealed-but-writable tool seams#315

Merged
Chisanan232 merged 6 commits into
masterfrom
v0.0.1/AAASM-4951/fix/redact_userinfo_seam_guard
Jul 21, 2026
Merged

[AAASM-4951] 🐛 (sdk): Redact URL userinfo + wrap sealed-but-writable tool seams#315
Chisanan232 merged 6 commits into
masterfrom
v0.0.1/AAASM-4951/fix/redact_userinfo_seam_guard

Conversation

@Chisanan232

@Chisanan232 Chisanan232 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Target

  • Task summary:

    Two low-severity SDK-core hardening fixes (AAASM-4951):

    1. redactErrorMessage missed URL userinfo credentials. The scrubber
      covered Bearer/Authorization/JWT/sk-/?token= shapes but not
      scheme://user:pass@host/..., so a wrapped transport error carrying a
      userinfo-credentialed URL leaked the password into the
      registration-failure warning path. Added a userinfo scrub pass
      (//<user>:<pass>@ -> //<redacted>@) and updated the coverage comment.

    2. Seam guard skipped a sealed-but-writable tool. isSeamWritable
      required Object.isExtensible(target) for every case. Object.seal()
      sets extensible:false but leaves existing data props writable:true,
      so tool.invoke = wrapper would in fact succeed on a sealed tool - yet
      the guard returned false and skipped wrapping (an over-conservative
      false-negative that silently downgraded enforcement while a loud warning
      fired). Now an existing writable/settable slot is treated as writable
      regardless of extensibility; only the add-property (absent-slot) case is
      gated on Object.isExtensible. The same fix is applied to the duplicated
      inline check in hooks/openai-agents.ts (wrapFunctionToolInvoke). A
      genuinely frozen tool (non-extensible, non-writable slot) still
      skips-with-warning.

  • Task tickets:

    • Task ID: AAASM-4951
    • Relative task IDs:
      • N/A.
    • Relative PRs:
      • N/A.
  • Key point change (optional):

    • isSeamWritable: absent slot -> gated on Object.isExtensible; existing
      writable/settable slot -> writable regardless of extensibility.

Effecting Scope

  • Action Types:
    • 🔧 Fixing bug
      • 🟢 No breaking change
    • 🍀 Improving something (security)
  • Scopes:
    • 🤖 Framework hooks
    • ⛑️ Error handling
    • 🧪 Testing
      • 🧪 Unit testing
  • Additional description:
    SDK core only - no browser/UI surface. No public API change.

Description

  • src/core/redact.ts - add URL userinfo scrub pass in redactErrorMessage; update coverage comment.
  • src/core/tool-seam-guard.ts - split isSeamWritable so extensibility only gates the absent-slot (add-property) case.
  • src/hooks/openai-agents.ts - mirror the same split in the inline writability check.
  • Tests - userinfo redaction + credential-free-URL passthrough; sealed-but-writable tool now wrapped and DENY-enforced across the langchain and @openai/agents paths; existing frozen-tool skip-with-warning tests unchanged.

How to verify

pnpm install && pnpm typecheck && pnpm lint && pnpm test - all green (630 passed, 2 skipped).

Closes AAASM-4951

🤖 Generated with Claude Code

Chisanan232 and others added 5 commits July 21, 2026 13:29
A wrapped transport error can carry a `scheme://user:pass@host` URL whose
userinfo is credential material; the prior passes (Bearer/JWT/sk-/?token=)
did not cover it, leaking the password into the registration-failure warning
path. Add a userinfo scrub pass and note it in the coverage comment.

Refs AAASM-4951
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…through

Refs AAASM-4951
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…itable

Object.seal() sets extensible:false but leaves existing data properties
writable:true, so `tool.invoke = wrapper` succeeds on a sealed tool — yet the
old guard gated every case on Object.isExtensible and returned false, skipping
wrapping and silently downgrading enforcement. Split the two shapes: an existing
writable/settable slot is assignable regardless of extensibility; only the
add-property (absent-slot) case needs Object.isExtensible.

Refs AAASM-4951
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirror the isSeamWritable split in wrapFunctionToolInvoke's duplicated inline
writability check so a sealed @openai/agents function tool is wrapped and
governed instead of skipped-with-warning.

Refs AAASM-4951
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Assert a sealed (non-extensible, writable-invoke) tool gets its seam replaced
and a policy DENY is enforced, across both the langchain wrapper and the
@openai/agents inline path. The existing frozen-tool tests still assert the
skip-with-warning path is untouched.

Refs AAASM-4951
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Chisanan232

Copy link
Copy Markdown
Contributor Author

🤖 Claude Code review

Scope: SDK core only — no browser/UI surface, so no FE/design-fidelity review applies here.

Item 1 — URL userinfo redaction (src/core/redact.ts)

  • Added pass /(:\/\/)[^/?#\s:@]+:[^/?#\s@]+@/g$1<redacted>@. Anchored on :// and requires a user:pass@ shape, so it drops the whole userinfo (the pass half is the secret) while preserving host/port/path/query. Character classes exclude / ? # whitespace so it won't run past the authority into the path or across tokens in prose. Non-userinfo URLs are left byte-identical (regression-tested).

Item 2 — sealed-but-writable seam (tool-seam-guard.ts + openai-agents.ts inline copy)

  • Root cause: Object.seal() leaves existing data props writable:true while flipping extensible:false; the old guard AND-ed every case with Object.isExtensible, so tool.invoke = wrapper (which would succeed) was skipped → enforcement silently downgraded while the loud warning fired.
  • Fix splits the two shapes: existing writable/settable slot → writable regardless of extensibility; absent slot (add-property) → gated on Object.isExtensible. A genuinely frozen tool (non-extensible + non-writable slot) still returns false and skips-with-warning — existing frozen tests unchanged and green.
  • The inline copy in wrapFunctionToolInvoke was updated identically so the two sites stay in lockstep (they were noted as intentional duplicates).

Validation: pnpm typecheck clean, pnpm lint clean, pnpm test 630 passed / 2 skipped. Added focused tests: userinfo redaction + credential-free-URL passthrough; sealed-but-writable tool now wrapped and DENY-enforced on both the langchain and @openai/agents paths.

No behavior change for callers; both fixes are strictly more-correct on previously-unhandled inputs.

@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Codecov flagged the `descriptor === undefined` side of the inline
writability check (a tool whose own `invoke` slot is absent — inherited from
the prototype) as an uncovered partial branch. Add a focused test that calls
`wrapFunctionToolInvoke` with an inherited-invoke, non-extensible tool and
asserts it is skipped-with-warning (no own `invoke` added), exercising the
add-property side of the Object.isExtensible gate.

Refs AAASM-4951
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@Chisanan232

Copy link
Copy Markdown
Contributor Author

🤖 Claude Code review — update (CodeCov fixed)

codecov/patch was failing at 80% — one partial branch in src/hooks/openai-agents.ts (the descriptor === undefined side of the inline seam-writability gate: an inherited/absent invoke slot on a non-extensible tool, which correctly refuses to add an own property → skip-with-warning). Added a focused test (wrapFunctionToolInvoke with an inherited-invoke, preventExtensions'd tool) exercising that branch. Test-only — src logic unchanged.

Now: codecov/patch = pass; branch coverage 89.15% → 90.36%; typecheck + ESLint clean; full suite 631 tests pass. All checks green.

@Chisanan232
Chisanan232 merged commit 85f0dd5 into master Jul 21, 2026
25 checks passed
@Chisanan232
Chisanan232 deleted the v0.0.1/AAASM-4951/fix/redact_userinfo_seam_guard branch July 21, 2026 06:09
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