T09 · Insecure Skill Coding Practices
- Location
scripts/generate_pr_description.py:30- Finding
Git Option Injection Through the Unvalidated Base Revision
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate_pr_description.py, lines 30-58 and 340-365
Vulnerability Type: Git argument and option injection
Risk Level: MediumVulnerable Code
python def get_diff_stats(base='main', cwd=None): """Get file-level diff statistics.""" output = run_git(['diff', '--stat', '--numstat', f'{base}...HEAD'], cwd=cwd) if not output: output = run_git(['diff', '--stat', '--numstat', base], cwd=cwd) return output def get_diff(base='main', cwd=None): """Get full diff.""" output = run_git(['diff', f'{base}...HEAD'], cwd=cwd) if not output: output = run_git(['diff', base], cwd=cwd) return output def get_commits(base='main', cwd=None): """Get commit messages since base branch.""" output = run_git(['log', '--oneline', '--no-merges', f'{base}...HEAD'], cwd=cwd) if not output: output = run_git(['log', '--oneline', '--no-merges', f'{base}..HEAD'], cwd=cwd) return output def get_changed_files(base='main', cwd=None): """Get list of changed files with status.""" output = run_git(['diff', '--name-status', f'{base}...HEAD'], cwd=cwd) if not output: output = run_git(['diff', '--name-status', base], cwd=cwd)The value originates from the command-line argument:
python parser.add_argument('--base', '-b', default='main', help='Base branch to compare against (default: main)')Technical Analysis
The user-controlled
basevalue is inserted directly into several Git command argument lists without first confirming that it is a valid commit or branch name. No check rejects values beginning with-, and no safe revision-resolution step is performed.Using an argument list with
subprocess.runprevents shell metacharacter injection, but it does not prevent option injection at the Git command layer. Git may interpret a craft ...[truncated 1756 chars]- Remediation
View remediation
Remediation Suggestions
- Reject base values that are empty, contain control characters, or begin with
-. - Resolve the supplied value to a commit before using it:
python def resolve_revision(base, cwd=None): if not base or base.startswith('-'): raise ValueError('Invalid base revision') result = subprocess.run( ['git', 'rev-parse', '--verify', f'{base}^{{commit}}'], cwd=cwd, capture_output=True, text=True, timeout=30, check=False, ) if result.returncode != 0: raise ValueError('Base revision does not resolve to a commit') return result.stdout.strip() - Construct comparison ranges only from the resolved commit hash, which cannot be interpreted as a command option.
- Where Git syntax permits it, use explicit option terminators and unambiguous revision arguments.
- Modify
run_git()to return and inspect the process return code rather than treating empty standard output as the only indication of failure. - Add regression tests using values such as
--output=...,--no-index, invalid revisions, and revision names containing unusual characters.
- Reject base values that are empty, contain control characters, or begin with
