( statusLineInput: StatusLineCommandInput, signal?: AbortSignal, timeoutMs: number = 5000, // Short timeout for status line logResult: boolean = false, )
| 4582 | * @returns The status line text to display, or undefined if no command configured |
| 4583 | */ |
| 4584 | export async function executeStatusLineCommand( |
| 4585 | statusLineInput: StatusLineCommandInput, |
| 4586 | signal?: AbortSignal, |
| 4587 | timeoutMs: number = 5000, // Short timeout for status line |
| 4588 | logResult: boolean = false, |
| 4589 | ): Promise<string | undefined> { |
| 4590 | // Check if all hooks (including statusLine) are disabled by managed settings |
| 4591 | if (shouldDisableAllHooksIncludingManaged()) { |
| 4592 | return undefined |
| 4593 | } |
| 4594 | |
| 4595 | // SECURITY: ALL hooks require workspace trust in interactive mode |
| 4596 | // This centralized check prevents RCE vulnerabilities for all current and future hooks |
| 4597 | if (shouldSkipHookDueToTrust()) { |
| 4598 | logForDebugging( |
| 4599 | `Skipping StatusLine command execution - workspace trust not accepted`, |
| 4600 | ) |
| 4601 | return undefined |
| 4602 | } |
| 4603 | |
| 4604 | // When disableAllHooks is set in non-managed settings, only managed statusLine runs |
| 4605 | // (non-managed settings cannot disable managed commands, but non-managed commands are disabled) |
| 4606 | let statusLine |
| 4607 | if (shouldAllowManagedHooksOnly()) { |
| 4608 | statusLine = getSettingsForSource('policySettings')?.statusLine |
| 4609 | } else { |
| 4610 | statusLine = getSettings_DEPRECATED()?.statusLine |
| 4611 | } |
| 4612 | |
| 4613 | if (!statusLine || statusLine.type !== 'command') { |
| 4614 | return undefined |
| 4615 | } |
| 4616 | |
| 4617 | // Use provided signal or create a default one |
| 4618 | const abortSignal = signal || AbortSignal.timeout(timeoutMs) |
| 4619 | |
| 4620 | try { |
| 4621 | // Convert status input to JSON |
| 4622 | const jsonInput = jsonStringify(statusLineInput) |
| 4623 | |
| 4624 | const result = await execCommandHook( |
| 4625 | statusLine, |
| 4626 | 'StatusLine', |
| 4627 | 'statusLine', |
| 4628 | jsonInput, |
| 4629 | abortSignal, |
| 4630 | randomUUID(), |
| 4631 | ) |
| 4632 | |
| 4633 | if (result.aborted) { |
| 4634 | return undefined |
| 4635 | } |
| 4636 | |
| 4637 | // For successful hooks (exit code 0), use stdout |
| 4638 | if (result.status === 0) { |
| 4639 | // Trim and split output into lines, then join with newlines |
| 4640 | const output = result.stdout |
| 4641 | .trim() |
no test coverage detected