(args: string[])
| 1757 | } |
| 1758 | |
| 1759 | function isDockerSafe(args: string[]): boolean { |
| 1760 | if (args.length === 0) { |
| 1761 | return true |
| 1762 | } |
| 1763 | |
| 1764 | // SECURITY: blanket PowerShell `$` variable rejection. Same guard as |
| 1765 | // isGitSafe and isGhSafe. Parser differential: validator sees literal |
| 1766 | // '$env:X'; PowerShell expands at runtime. Runs BEFORE the fast-path |
| 1767 | // return — the previous location (after fast-path) never fired for |
| 1768 | // `docker ps`/`docker images`. The earlier comment claiming those take no |
| 1769 | // --format was wrong: `docker ps --format $env:AWS_SECRET_ACCESS_KEY` |
| 1770 | // auto-allowed, PowerShell expanded, docker errored with the secret in |
| 1771 | // its output, model read it. Check ALL args, not flagArgs — args[0] |
| 1772 | // (subcommand slot) could also be `$env:X`. elementTypes whitelist isn't |
| 1773 | // applicable here: this function receives string[] (post-stringify), not |
| 1774 | // ParsedCommandElement; the isAllowlistedCommand caller applies the |
| 1775 | // elementTypes gate one layer up. |
| 1776 | for (const arg of args) { |
| 1777 | if (arg.includes('$')) { |
| 1778 | return false |
| 1779 | } |
| 1780 | } |
| 1781 | |
| 1782 | const oneWordKey = `docker ${args[0]?.toLowerCase()}` |
| 1783 | |
| 1784 | // Fast path: EXTERNAL_READONLY_COMMANDS entries ('docker ps', 'docker images') |
| 1785 | // have no flag constraints — allow unconditionally (after $ guard above). |
| 1786 | if (EXTERNAL_READONLY_COMMANDS.includes(oneWordKey)) { |
| 1787 | return true |
| 1788 | } |
| 1789 | |
| 1790 | // DOCKER_READ_ONLY_COMMANDS entries ('docker logs', 'docker inspect') have |
| 1791 | // per-flag configs. Mirrors isGhSafe: look up config, then validateFlags. |
| 1792 | const config: ExternalCommandConfig | undefined = |
| 1793 | DOCKER_READ_ONLY_COMMANDS[oneWordKey] |
| 1794 | if (!config) { |
| 1795 | return false |
| 1796 | } |
| 1797 | |
| 1798 | const flagArgs = args.slice(1) |
| 1799 | |
| 1800 | if ( |
| 1801 | config.additionalCommandIsDangerousCallback && |
| 1802 | config.additionalCommandIsDangerousCallback('', flagArgs) |
| 1803 | ) { |
| 1804 | return false |
| 1805 | } |
| 1806 | return validateFlags(flagArgs, 0, config) |
| 1807 | } |
| 1808 | |
| 1809 | function isDotnetSafe(args: string[]): boolean { |
| 1810 | if (args.length === 0) { |
no test coverage detected