mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 04:30:48 +00:00
feat(hooks): Add HTTP Hook, Function Hook and Async Hook support (#2827)
* add http/async/function type * fix url error * resolve comment * align cc non blocking error * fix hookRunner for async * fix(hooks): update hook type validation to support http and function types - Change validated hook types from ['command', 'plugin'] to ['command', 'http', 'function'] - Add validation for HTTP hooks requiring url field - Add validation for function hooks requiring callback field - Add comprehensive test coverage for all hook type validations Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(hooks): align SSRF protection with Claude Code behavior - Allow 127.0.0.0/8 (loopback) for local dev hooks - Allow localhost hostname for local dev hooks - Allow ::1 (IPv6 loopback) for local dev hooks - Add 100.64.0.0/10 (CGNAT) to blocked ranges (RFC 6598) - Update tests to match Claude Code's ssrfGuard.ts behavior This fixes HTTP hooks failing to connect to local dev servers. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(hooks): align HTTP hook security with Claude Code behavior - Add CRLF/NUL sanitization for env var interpolation (header injection) - Implement combined abort signal (external signal + timeout) - Upgrade SSRF protection to DNS-level with ssrfGuard - Allow loopback (127.0.0.0/8, ::1) for local dev hooks - Block CGNAT (100.64.0.0/10) and IPv6 private ranges - Increase default HTTP hook timeout to 10 minutes - Fix VS Code hooks schema to support http type - Add url, headers, allowedEnvVars, async, once, statusMessage, shell fields - Note: "function" type is SDK-only (callback cannot be serialized to JSON) * feat(hooks): enhance Function Hook with messages, skillRoot, shell, and matcher support - Add MessagesProvider for automatic conversation history passing to function hooks - Add FunctionHookContext with messages, toolUseID, and signal - Add skillRoot support for skill-scoped session hooks - Add shell parameter support for command hooks (bash/powershell) - Add regex matcher support for hook pattern matching - Add statusMessage to CommandHookConfig - Change default function hook timeout from 60s to 5s - Add comprehensive unit tests for all new features Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * add session hook for skill * fix function hook parsing * refactor ui for http hook/async hook/function hook * update doc and add integration test * change telemetryn type and refactor SSRF * fix project level bug --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
70396d1276
commit
b5115e731e
63 changed files with 9301 additions and 469 deletions
|
|
@ -701,6 +701,14 @@ export async function loadCliConfig(
|
|||
argv: CliArgs,
|
||||
cwd: string = process.cwd(),
|
||||
overrideExtensions?: string[],
|
||||
/**
|
||||
* Optional separated hooks for proper source attribution.
|
||||
* If provided, these override settings.hooks for hook loading.
|
||||
*/
|
||||
hooksConfig?: {
|
||||
userHooks?: Record<string, unknown>;
|
||||
projectHooks?: Record<string, unknown>;
|
||||
},
|
||||
): Promise<Config> {
|
||||
const debugMode = isDebugMode(argv);
|
||||
|
||||
|
|
@ -1099,6 +1107,7 @@ export async function loadCliConfig(
|
|||
generationConfigSources: resolvedCliConfig.sources,
|
||||
generationConfig: resolvedCliConfig.generationConfig,
|
||||
warnings: resolvedCliConfig.warnings,
|
||||
allowedHttpHookUrls: settings.security?.allowedHttpHookUrls ?? [],
|
||||
cliVersion: await getCliVersion(),
|
||||
webSearch: buildWebSearchConfig(argv, settings, selectedAuthType),
|
||||
ideMode,
|
||||
|
|
@ -1119,7 +1128,10 @@ export async function loadCliConfig(
|
|||
output: {
|
||||
format: outputSettingsFormat,
|
||||
},
|
||||
hooks: settings.hooks,
|
||||
// Use separated hooks if provided, otherwise fall back to merged hooks
|
||||
userHooks: hooksConfig?.userHooks ?? settings.hooks,
|
||||
projectHooks: hooksConfig?.projectHooks,
|
||||
hooks: settings.hooks, // Keep for backward compatibility
|
||||
disableAllHooks: settings.disableAllHooks ?? false,
|
||||
channel: argv.channel,
|
||||
// Precedence: explicit CLI flag > settings file > default(true).
|
||||
|
|
|
|||
|
|
@ -437,6 +437,26 @@ export class LoadedSettings {
|
|||
this._merged = this.computeMergedSettings();
|
||||
saveSettings(settingsFile, createSettingsUpdate(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user-level hooks from user settings (not merged with workspace).
|
||||
* These hooks should always be loaded regardless of folder trust.
|
||||
*/
|
||||
getUserHooks(): Record<string, unknown> | undefined {
|
||||
return this.user.settings.hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project-level hooks from workspace settings (not merged).
|
||||
* Returns undefined if workspace is not trusted (hooks filtered out).
|
||||
*/
|
||||
getProjectHooks(): Record<string, unknown> | undefined {
|
||||
// Only return project hooks if workspace is trusted
|
||||
if (!this.isTrusted) {
|
||||
return undefined;
|
||||
}
|
||||
return this.workspace.settings.hooks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -131,18 +131,36 @@ const HOOK_DEFINITION_ITEMS: SettingItemDefinition = {
|
|||
items: {
|
||||
type: 'object',
|
||||
description:
|
||||
'A hook configuration entry that defines a command to execute.',
|
||||
'A hook configuration entry that defines a hook to execute.',
|
||||
properties: {
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'The type of hook.',
|
||||
enum: ['command'],
|
||||
description:
|
||||
'The type of hook. Note: "function" type is only available via SDK registration, not settings.json.',
|
||||
enum: ['command', 'http'],
|
||||
required: true,
|
||||
},
|
||||
command: {
|
||||
type: 'string',
|
||||
description: 'The command to execute when the hook is triggered.',
|
||||
required: true,
|
||||
description:
|
||||
'The command to execute when the hook is triggered. Required for "command" type.',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The URL to send the POST request to. Required for "http" type.',
|
||||
},
|
||||
headers: {
|
||||
type: 'object',
|
||||
description:
|
||||
'HTTP headers to include in the request. Supports env var interpolation ($VAR, ${VAR}).',
|
||||
additionalProperties: { type: 'string' },
|
||||
},
|
||||
allowedEnvVars: {
|
||||
type: 'array',
|
||||
description:
|
||||
'List of environment variables allowed for interpolation in headers and URL.',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
|
|
@ -154,7 +172,7 @@ const HOOK_DEFINITION_ITEMS: SettingItemDefinition = {
|
|||
},
|
||||
timeout: {
|
||||
type: 'number',
|
||||
description: 'Timeout in milliseconds for the hook execution.',
|
||||
description: 'Timeout in seconds for the hook execution.',
|
||||
},
|
||||
env: {
|
||||
type: 'object',
|
||||
|
|
@ -162,6 +180,25 @@ const HOOK_DEFINITION_ITEMS: SettingItemDefinition = {
|
|||
'Environment variables to set when executing the hook command.',
|
||||
additionalProperties: { type: 'string' },
|
||||
},
|
||||
async: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether to execute the hook asynchronously (non-blocking, for "command" type only).',
|
||||
},
|
||||
once: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether to execute the hook only once per session (for "http" type).',
|
||||
},
|
||||
statusMessage: {
|
||||
type: 'string',
|
||||
description: 'A message to display while the hook is executing.',
|
||||
},
|
||||
shell: {
|
||||
type: 'string',
|
||||
description: 'The shell to use for command execution.',
|
||||
enum: ['bash', 'powershell'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -1338,6 +1375,20 @@ const SETTINGS_SCHEMA = {
|
|||
},
|
||||
},
|
||||
},
|
||||
allowedHttpHookUrls: {
|
||||
type: 'array',
|
||||
label: 'Allowed HTTP Hook URLs',
|
||||
category: 'Security',
|
||||
requiresRestart: false,
|
||||
default: [] as string[],
|
||||
description:
|
||||
'Whitelist of URL patterns for HTTP hooks. Supports * wildcard. If empty, all URLs are allowed (subject to SSRF protection).',
|
||||
showInDialog: false,
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'URL pattern (supports * wildcard)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue