-
Notifications
You must be signed in to change notification settings - Fork 3k
fix(core): strip daemon secrets from hook and tool-discovery child env #7527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,6 +24,8 @@ import { ToolErrorType } from './tool-error.js'; | |||||||||||||||
| import { safeJsonStringify } from '../utils/safeJsonStringify.js'; | ||||||||||||||||
| import type { EventEmitter } from 'node:events'; | ||||||||||||||||
| import { createDebugLogger } from '../utils/debugLogger.js'; | ||||||||||||||||
| import { sanitizeChildEnv } from '../utils/sanitize-child-env.js'; | ||||||||||||||||
| import { normalizePathEnvForWindows } from '../utils/windowsPath.js'; | ||||||||||||||||
| import type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; | ||||||||||||||||
| import { normalizeMcpToolName } from '../utils/tool-name-utils.js'; | ||||||||||||||||
|
|
||||||||||||||||
|
|
@@ -61,7 +63,14 @@ class DiscoveredToolInvocation extends BaseToolInvocation< | |||||||||||||||
| _updateOutput?: (output: ToolResultDisplay) => void, | ||||||||||||||||
| ): Promise<ToolResult> { | ||||||||||||||||
| const callCommand = this.config.getToolCallCommand()!; | ||||||||||||||||
| const child = spawn(callCommand, [this.toolName]); | ||||||||||||||||
| // The user-configured tool-call command is a child process launched on the | ||||||||||||||||
| // agent's behalf, so it must not inherit Qwen-internal daemon secrets. | ||||||||||||||||
| // Passing `env` explicitly loses the native inheritance that resolved | ||||||||||||||||
| // Windows' case-insensitive PATH keys, so normalize as the shell and MCP | ||||||||||||||||
| // spawn sites do (a no-op off win32). | ||||||||||||||||
| const child = spawn(callCommand, [this.toolName], { | ||||||||||||||||
| env: normalizePathEnvForWindows(sanitizeChildEnv(process.env)), | ||||||||||||||||
| }); | ||||||||||||||||
|
Comment on lines
+71
to
+73
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Missing Both The sibling sites in
Suggested change
— qwen3.7-max via Qwen Code /review |
||||||||||||||||
| child.stdin.write(JSON.stringify(this.params)); | ||||||||||||||||
| child.stdin.end(); | ||||||||||||||||
|
|
||||||||||||||||
|
|
@@ -592,7 +601,12 @@ export class ToolRegistry { | |||||||||||||||
| 'Tool discovery command is empty or contains only whitespace.', | ||||||||||||||||
| ); | ||||||||||||||||
| } | ||||||||||||||||
| const proc = spawn(cmdParts[0] as string, cmdParts.slice(1) as string[]); | ||||||||||||||||
| // Same as the tool-call command above: the discovery command is | ||||||||||||||||
| // agent-launched, must not inherit Qwen-internal daemon secrets, and | ||||||||||||||||
| // needs the Windows PATH normalization that comes with an explicit env. | ||||||||||||||||
| const proc = spawn(cmdParts[0] as string, cmdParts.slice(1) as string[], { | ||||||||||||||||
| env: normalizePathEnvForWindows(sanitizeChildEnv(process.env)), | ||||||||||||||||
| }); | ||||||||||||||||
|
Comment on lines
+607
to
+609
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Same
Suggested change
— qwen3.7-max via Qwen Code /review |
||||||||||||||||
| let stdout = ''; | ||||||||||||||||
| const stdoutDecoder = new StringDecoder('utf8'); | ||||||||||||||||
| let stderr = ''; | ||||||||||||||||
|
|
||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion] Missing
normalizePathEnvForWindowswrapper — inconsistent with sibling spawn sitesBoth
tool-registry.tsspawn calls now correctly wrap withnormalizePathEnvForWindows(sanitizeChildEnv(process.env)), but thishookRunner.tssite applies onlysanitizeChildEnvwithout the Windows PATH normalization. Every other explicit-env spawn site in the codebase (tool-registry.ts:72,:608,mcp-client.ts:2152,shellExecutionService.ts:768,1471) uses both wrappers.The Windows PATH issue is pre-existing at this site (the old
...process.envspread into a plain object had the same effect), but since this PR touches this exact line and establishes the convention at the other sites, applying it here would be consistent.Failure scenario: on Windows,
process.envhas case-variant PATH keys (Path,PATH,path). Spreading into a plain object loses the Proxy's case-insensitive merging. Hook commands likenpx,gh,npmcould fail with "command not found" while regular shell, tool discovery, tool calls, and MCP servers all resolve correctly.— qwen3.7-max via Qwen Code /review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Holding here, and the premise is off by one —
hookRunner.tsis not the only site without the wrapper. Full audit on this branch:monitor.ts:369spreadssanitizeChildEnv(process.env)into an explicitenvobject exactly likehookRunner.tsdoes, and both got that shape from #7256. So wrapping onlyhookRunnerwould leavemonitor.tsas the outlier instead — the inconsistency doesn't get resolved, it just moves.The distinction that decides it for me is what this PR changed. The two
tool-registry.tsspawns previously passed noenvat all, so Node inherited natively and Windows resolved its case-variant PATH keys itself; makingenvexplicit is what gave that up, so restoring it belongs here.hookRunner.tsandmonitor.tsalready built explicitenvobjects before this branch and never had the native-inheritance behavior to lose — their Windows PATH exposure is identical before and after this PR.That makes it a genuine pre-existing bug at two sites rather than a regression at one, and I'd rather fix both together in a PR that says so than half of it as a side effect of a secrets fix. Happy to open that immediately if you want it.
tool-registry+hookRunnersuites 82/82; eslint clean.