(_rawCommand: string, args: string[])
| 942 | // This covers BOTH --repo values AND positional URL/repo arguments, INCLUDING |
| 943 | // the equals-attached form `--repo=HOST/OWNER/REPO` (cobra accepts both forms). |
| 944 | function ghIsDangerousCallback(_rawCommand: string, args: string[]): boolean { |
| 945 | for (const token of args) { |
| 946 | if (!token) continue |
| 947 | // For flag tokens, extract the VALUE after `=` for inspection. Without this, |
| 948 | // `--repo=evil.com/SECRET/x` (single token starting with `-`) gets skipped |
| 949 | // entirely, bypassing the HOST check. Cobra treats `--flag=val` identically |
| 950 | // to `--flag val`; we must inspect both forms. |
| 951 | let value = token |
| 952 | if (token.startsWith('-')) { |
| 953 | const eqIdx = token.indexOf('=') |
| 954 | if (eqIdx === -1) continue // flag without inline value, nothing to inspect |
| 955 | value = token.slice(eqIdx + 1) |
| 956 | if (!value) continue |
| 957 | } |
| 958 | // Skip values that are clearly not repo specs (no `/` at all, or pure numbers) |
| 959 | if ( |
| 960 | !value.includes('/') && |
| 961 | !value.includes('://') && |
| 962 | !value.includes('@') |
| 963 | ) { |
| 964 | continue |
| 965 | } |
| 966 | // URL schemes: https://, http://, git://, ssh:// |
| 967 | if (value.includes('://')) { |
| 968 | return true |
| 969 | } |
| 970 | // SSH-style: git@host:owner/repo |
| 971 | if (value.includes('@')) { |
| 972 | return true |
| 973 | } |
| 974 | // 3+ segments = HOST/OWNER/REPO (normal gh format is OWNER/REPO, 1 slash) |
| 975 | // Count slashes: 2+ slashes means 3+ segments |
| 976 | const slashCount = (value.match(/\//g) || []).length |
| 977 | if (slashCount >= 2) { |
| 978 | return true |
| 979 | } |
| 980 | } |
| 981 | return false |
| 982 | } |
| 983 | |
| 984 | export const GH_READ_ONLY_COMMANDS: Record<string, ExternalCommandConfig> = { |
| 985 | // gh pr view is read-only — displays pull request details |
nothing calls this directly
no outgoing calls
no test coverage detected