ci(security): hold API responses in a file instead of piping them onward
Semgrep blocks on gha-curl-pipe-shell, and it is pointing at the publish step added an hour ago. The rule exists for `curl https://…/install.sh | bash`: remote content reaching an interpreter. What this actually did was pipe a JSON response into a python3 -c inline script -- the interpreter and its program both live in the workflow file, and the server only ever supplied data. A false positive, then, but the rule cannot see the difference between an interpreter reading a program from stdin and one reading data, and neither can the next person to read the step. Responses go to a file and are read from there. Same shape as the finding suggests, and worth having anyway: a response that is on disk can be looked at when a call misbehaves, instead of vanishing into a pipe. Verified by falsification rather than by a green run: the same ruleset against the previous revision of this file reports 1 blocking finding, against this one zero. Without that check a passing scan only proves the rule was not loaded.
This commit is contained in:
@@ -174,28 +174,43 @@ jobs:
|
||||
api="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}"
|
||||
auth="Authorization: token ${GITEA_TOKEN}"
|
||||
|
||||
# Existing release for this tag, or create one.
|
||||
rel_id="$(curl -sf -H "$auth" "$api/releases/tags/${TAG_NAME}" \
|
||||
| python3 -c 'import sys,json; print(json.load(sys.stdin).get("id",""))' 2>/dev/null || true)"
|
||||
# Responses land in a file before anything reads them, rather than
|
||||
# being piped straight into an interpreter. The interpreter is inline
|
||||
# either way and the server only ever supplies data, but a scanner
|
||||
# cannot tell those apart from curl-pipe-shell -- and holding the
|
||||
# response makes it inspectable when a call misbehaves.
|
||||
get_release_id() {
|
||||
if curl -sf -H "$auth" "$api/releases/tags/${TAG_NAME}" -o release.json; then
|
||||
python3 -c 'import json; print(json.load(open("release.json")).get("id",""))'
|
||||
fi
|
||||
}
|
||||
|
||||
rel_id="$(get_release_id || true)"
|
||||
|
||||
if [ -z "$rel_id" ]; then
|
||||
payload="$(python3 -c '
|
||||
import json, os, sys
|
||||
python3 - <<'PYCREATE' > create.json
|
||||
import json, os
|
||||
body = open("release-body.md", encoding="utf-8").read()
|
||||
json.dump({"tag_name": os.environ["TAG_NAME"], "name": os.environ["TAG_NAME"],
|
||||
"body": body, "draft": False, "prerelease": False}, sys.stdout)
|
||||
')"
|
||||
rel_id="$(printf '%s' "$payload" \
|
||||
| curl -sf -X POST -H "$auth" -H "Content-Type: application/json" -d @- "$api/releases" \
|
||||
| python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')"
|
||||
print(json.dumps({
|
||||
"tag_name": os.environ["TAG_NAME"],
|
||||
"name": os.environ["TAG_NAME"],
|
||||
"body": body,
|
||||
"draft": False,
|
||||
"prerelease": False,
|
||||
}))
|
||||
PYCREATE
|
||||
curl -sf -X POST -H "$auth" -H "Content-Type: application/json" \
|
||||
-d @create.json "$api/releases" -o created.json
|
||||
rel_id="$(python3 -c 'import json; print(json.load(open("created.json"))["id"])')"
|
||||
echo "Created release $rel_id for ${TAG_NAME}"
|
||||
else
|
||||
echo "Reusing release $rel_id for ${TAG_NAME}"
|
||||
fi
|
||||
|
||||
# Drop a same-named asset from an earlier attempt, or the upload 409s.
|
||||
old_id="$(curl -sf -H "$auth" "$api/releases/${rel_id}/assets" \
|
||||
| python3 -c 'import sys,json; print(next((a["id"] for a in json.load(sys.stdin) if a["name"]=="latest.zip"), ""))' 2>/dev/null || true)"
|
||||
# A same-named asset from an earlier attempt has to go, or the upload
|
||||
# collides with it. This is the state a recovery run finds.
|
||||
curl -sf -H "$auth" "$api/releases/${rel_id}/assets" -o assets.json
|
||||
old_id="$(python3 -c 'import json; print(next((a["id"] for a in json.load(open("assets.json")) if a["name"]=="latest.zip"), ""))')"
|
||||
if [ -n "$old_id" ]; then
|
||||
curl -sf -X DELETE -H "$auth" "$api/releases/${rel_id}/assets/${old_id}"
|
||||
echo "Replaced existing latest.zip (asset $old_id)"
|
||||
@@ -203,5 +218,5 @@ jobs:
|
||||
|
||||
curl -sf -X POST -H "$auth" \
|
||||
-F "attachment=@${ZIP_PATH};filename=latest.zip" \
|
||||
"$api/releases/${rel_id}/assets?name=latest.zip" \
|
||||
| python3 -c 'import sys,json; a=json.load(sys.stdin); print("Attached", a["name"], a["size"], "bytes")'
|
||||
"$api/releases/${rel_id}/assets?name=latest.zip" -o uploaded.json
|
||||
python3 -c 'import json; a=json.load(open("uploaded.json")); print("Attached", a["name"], a["size"], "bytes")'
|
||||
|
||||
Reference in New Issue
Block a user