T09 · Insecure Skill Coding Practices
- Location
scripts/collect_release_changes.sh:4- Finding
Unvalidated Git Revision Allows Command-Line Option Injection
- Content
View full analysis
Vulnerability Details
File Location:
scripts/collect_release_changes.sh, lines 4-5, 13-17, 27, and 30
Vulnerability Type: Git command-line option injection through an unquoted and unvalidated revision range
Risk Level: MediumVulnerable Code
bash since_ref="${1:-}" until_ref="${2:-HEAD}" if [[ -z "${since_ref}" ]]; then if git describe --tags --abbrev=0 >/dev/null 2>&1; then since_ref="$(git describe --tags --abbrev=0)" fi fi range="" if [[ -n "${since_ref}" ]]; then range="${since_ref}..${until_ref}" else range="${until_ref}" fi git log --reverse --date=short --pretty=format:'%h|%ad|%s' ${range} git log --reverse --name-only --pretty=format:'--- %h %s' ${range} | sed '/^$/d'Technical Analysis
The script accepts Git revisions from positional arguments and incorporates them into
rangewithout validating that they resolve to commits. It then expands${range}without quotation and does not use an appropriate option/revision disambiguation mechanism.If
since_refremains empty,until_refbecomes the entire argument supplied to bothgit logcommands. A value beginning with--may consequently be interpreted as a Git command-line option rather than as a revision. For example, Git's--output=<path>option can redirect command output to a local file selected by the caller.The unquoted expansion additionally subjects the value to shell word splitting and pathname expansion. Shell metacharacters embedded in the variable are not reparsed as shell operators, so this issue does not by itself provide arbitrary shell-command execution. The confirmed primitive is injection of supported
git logoptions.Attack Path
- An attacker influences a requested revision used to generate release notes.
- The caller invokes the script with an empty first argument and an attacker-controlled second argument.
- The script assigns the second ...[truncated 1298 chars]
- Remediation
View remediation
Remediation Suggestions
Validate every user-provided revision with
git rev-parse --verifyand convert it to a commit object ID before constructing a range. Reject values that do not resolve to commits. Keep all variable expansions quoted so the shell cannot perform word splitting or pathname expansion.A hardened implementation can use the following pattern:
bash if [[ -n "${since_ref}" ]]; then since_oid="$(git rev-parse --verify "${since_ref}^{commit}")" || { printf 'Invalid starting revision: %s\n' "${since_ref}" >&2 exit 1 } fi until_oid="$(git rev-parse --verify "${until_ref}^{commit}")" || { printf 'Invalid ending revision: %s\n' "${until_ref}" >&2 exit 1 } if [[ -n "${since_ref}" ]]; then range="${since_oid}..${until_oid}" else range="${until_oid}" fi git log --reverse --date=short \ --pretty=format:'%h|%ad|%s' "${range}" git log --reverse --name-only \ --pretty=format:'--- %h %s' "${range}" | sed '/^$/d'Additional hardening measures include:
- Rejecting arguments that begin with
-before invoking Git. - Using resolved hexadecimal object IDs rather than raw caller input.
- Applying the same validation and quoting to both
git logcalls. - Testing malformed refs, whitespace-containing values, wildcard characters, and option-like values.
- Running the skill with least-privilege filesystem permissions to limit the impact of unintended file writes.
- Rejecting arguments that begin with
