( fileSuggestionInput: FileSuggestionCommandInput, signal?: AbortSignal, timeoutMs: number = 5000, // Short timeout for typeahead suggestions )
| 4677 | * @returns Array of file paths, or empty array if no command configured |
| 4678 | */ |
| 4679 | export async function executeFileSuggestionCommand( |
| 4680 | fileSuggestionInput: FileSuggestionCommandInput, |
| 4681 | signal?: AbortSignal, |
| 4682 | timeoutMs: number = 5000, // Short timeout for typeahead suggestions |
| 4683 | ): Promise<string[]> { |
| 4684 | // Check if all hooks are disabled by managed settings |
| 4685 | if (shouldDisableAllHooksIncludingManaged()) { |
| 4686 | return [] |
| 4687 | } |
| 4688 | |
| 4689 | // SECURITY: ALL hooks require workspace trust in interactive mode |
| 4690 | // This centralized check prevents RCE vulnerabilities for all current and future hooks |
| 4691 | if (shouldSkipHookDueToTrust()) { |
| 4692 | logForDebugging( |
| 4693 | `Skipping FileSuggestion command execution - workspace trust not accepted`, |
| 4694 | ) |
| 4695 | return [] |
| 4696 | } |
| 4697 | |
| 4698 | // When disableAllHooks is set in non-managed settings, only managed fileSuggestion runs |
| 4699 | // (non-managed settings cannot disable managed commands, but non-managed commands are disabled) |
| 4700 | let fileSuggestion |
| 4701 | if (shouldAllowManagedHooksOnly()) { |
| 4702 | fileSuggestion = getSettingsForSource('policySettings')?.fileSuggestion |
| 4703 | } else { |
| 4704 | fileSuggestion = getSettings_DEPRECATED()?.fileSuggestion |
| 4705 | } |
| 4706 | |
| 4707 | if (!fileSuggestion || fileSuggestion.type !== 'command') { |
| 4708 | return [] |
| 4709 | } |
| 4710 | |
| 4711 | // Use provided signal or create a default one |
| 4712 | const abortSignal = signal || AbortSignal.timeout(timeoutMs) |
| 4713 | |
| 4714 | try { |
| 4715 | const jsonInput = jsonStringify(fileSuggestionInput) |
| 4716 | |
| 4717 | const hook = { type: 'command' as const, command: fileSuggestion.command } |
| 4718 | |
| 4719 | const result = await execCommandHook( |
| 4720 | hook, |
| 4721 | 'FileSuggestion', |
| 4722 | 'FileSuggestion', |
| 4723 | jsonInput, |
| 4724 | abortSignal, |
| 4725 | randomUUID(), |
| 4726 | ) |
| 4727 | |
| 4728 | if (result.aborted || result.status !== 0) { |
| 4729 | return [] |
| 4730 | } |
| 4731 | |
| 4732 | return result.stdout |
| 4733 | .split('\n') |
| 4734 | .map(line => line.trim()) |
| 4735 | .filter(Boolean) |
| 4736 | } catch (error) { |
no test coverage detected