From 0825170044a07678b87fe83cec87e0c96e736388 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Mon, 6 Jul 2026 00:33:47 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E6=9C=8D=E5=8A=A1=E7=AB=AF=E6=B5=81?= =?UTF-8?q?=E6=B0=B4=E7=BA=BF=E6=B3=A8=E5=85=A5=E5=8A=A0=E5=9B=BA(tag=20?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C/JSON=20=E8=BD=AC=E4=B9=89/workflow=20env)+?= =?UTF-8?q?=20gitignore=20server/out?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u --- .gitea/workflows/deploy-server.yml | 6 ++++-- .gitignore | 1 + scripts/ci/deploy-server.sh | 10 ++++++++++ scripts/ci/lib-forgejo.sh | 24 +++++++++++++++++++++--- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/deploy-server.yml b/.gitea/workflows/deploy-server.yml index ef722ce..5614d2b 100644 --- a/.gitea/workflows/deploy-server.yml +++ b/.gitea/workflows/deploy-server.yml @@ -32,9 +32,11 @@ jobs: env: FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }} FORGEJO_URL: ${{ secrets.FORGEJO_URL }} - run: bash scripts/ci/release-server.sh "${{ gitea.ref_name }}" + TAG: ${{ gitea.ref_name }} + run: bash scripts/ci/release-server.sh "$TAG" - name: Deploy → pangolin1 env: DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} - run: bash scripts/ci/deploy-server.sh "${{ gitea.ref_name }}" + TAG: ${{ gitea.ref_name }} + run: bash scripts/ci/deploy-server.sh "$TAG" diff --git a/.gitignore b/.gitignore index 7a3dc7b..000756a 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ app/kernel/.build/ # Go 编译产物(mock server 等) server/mockserver server/pangolin-server +server/out/ # Flutter / Dart 构建产物与本地配置 client/android/local.properties diff --git a/scripts/ci/deploy-server.sh b/scripts/ci/deploy-server.sh index 5963d6f..1a9dbf6 100644 --- a/scripts/ci/deploy-server.sh +++ b/scripts/ci/deploy-server.sh @@ -17,6 +17,16 @@ DB=/var/lib/pangolin/pangolin.db BIN=/usr/local/bin TAG="${1:?usage: deploy-server.sh }" +# Refuse anything that isn't a strict server-vX.Y.Z[-suffix] tag before it can +# reach the remote heredoc / backup paths below (command-injection guard). +# An anchored regex is used instead of a `case` glob: a trailing `*` in a +# case pattern matches ANY trailing characters (including shell metachars +# like `; rm -rf /`), which would defeat the point of this check. +if ! [[ "$TAG" =~ ^server-v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.]+)?$ ]]; then + echo "deploy-server: refusing unexpected tag '$TAG'" >&2 + exit 1 +fi + # setup_ssh registers the EXIT cleanup trap itself (before writing the key), # so a mid-setup failure still cleans up — see lib-ssh.sh. It exports # SSH_KEY_FILE / DEPLOY_PORT / SSH_KNOWN_HOSTS_FILE / DEPLOY_HOST used below diff --git a/scripts/ci/lib-forgejo.sh b/scripts/ci/lib-forgejo.sh index aaa71ac..471641a 100755 --- a/scripts/ci/lib-forgejo.sh +++ b/scripts/ci/lib-forgejo.sh @@ -59,18 +59,36 @@ forgejo_release_ensure() { rm -f "$get_body_file" echo "==> forgejo: creating release ${tag}" - local create_code_file create_body_file create_code + local create_code_file create_body_file create_code create_req_file create_code_file="/tmp/forgejo_create_code.$$" create_body_file="/tmp/forgejo_create_body.$$.json" + create_req_file="/tmp/forgejo_create_req.$$.json" + + # Build the JSON request body via python3's json.dumps rather than raw + # string interpolation, so a tag/title containing `"` / `\` / control + # characters can't break out of the JSON structure (json-injection guard). + # Values are piped in NUL-separated on stdin — never interpolated into the + # python source — and no $() command substitution is used. + printf '%s\0%s\0' "$tag" "$title" | python3 -c ' +import json +import sys + +raw = sys.stdin.buffer.read() +tag, title = (part.decode() for part in raw.split(b"\0")[:2]) +json.dump( + {"tag_name": tag, "name": title, "draft": False, "prerelease": False}, + sys.stdout, +) +' > "$create_req_file" curl "${FORGEJO_CURL_TLS[@]}" -s -o "$create_body_file" -w '%{http_code}' \ -X POST "${FORGEJO_URL}/api/v1/repos/${FORGEJO_REPO}/releases" \ -H "Authorization: token ${FORGEJO_TOKEN}" \ -H "Content-Type: application/json" \ - -d "{\"tag_name\":\"${tag}\",\"name\":\"${title}\",\"draft\":false,\"prerelease\":false}" \ + --data @"$create_req_file" \ > "$create_code_file" read -r create_code < "$create_code_file" - rm -f "$create_code_file" + rm -f "$create_code_file" "$create_req_file" if [ "$create_code" -lt 200 ] || [ "$create_code" -ge 300 ]; then echo "==> forgejo: release create FAILED (HTTP ${create_code})" >&2