Conversation
🎉 Package Size Decrease
|
📝 WalkthroughWalkthroughThe release script now passes Merge Risk: 🟠 High · up to The release workflow currently exposes pull-request-controlled commit data to shell parsing and can fail or handle tags incorrectly when a release is retried or races with another job. These issues could compromise release execution or produce incorrect release state, so they should be fixed before merging. 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 081a3104-9e4c-4dfa-86d4-022e57837066
📒 Files selected for processing (2)
.github/workflows/publish.ymlpackage.json
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.
| gh api "repos/$GITHUB_REPOSITORY/git/refs" \ | ||
| -f ref="refs/tags/v$VERSION" \ | ||
| -f sha="${{ github.event.pull_request.merge_commit_sha }}" |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
sed -n '1,90p' .github/workflows/publish.yml
printf '%s\n' '--- related references ---'
rg -n 'git/refs|merge_commit_sha|VERSION|publish' .github README.md 2>/dev/null || trueRepository: npmx-dev/vscode-npmx
Length of output: 2705
🌐 Web query:
GitHub REST API create a reference existing ref conflict get a reference tag object SHA annotated tag documentation
💡 Result:
To create a reference (like a branch or tag) using the GitHub REST API, you use the POST /repos/{owner}/{repo}/git/refs endpoint [1][2]. If you attempt to create a reference that already exists, the API will return a 409 Conflict status code [1][3]. Regarding annotated tags and their object SHAs: 1. An annotated tag in Git is an object that contains its own unique SHA (the tag object SHA), which is distinct from the SHA of the commit it points to [4]. 2. When you retrieve a reference for an annotated tag (e.g., using GET /repos/{owner}/{repo}/git/refs/tags/{tag_name}), the response contains an object block. This block includes the tag's type (which will be "tag") and its object SHA [4]. 3. To get the full details of the annotated tag object (such as the tagger name, date, and the underlying commit SHA), you use the GET /repos/{owner}/{repo}/git/tags/{tag_sha} endpoint, passing the tag object SHA obtained from the reference [5][4]. Creating an annotated tag via the API is a two-step process: first, create the annotated tag object (POST /repos/{owner}/{repo}/git/tags), then create the reference pointing to that object (POST /repos/{owner}/{repo}/git/refs) [5][6]. Creating only the reference without the tag object results in a lightweight tag [5][6].
Citations:
- 1: https://docs.github.com/en/rest/git/refs
- 2: https://docs.github.com/en/rest/git/refs?apiVersion=2022-11-28
- 3: https://docs.github.com/enterprise-server%403.18/rest/git/refs
- 4: https://stackoverflow.com/questions/72429056/how-to-get-single-tag-information-using-github-api
- 5: https://docs.github.com/en/rest/git/tags
- 6: https://docs.github.com/rest/git/tags
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
import json
import re
import urllib.request
from pathlib import Path
workflow = Path(".github/workflows/publish.yml").read_text()
create = re.search(
r'gh api "repos/\$GITHUB_REPOSITORY/git/refs"\s*\\\n'
r'\s*-f ref="refs/tags/v\$VERSION"\s*\\\n'
r'\s*-f sha="\$\{\{ github\.event\.pull_request\.merge_commit_sha \}\}"',
workflow,
)
steps = re.findall(r'^\s+- name: (.+)$', workflow, re.MULTILINE)
print("create_request_shape:", bool(create))
print("step_order:", steps)
print("create_before_build:", steps.index("Create tag") < steps.index("Build"))
print("create_before_publish:", steps.index("Create tag") < steps.index("Publish npm packages to registry"))
repo = "npmx-dev/vscode-npmx"
req = urllib.request.Request(
f"https://api.github.com/repos/{repo}/git/refs/tags",
headers={"Accept": "application/vnd.github+json", "User-Agent": "read-only-review-probe"},
)
try:
with urllib.request.urlopen(req, timeout=10) as response:
refs = json.load(response)
print("tag_listing_status: 200")
print("tag_count:", len(refs))
print("sample_refs:", [item.get("ref") for item in refs[:5]])
except urllib.error.HTTPError as exc:
print("tag_listing_status:", exc.code)
print("tag_listing_error:", exc.read().decode("utf-8", "replace")[:300])
except Exception as exc:
print("tag_listing_error:", type(exc).__name__, str(exc))
PYRepository: npmx-dev/vscode-npmx
Length of output: 566
Make tag creation safe to retry.
If a later step fails, a rerun receives 409 Conflict because the tag already exists. Read refs/tags/v$VERSION first. Continue only when it points to the merge commit. Fail when it points elsewhere. If creation races with another job, re-read the tag after 409 and apply the same comparison. Dereference annotated tags before comparing their target commit.
🧰 Tools
🪛 zizmor (1.29.0)
[error] 33-33: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
| VERSION=$(jq -r '.version' package.json) | ||
| gh api "repos/$GITHUB_REPOSITORY/git/refs" \ | ||
| -f ref="refs/tags/v$VERSION" \ | ||
| -f sha="${{ github.event.pull_request.merge_commit_sha }}" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/*' | sort
printf '%s\n' '--- publish workflow size ---'
wc -l .github/workflows/publish.yml
printf '%s\n' '--- publish workflow relevant sections ---'
cat -n .github/workflows/publish.yml | sed -n '1,75p'
printf '%s\n' '--- zizmor references and merge_commit_sha uses ---'
rg -n -C 3 'zizmor|merge_commit_sha|template-injection|pull_request_target|pull_request' \
.github pyproject.toml package.json Makefile 2>/dev/null || trueRepository: npmx-dev/vscode-npmx
Length of output: 5310
🌐 Web query:
GitHub pull_request webhook payload merge_commit_sha type documentation zizmor template-injection environment variable remediation
💡 Result:
Regarding GitHub pull_request webhooks, the merge_commit_sha field is technically present in live payloads; however, it has been observed that some recent API schema definitions (e.g., the 2026-03-10 spec) inaccurately omit this field [1]. This discrepancy between the live runtime contract and the formal schema specification can cause issues for tools or integrations relying solely on the generated API schemas [1]. Zizmor is a static analysis tool designed to secure GitHub Actions workflows [2][3]. One of its key capabilities is detecting template injection vulnerabilities, which occur when user-controlled inputs (like issue titles or PR data) are improperly expanded within ${{... }} delimiters in workflow files [4]. Because these expansions happen before execution, they can lead to unintended code injection if placed inside sensitive contexts like run: blocks [4]. To remediate these template injection vulnerabilities, zizmor recommends replacing the direct template expression with an environment variable [4]. The remediation process involves two steps: 1. Defining the environment variable within the step's env: block, assigning it the value of the template expression (e.g., FOO: ${{ github.event.issue.title }}) [5]. 2. Using the shell-specific syntax to reference that variable in the script block (e.g., echo ${FOO} for bash/sh) [4][5]. This approach ensures that the data is handled via the shell's variable expansion mechanisms rather than the GitHub Actions workflow runner's template expansion, effectively preventing the injection [4]. Users should ensure their remediation uses the correct shell syntax (e.g., ${VAR} for sh/bash, %VAR% for cmd, or $env:VAR for PowerShell) [5]. Note that some zizmor versions have refined this logic to preserve case sensitivity for specific contexts, such as existing env.* variables [6].
Citations:
- 1: [Schema Inaccuracy] 2026-03-10 pull_request webhook payloads are missing merge_commit_sha github/rest-api-description#6344
- 2: https://zizmor.sh/
- 3: https://github.com/zizmorcore/zizmor/
- 4: https://docs.zizmor.sh/audits/
- 5: https://github.com/zizmorcore/zizmor/blob/563b7b25/crates/zizmor/src/audit/template_injection.rs
- 6: fix: preserve env var case in template injection fixes zizmorcore/zizmor#1766
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
import subprocess
script = r'''
set -u
MERGE_COMMIT_SHA_PLACEHOLDER="$1"
# Model the workflow's current shell source after GitHub expression interpolation.
eval 'printf "current:%s\n" "' + MERGE_COMMIT_SHA_PLACEHOLDER + '"'
'''
values = {
"valid_sha": "0123456789abcdef0123456789abcdef01234567",
"quoted_sha": '0123456789abcdef0123456789abcdef01234567"; printf "injected"',
"command_substitution": '$(printf injected)',
}
for name, value in values.items():
print(f"--- {name} ---")
result = subprocess.run(
["bash", "-c", script, "probe", value],
text=True,
capture_output=True,
)
print("exit:", result.returncode)
print("stdout:", result.stdout.rstrip())
print("stderr:", result.stderr.rstrip())
print("--- environment-variable form ---")
for name, value in values.items():
result = subprocess.run(
["bash", "-c", 'MERGE_COMMIT_SHA="$1"; printf "env:%s\\n" "$MERGE_COMMIT_SHA"', "probe", value],
text=True,
capture_output=True,
)
print(name, "exit:", result.returncode, "stdout:", result.stdout.rstrip())
PYRepository: npmx-dev/vscode-npmx
Length of output: 682
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
import subprocess
values = {
"valid_sha": "0123456789abcdef0123456789abcdef01234567",
"quoted_sha": '0123456789abcdef0123456789abcdef01234567"; printf "injected"',
"command_substitution": '$(printf injected)',
}
for name, value in values.items():
print(f"--- {name}: direct interpolation ---")
source = f'printf "direct:%s\\n" "{value}"'
result = subprocess.run(
["bash", "-c", source],
text=True,
capture_output=True,
)
print("exit:", result.returncode)
print("stdout:", result.stdout.rstrip())
print("stderr:", result.stderr.rstrip())
print(f"--- {name}: environment variable ---")
result = subprocess.run(
["bash", "-c", 'printf "env:%s\\n" "$MERGE_COMMIT_SHA"'],
env={"MERGE_COMMIT_SHA": value},
text=True,
capture_output=True,
)
print("exit:", result.returncode)
print("stdout:", result.stdout.rstrip())
print("stderr:", result.stderr.rstrip())
PYRepository: npmx-dev/vscode-npmx
Length of output: 833
Keep event data out of shell source.
Pass github.event.pull_request.merge_commit_sha through env and use $MERGE_COMMIT_SHA in the gh api command. This prevents shell injection and addresses the zizmor template-injection finding.
🧰 Tools
🪛 zizmor (1.29.0)
[error] 33-33: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
Source: Linters/SAST tools
No description provided.