( fileSuggestionInput: FileSuggestionCommandInput, signal?: AbortSignal, timeoutMs: number = 5000, // Short timeout for typeahead suggestions )
| 4772 | * @returns Array of file paths, or empty array if no command configured |
| 4773 | */ |
| 4774 | export async function executeFileSuggestionCommand( |
| 4775 | fileSuggestionInput: FileSuggestionCommandInput, |
| 4776 | signal?: AbortSignal, |
| 4777 | timeoutMs: number = 5000, // Short timeout for typeahead suggestions |
| 4778 | ): Promise<string[]> { |
| 4779 | // Check if all hooks are disabled by managed settings |
| 4780 | if (shouldDisableAllHooksIncludingManaged()) { |
| 4781 | return [] |
| 4782 | } |
| 4783 | |
| 4784 | // SECURITY: ALL hooks require workspace trust in interactive mode |
| 4785 | // This centralized check prevents RCE vulnerabilities for all current and future hooks |
| 4786 | if (shouldSkipHookDueToTrust()) { |
| 4787 | logForDebugging( |
| 4788 | `Skipping FileSuggestion command execution - workspace trust not accepted`, |
| 4789 | ) |
| 4790 | return [] |
| 4791 | } |
| 4792 | |
| 4793 | // When disableAllHooks is set in non-managed settings, only managed fileSuggestion runs |
| 4794 | // (non-managed settings cannot disable managed commands, but non-managed commands are disabled) |
| 4795 | let fileSuggestion |
| 4796 | if (shouldAllowManagedHooksOnly()) { |
| 4797 | fileSuggestion = getSettingsForSource('policySettings')?.fileSuggestion |
| 4798 | } else { |
| 4799 | fileSuggestion = getSettings_DEPRECATED()?.fileSuggestion |
| 4800 | } |
| 4801 | |
| 4802 | if (!fileSuggestion || fileSuggestion.type !== 'command') { |
| 4803 | return [] |
| 4804 | } |
| 4805 | |
| 4806 | // Use provided signal or create a default one |
| 4807 | const abortSignal = signal || AbortSignal.timeout(timeoutMs) |
| 4808 | |
| 4809 | try { |
| 4810 | const jsonInput = jsonStringify(fileSuggestionInput) |
| 4811 | |
| 4812 | const hook = { type: 'command' as const, command: fileSuggestion.command } |
| 4813 | |
| 4814 | const result = await execCommandHook( |
| 4815 | hook, |
| 4816 | 'FileSuggestion', |
| 4817 | 'FileSuggestion', |
| 4818 | jsonInput, |
| 4819 | abortSignal, |
| 4820 | randomUUID(), |
| 4821 | ) |
| 4822 | |
| 4823 | if (result.aborted || result.status !== 0) { |
| 4824 | return [] |
| 4825 | } |
| 4826 | |
| 4827 | return result.stdout |
| 4828 | .split('\n') |
| 4829 | .map(line => line.trim()) |
| 4830 | .filter(Boolean) |
| 4831 | } catch (error) { |
no test coverage detected