fix(langchain): enforce the tool allowlist in handleToolStart - #6
Merged
Conversation
WHY: `aegisCallbackHandler({ allowedTools: [...] })` accepted an
allowlist but never enforced it. handleToolStart only ran an
OUTPUT-scope content scan, so the LLM08 tool-call-OOB allowlist guard
(and the tool-only SSRF patterns) — which run only under TOOL scope —
never executed. A user who set allowedTools reasonably expected tool
gating, but every out-of-allowlist tool call passed through silently.
WHAT: When an allowlist is configured (via options, policy, or the
ALLOWED_TOOLS env var), handleToolStart now runs a TOOL-scope scan of
the tool identity, throwing AegisBlockedError("tool", ...) on a
violation. The identity is resolved from LangChain's serialized tool
descriptor (`.name`, or the last segment of `.id`), falling back to the
input string when the descriptor carries no name. The allowlist is only
enforced when one is configured — an empty list fails closed and would
otherwise block every tool, so unguarded chains are unaffected. The
existing OUTPUT-scope scan of tool arguments (dangerous commands /
secrets) is preserved. Adds regression tests covering descriptor-name,
id-path, input-fallback, policy-supplied allowlists, and the
no-allowlist no-regression path.
Co-authored-by: mattia-mamini-gh <281593356+mattia-mamini-gh@users.noreply.github.com>
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.
Why
aegisCallbackHandler({ allowedTools: [...] })accepted an allowlist but never enforced it.handleToolStartonly ran an OUTPUT-scope content scan of the tool input, while the LLM08 tool-call-OOB allowlist guard and the tool-only SSRF patterns run only under TOOL scope. A user who setallowedToolson the LangChain handler reasonably expected tool gating, but every out-of-allowlist tool call passed through silently.Reproduction on
main(before):aegisCallbackHandler({ allowedTools: ['search'] }).handleToolStart({}, 'delete_db')resolved without blocking — allowlist ignored.What
When an allowlist is configured (via
options.allowedTools,policy.allowedTools, or theALLOWED_TOOLSenv var),handleToolStartnow runs a TOOL-scope scan of the tool identity and throwsAegisBlockedError("tool", ...)on a violation..name, or the last segment of.id), falling back to theinputstring when the descriptor has no name.@langchain/*-dependency structural shim.Verification
npm run typecheck— cleannpm run lint— cleannpm test— 319 passed / 1 skipped (added 10 regression tests intests/langchain.test.ts)handleToolStart({}, "delete_db")→ BLOCKED (tool) TOOL_CALL_OOBhandleToolStart({}, "search")→ passeshandleToolStart({ name: "delete_db" }, args)→ BLOCKEDhandleToolStart({ name: "search" }, args)→ passes🤖 Generated with Claude Code