fix(ci): 服务端流水线注入加固(tag 校验/JSON 转义/workflow env)+ gitignore server/out
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
@@ -32,9 +32,11 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
||||||
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
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
|
- name: Deploy → pangolin1
|
||||||
env:
|
env:
|
||||||
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
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"
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ app/kernel/.build/
|
|||||||
# Go 编译产物(mock server 等)
|
# Go 编译产物(mock server 等)
|
||||||
server/mockserver
|
server/mockserver
|
||||||
server/pangolin-server
|
server/pangolin-server
|
||||||
|
server/out/
|
||||||
|
|
||||||
# Flutter / Dart 构建产物与本地配置
|
# Flutter / Dart 构建产物与本地配置
|
||||||
client/android/local.properties
|
client/android/local.properties
|
||||||
|
|||||||
@@ -17,6 +17,16 @@ DB=/var/lib/pangolin/pangolin.db
|
|||||||
BIN=/usr/local/bin
|
BIN=/usr/local/bin
|
||||||
TAG="${1:?usage: deploy-server.sh <tag>}"
|
TAG="${1:?usage: deploy-server.sh <tag>}"
|
||||||
|
|
||||||
|
# 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),
|
# 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
|
# 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
|
# SSH_KEY_FILE / DEPLOY_PORT / SSH_KNOWN_HOSTS_FILE / DEPLOY_HOST used below
|
||||||
|
|||||||
@@ -59,18 +59,36 @@ forgejo_release_ensure() {
|
|||||||
rm -f "$get_body_file"
|
rm -f "$get_body_file"
|
||||||
|
|
||||||
echo "==> forgejo: creating release ${tag}"
|
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_code_file="/tmp/forgejo_create_code.$$"
|
||||||
create_body_file="/tmp/forgejo_create_body.$$.json"
|
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}' \
|
curl "${FORGEJO_CURL_TLS[@]}" -s -o "$create_body_file" -w '%{http_code}' \
|
||||||
-X POST "${FORGEJO_URL}/api/v1/repos/${FORGEJO_REPO}/releases" \
|
-X POST "${FORGEJO_URL}/api/v1/repos/${FORGEJO_REPO}/releases" \
|
||||||
-H "Authorization: token ${FORGEJO_TOKEN}" \
|
-H "Authorization: token ${FORGEJO_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"tag_name\":\"${tag}\",\"name\":\"${title}\",\"draft\":false,\"prerelease\":false}" \
|
--data @"$create_req_file" \
|
||||||
> "$create_code_file"
|
> "$create_code_file"
|
||||||
read -r create_code < "$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
|
if [ "$create_code" -lt 200 ] || [ "$create_code" -ge 300 ]; then
|
||||||
echo "==> forgejo: release create FAILED (HTTP ${create_code})" >&2
|
echo "==> forgejo: release create FAILED (HTTP ${create_code})" >&2
|
||||||
|
|||||||
Reference in New Issue
Block a user