From 64f26e76f690c6c9778b045c9d2cfd54693fb8e1 Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Tue, 30 Jun 2026 13:05:04 +0800 Subject: [PATCH 1/4] chore: add security automation workflows --- .github/dependabot.yml | 29 ++++ .github/workflows/codeql.yml | 44 ++++++ .github/workflows/security-extra.yml | 199 +++++++++++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/security-extra.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ffedab4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,29 @@ +version: 2 + +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "05:00" + timezone: "Asia/Shanghai" + open-pull-requests-limit: 5 + groups: + npm-minor-and-patch: + update-types: + - "minor" + - "patch" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "05:10" + timezone: "Asia/Shanghai" + open-pull-requests-limit: 5 + groups: + github-actions: + patterns: + - "*" \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..fb30230 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,44 @@ +name: "CodeQL Advanced" + +on: + push: + branches: + - "**" + +permissions: + contents: read + actions: read + security-events: write + packages: read + +jobs: + analyze: + name: CodeQL Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: javascript-typescript + build-mode: none + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + with: + persist-credentials: false + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + queries: security-extended,security-and-quality + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{ matrix.language }}" \ No newline at end of file diff --git a/.github/workflows/security-extra.yml b/.github/workflows/security-extra.yml new file mode 100644 index 0000000..6bb9015 --- /dev/null +++ b/.github/workflows/security-extra.yml @@ -0,0 +1,199 @@ +name: "Extra Security Scan" + +on: + push: + branches: + - "**" + +permissions: + contents: read + +jobs: + gitleaks: + name: Gitleaks Secret Scan + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout full history + uses: actions/checkout@v7 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Run Gitleaks + uses: gitleaks/gitleaks-action@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_ENABLE_SUMMARY: "true" + GITLEAKS_ENABLE_UPLOAD_ARTIFACT: "true" + # 如果仓库属于 GitHub Organization,需要在 Settings -> Secrets 里加 GITLEAKS_LICENSE + # GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} + + osv: + name: OSV Dependency Scan + uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.3.8 + + permissions: + contents: read + actions: read + security-events: write + + with: + scan-args: |- + --recursive + ./ + upload-sarif: true + fail-on-vuln: true + + pnpm-audit: + name: pnpm audit + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 22 + + - name: Run pnpm audit + shell: bash + run: | + if [ ! -f pnpm-lock.yaml ]; then + echo "pnpm-lock.yaml not found, skip pnpm audit." + exit 0 + fi + + corepack enable + corepack prepare pnpm@10 --activate + pnpm audit --audit-level=high + + semgrep: + name: Semgrep CE Scan + runs-on: ubuntu-latest + + permissions: + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + with: + persist-credentials: false + + - name: Run Semgrep CE + shell: bash + run: | + docker run --rm \ + -v "${PWD}:/src" \ + -w /src \ + semgrep/semgrep:latest \ + semgrep scan --config p/default --sarif --output semgrep.sarif . || true + + if [ ! -f semgrep.sarif ]; then + cat > semgrep.sarif <<'EOF' + { + "version": "2.1.0", + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "runs": [ + { + "tool": { + "driver": { + "name": "Semgrep", + "informationUri": "https://semgrep.dev", + "rules": [] + } + }, + "results": [] + } + ] + } + EOF + fi + + - name: Upload Semgrep SARIF + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: semgrep.sarif + category: semgrep + + actionlint: + name: GitHub Actions Syntax Scan + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + with: + persist-credentials: false + + - name: Run actionlint + shell: bash + run: | + docker run --rm \ + -v "${PWD}:/repo" \ + -w /repo \ + rhysd/actionlint:latest + + zizmor: + name: GitHub Actions Security Scan + runs-on: ubuntu-latest + + permissions: + contents: read + actions: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@v0.5.7 + with: + persona: auditor + min-severity: medium + min-confidence: medium + + scorecard: + name: OpenSSF Scorecard + runs-on: ubuntu-latest + + permissions: + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + with: + persist-credentials: false + + - name: Run OpenSSF Scorecard + uses: ossf/scorecard-action@v2.4.3 + with: + results_file: scorecard.sarif + results_format: sarif + publish_results: false + + - name: Upload Scorecard SARIF + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: scorecard.sarif + category: openssf-scorecard \ No newline at end of file From 32b3d2ade193b352c9520bc4e5ee689a81848a65 Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Tue, 30 Jun 2026 13:11:07 +0800 Subject: [PATCH 2/4] chore: override ws vulnerability --- package-lock.json | 6 +++--- package.json | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4a103ae..a6c14aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4666,9 +4666,9 @@ } }, "node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "version": "8.21.0", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index bc1b3b3..de09f26 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "overrides": { "undici": ">=7.28.0", "@babel/core": ">=7.29.6", - "esbuild": ">=0.28.1" + "esbuild": ">=0.28.1", + "ws": "8.21.0" }, "devDependencies": { "@cloudflare/workers-types": "^4.20260131.0", From 78af1f9bdd905d0c6584fc6815de5bd942f4e4de Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Tue, 30 Jun 2026 13:14:23 +0800 Subject: [PATCH 3/4] ci: skip scorecard outside main --- .github/workflows/security-extra.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/security-extra.yml b/.github/workflows/security-extra.yml index 6bb9015..d3d1b85 100644 --- a/.github/workflows/security-extra.yml +++ b/.github/workflows/security-extra.yml @@ -174,6 +174,7 @@ jobs: scorecard: name: OpenSSF Scorecard runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' permissions: contents: read @@ -196,4 +197,4 @@ jobs: uses: github/codeql-action/upload-sarif@v4 with: sarif_file: scorecard.sarif - category: openssf-scorecard \ No newline at end of file + category: openssf-scorecard From 49c872a8ec8081e8244df98f59771da7b8cb1387 Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Tue, 30 Jun 2026 13:18:02 +0800 Subject: [PATCH 4/4] ci: fix sync workflow shell lint --- .github/workflows/sync-upstream.yml | 42 +++++++++++++++++------------ 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml index 2925833..367904f 100644 --- a/.github/workflows/sync-upstream.yml +++ b/.github/workflows/sync-upstream.yml @@ -49,9 +49,11 @@ jobs: echo "Tag '$LATEST_TAG' not found after fetch." exit 1 fi - echo "mode=auto" >> $GITHUB_OUTPUT - echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT - echo "target_sha=$TARGET_SHA" >> $GITHUB_OUTPUT + { + echo "mode=auto" + echo "latest_tag=$LATEST_TAG" + echo "target_sha=$TARGET_SHA" + } >> "$GITHUB_OUTPUT" echo "Auto mode — latest release: $LATEST_TAG ($TARGET_SHA)" elif [ -n "$MANUAL_INPUT" ]; then @@ -61,15 +63,19 @@ jobs: echo "Cannot resolve '$MANUAL_INPUT' to a commit." exit 1 fi - echo "mode=manual" >> $GITHUB_OUTPUT - echo "target_sha=$TARGET_SHA" >> $GITHUB_OUTPUT + { + echo "mode=manual" + echo "target_sha=$TARGET_SHA" + } >> "$GITHUB_OUTPUT" echo "Manual mode — target: $MANUAL_INPUT ($TARGET_SHA)" else # Manual mode, blank input: use latest commit on upstream/main TARGET_SHA=$(git rev-parse upstream/main) - echo "mode=manual" >> $GITHUB_OUTPUT - echo "target_sha=$TARGET_SHA" >> $GITHUB_OUTPUT + { + echo "mode=manual" + echo "target_sha=$TARGET_SHA" + } >> "$GITHUB_OUTPUT" echo "Manual mode — latest commit: $TARGET_SHA" fi @@ -84,19 +90,19 @@ jobs: CURRENT_SHA=$(git rev-parse HEAD) if [ "$CURRENT_SHA" = "$TARGET_SHA" ]; then echo "Already at $TARGET_SHA — skipping." - echo "needs_update=false" >> $GITHUB_OUTPUT + echo "needs_update=false" >> "$GITHUB_OUTPUT" else echo "Switching to $TARGET_SHA" - echo "needs_update=true" >> $GITHUB_OUTPUT + echo "needs_update=true" >> "$GITHUB_OUTPUT" fi else # Auto: skip if target is already in ancestry if git merge-base --is-ancestor "$TARGET_SHA" HEAD 2>/dev/null; then echo "Already up to date with $TARGET_SHA — skipping." - echo "needs_update=false" >> $GITHUB_OUTPUT + echo "needs_update=false" >> "$GITHUB_OUTPUT" else echo "Update needed — target: $TARGET_SHA" - echo "needs_update=true" >> $GITHUB_OUTPUT + echo "needs_update=true" >> "$GITHUB_OUTPUT" fi fi @@ -117,7 +123,7 @@ jobs: if: steps.check.outputs.needs_update == 'true' run: | # Always keep our own workflow file, never let upstream overwrite it - git checkout HEAD@{1} -- .github/workflows/sync-upstream.yml 2>/dev/null || true + git checkout 'HEAD@{1}' -- .github/workflows/sync-upstream.yml 2>/dev/null || true if ! git diff --cached --quiet; then git commit -m "chore: restore sync-upstream workflow after sync" fi @@ -134,10 +140,12 @@ jobs: - name: Summary run: | if [ "${{ steps.check.outputs.needs_update }}" = "true" ]; then - echo "### Synced successfully" >> $GITHUB_STEP_SUMMARY - echo "- **Mode:** ${{ steps.resolve.outputs.mode }}" >> $GITHUB_STEP_SUMMARY - echo "- **Tag:** ${{ steps.resolve.outputs.latest_tag || 'N/A (manual)' }}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit:** \`${{ steps.resolve.outputs.target_sha }}\`" >> $GITHUB_STEP_SUMMARY + { + echo "### Synced successfully" + echo "- **Mode:** ${{ steps.resolve.outputs.mode }}" + echo "- **Tag:** ${{ steps.resolve.outputs.latest_tag || 'N/A (manual)' }}" + echo "- **Commit:** \`${{ steps.resolve.outputs.target_sha }}\`" + } >> "$GITHUB_STEP_SUMMARY" else - echo "### Nothing to update" >> $GITHUB_STEP_SUMMARY + echo "### Nothing to update" >> "$GITHUB_STEP_SUMMARY" fi