- Location
- scripts/github_indexer.py:28
- Finding
- GitHub CLI Argument Injection in Repository Indexing<![CDATA[
## Vulnerability Details
**File Location**: `scripts/github_indexer.py:28-32`; user-controlled values reach the helper at `scripts/github_indexer.py:153`, `179`, and `211`
**Vulnerability Type**: Improper subprocess argument construction
**Risk Level**: Medium
### Vulnerable Code
```python
def gh(args: str) -> dict:
result = subprocess.run(
f'"{GH_EXE}" {args}',
capture_output=True, encoding="utf-8", errors="replace"
)
```
Representative user-controlled call:
```python
def fetch_issues(repo: str, state: str = "all", limit: int = 100) -> List[GitHubItem]:
data = gh_list(f"issue list --repo {repo} --state {state} --limit {limit} --json number,title,body,state,author,labels,url,createdAt,updatedAt")
```
Related call sites construct commands in the same manner:
```python
data = gh_list(f"pr list --repo {repo} --state {state} --limit {limit} --json number,title,body,state,author,labels,url,createdAt,updatedAt,isDraft")
```
```python
data = gh(f"repo view {repo} --json name,description,stargazerCount,url,languages,repositoryTopics")
```
### Technical Analysis
The repository name and other arguments are interpolated into a single command-line string. On the intended Windows environment, process argument parsing can interpret spaces and quotes in an attacker-controlled repository value as argument boundaries. The input is not validated as an `owner/repository` identifier.
Because `shell=False` is used, this is not a confirmed shell-metacharacter or arbitrary operating-system command injection vulnerability. It is an argument-injection weakness: crafted input may introduce additional GitHub CLI flags or otherwise alter the intended request.
The command executes under the operator's existing authenticated GitHub CLI identity.
### Attack Path
1. An attacker able to invoke the script supplies a crafted value for the positional `repo` argument.
2. The value passes through `argparse` without repository-format validation.
3. `
...[truncated 705 chars]
- Remediation
- <![CDATA[
## Remediation Suggestions
- Change subprocess helpers to accept `list[str]` rather than preformatted strings.
- Pass every argument as a separate list element:
```python
result = subprocess.run(
[GH_EXE, "issue", "list", "--repo", repo, "--state", state,
"--limit", str(limit), "--json",
"number,title,body,state,author,labels,url,createdAt,updatedAt"],
capture_output=True,
encoding="utf-8",
errors="replace",
check=False,
)
```
- Validate repository identifiers before invocation, for example with a strict pattern such as:
```python
r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$"
```
- Reject control characters, whitespace, quotes, and values beginning with an option prefix.
- Apply reasonable upper and lower bounds to numeric arguments such as `limit`.
]]>