From ff923ef089e7fb98db462adffe72e5c905737e63 Mon Sep 17 00:00:00 2001 From: jhfnetboy Date: Wed, 24 Jun 2026 08:10:00 +0700 Subject: [PATCH 1/4] security: remove hardcoded Infura API key from debug scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check-account.js and test-userop-hash.js hardcoded a Sepolia Infura API key in the RPC URL. Replaced with process.env.ETH_RPC_URL (public-RPC fallback) so the scripts still run without embedding a secret. NOTE: the key is already exposed in this public repo's git history (and was also present in an earlier ecosystem.config.js fallback), so it MUST be rotated in the Infura dashboard — removing it from the tree does not undo the exposure. The ETH_PRIVATE_KEY and Pimlico key that also appear in history must be rotated too. --- aastar/check-account.js | 2 +- aastar/test-userop-hash.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aastar/check-account.js b/aastar/check-account.js index f5310717..fbb80e6f 100644 --- a/aastar/check-account.js +++ b/aastar/check-account.js @@ -1,6 +1,6 @@ const { ethers } = require("ethers"); -const RPC_URL = "https://sepolia.infura.io/v3/7051eb377c77490881070faaf93aef20"; +const RPC_URL = process.env.ETH_RPC_URL || "https://ethereum-sepolia-rpc.publicnode.com"; const ACCOUNT_ADDRESS = "0x975961302a83090B1eb94676E1430B5baCa43F9E"; // Simple Account ABI - just the owner() function diff --git a/aastar/test-userop-hash.js b/aastar/test-userop-hash.js index 45be5b9d..35d91717 100644 --- a/aastar/test-userop-hash.js +++ b/aastar/test-userop-hash.js @@ -44,7 +44,7 @@ function calculateUserOpHashSDK() { // 方法2:调用 EntryPoint 合约(后端方式) async function calculateUserOpHashContract() { const provider = new ethers.JsonRpcProvider( - "https://sepolia.infura.io/v3/7051eb377c77490881070faaf93aef20" + process.env.ETH_RPC_URL || "https://ethereum-sepolia-rpc.publicnode.com" ); const entryPointABI = [ From c8046c6602b521926dfee448f44b5c7aca843d30 Mon Sep 17 00:00:00 2001 From: jhfnetboy Date: Wed, 24 Jun 2026 08:24:58 +0700 Subject: [PATCH 2/4] ci: add LeakShield secret-scanning workflow Scans the repo for private keys / API keys on push + PR to main/master/develop (complements the local pre-commit hook). Installed via LeakShield. --- .github/workflows/check-secrets.yml | 257 ++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 .github/workflows/check-secrets.yml diff --git a/.github/workflows/check-secrets.yml b/.github/workflows/check-secrets.yml new file mode 100644 index 00000000..b49cfde1 --- /dev/null +++ b/.github/workflows/check-secrets.yml @@ -0,0 +1,257 @@ +name: Security - Check for Private Keys + +on: + pull_request: + branches: [ main, master, develop ] + push: + branches: [ main, master, develop ] + +jobs: + check-secrets: + runs-on: ubuntu-latest + name: Scan for Private Keys and Secrets + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check for private keys and sensitive data + run: | + #!/bin/bash + set -e + + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + BLUE='\033[0;34m' + NC='\033[0m' + + # 排除的目录 + EXCLUDED_DIRS="node_modules|\.git|\.netlify|\.svelte-kit|build|dist|\.next|contracts/broadcast|contracts/cache|contracts/lib|test-results|playwright-report|\.auth|vendor|cache|out" + + # 扫描的文件扩展名 + SCAN_EXTENSIONS="\.(ts|tsx|js|jsx|svelte|sol|md|json|env|example|toml|yaml|yml)$" + + echo -e "${BLUE}🔒 GitHub Actions: Scanning repository for private keys...${NC}\n" + + # 获取所有文件(排除特定目录) + FILES_TO_SCAN=$(find . -type f | grep -E "$SCAN_EXTENSIONS" | grep -v -E "$EXCLUDED_DIRS" || true) + + if [ -z "$FILES_TO_SCAN" ]; then + echo -e "${GREEN}✅ No files to scan${NC}" + exit 0 + fi + + FILE_COUNT=$(echo "$FILES_TO_SCAN" | wc -l | tr -d ' ') + echo -e "${BLUE}📋 Scanning ${FILE_COUNT} file(s) for secrets...${NC}\n" + + # 检测模式 + # 1. Ethereum private key: 0x + 64 hex chars + ETHEREUM_KEY_PATTERN='0x[a-fA-F0-9]{64}' + + # 2. PEM format private keys + PEM_PATTERN='BEGIN.*PRIVATE KEY' + + # 3. AWS access key + AWS_ACCESS_PATTERN='AKIA[0-9A-Z]{16}' + + # 4. AWS secret key + AWS_SECRET_PATTERN='aws_secret_access_key[[:space:]]*=[[:space:]]*[a-zA-Z0-9/+]{40}' + + # 5. Private key with actual hex value + PRIVATE_KEY_WITH_VALUE_PATTERN='private[_-]key[[:space:]]*[:=][[:space:]]*0x[a-fA-F0-9]{32,}' + + # 6. OpenAI API keys (sk-... or sk-proj-...) + OPENAI_KEY_PATTERN='sk-[a-zA-Z0-9]{48,}' + OPENAI_PROJ_KEY_PATTERN='sk-proj-[a-zA-Z0-9_-]{48,}' + + # 7. Google AI (Gemini) API keys (AIza...) + GOOGLE_AI_KEY_PATTERN='AIza[a-zA-Z0-9_-]{35,}' + + # 8. Anthropic (Claude) API keys (sk-ant-...) + ANTHROPIC_KEY_PATTERN='sk-ant-[a-zA-Z0-9_-]{95,}' + + # 9. GitHub Personal Access Tokens (ghp_..., gho_..., ghs_...) + GITHUB_PAT_PATTERN='gh[pousr]_[a-zA-Z0-9]{36,}' + + # 10. Stripe API keys (sk_live_..., sk_test_...) + STRIPE_KEY_PATTERN='sk_(live|test)_[a-zA-Z0-9]{24,}' + + # 11. Generic API keys + API_KEY_PATTERN='api[_-]key[[:space:]]*[:=][[:space:]]*[\"\\x27][a-zA-Z0-9]{32,}[\"\\x27]' + + FOUND_SECRETS=0 + TOTAL_FINDINGS=0 + + for FILE in $FILES_TO_SCAN; do + if [ ! -f "$FILE" ]; then + continue + fi + + FINDINGS="" + + # Check Ethereum keys + if grep -nE "$ETHEREUM_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then + FINDINGS="${FINDINGS} [CRITICAL] Ethereum Private Key (256-bit hex)\n" + FINDINGS="${FINDINGS}$(grep -nE "$ETHEREUM_KEY_PATTERN" "$FILE" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + + # Check PEM keys + if grep -nE "$PEM_PATTERN" "$FILE" > /dev/null 2>&1; then + FINDINGS="${FINDINGS} [CRITICAL] PEM Private Key\n" + FINDINGS="${FINDINGS}$(grep -nE "$PEM_PATTERN" "$FILE" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + + # Check AWS access keys + if grep -nE "$AWS_ACCESS_PATTERN" "$FILE" > /dev/null 2>&1; then + FINDINGS="${FINDINGS} [CRITICAL] AWS Access Key\n" + FINDINGS="${FINDINGS}$(grep -nE "$AWS_ACCESS_PATTERN" "$FILE" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + + # Check AWS secret keys + if grep -nE "$AWS_SECRET_PATTERN" "$FILE" > /dev/null 2>&1; then + FINDINGS="${FINDINGS} [CRITICAL] AWS Secret Key\n" + FINDINGS="${FINDINGS}$(grep -nE "$AWS_SECRET_PATTERN" "$FILE" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + + # Check private keys with values + if grep -nEi "$PRIVATE_KEY_WITH_VALUE_PATTERN" "$FILE" > /dev/null 2>&1; then + PRIVATE_KEY_LINES=$(grep -nEi "$PRIVATE_KEY_WITH_VALUE_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") + if [ -n "$PRIVATE_KEY_LINES" ]; then + FINDINGS="${FINDINGS} [CRITICAL] Private Key with Hex Value\n" + FINDINGS="${FINDINGS}$(echo "$PRIVATE_KEY_LINES" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + fi + + # Check OpenAI API keys + if grep -nE "$OPENAI_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then + OPENAI_LINES=$(grep -nE "$OPENAI_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") + if [ -n "$OPENAI_LINES" ]; then + FINDINGS="${FINDINGS} [CRITICAL] OpenAI API Key\n" + FINDINGS="${FINDINGS}$(echo "$OPENAI_LINES" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + fi + + if grep -nE "$OPENAI_PROJ_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then + OPENAI_PROJ_LINES=$(grep -nE "$OPENAI_PROJ_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") + if [ -n "$OPENAI_PROJ_LINES" ]; then + FINDINGS="${FINDINGS} [CRITICAL] OpenAI Project API Key\n" + FINDINGS="${FINDINGS}$(echo "$OPENAI_PROJ_LINES" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + fi + + # Check Google AI (Gemini) API keys + if grep -nE "$GOOGLE_AI_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then + GOOGLE_LINES=$(grep -nE "$GOOGLE_AI_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") + if [ -n "$GOOGLE_LINES" ]; then + FINDINGS="${FINDINGS} [CRITICAL] Google AI (Gemini) API Key\n" + FINDINGS="${FINDINGS}$(echo "$GOOGLE_LINES" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + fi + + # Check Anthropic (Claude) API keys + if grep -nE "$ANTHROPIC_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then + ANTHROPIC_LINES=$(grep -nE "$ANTHROPIC_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") + if [ -n "$ANTHROPIC_LINES" ]; then + FINDINGS="${FINDINGS} [CRITICAL] Anthropic (Claude) API Key\n" + FINDINGS="${FINDINGS}$(echo "$ANTHROPIC_LINES" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + fi + + # Check GitHub tokens + if grep -nE "$GITHUB_PAT_PATTERN" "$FILE" > /dev/null 2>&1; then + GITHUB_LINES=$(grep -nE "$GITHUB_PAT_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") + if [ -n "$GITHUB_LINES" ]; then + FINDINGS="${FINDINGS} [CRITICAL] GitHub Personal Access Token\n" + FINDINGS="${FINDINGS}$(echo "$GITHUB_LINES" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + fi + + # Check Stripe API keys + if grep -nE "$STRIPE_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then + STRIPE_LINES=$(grep -nE "$STRIPE_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") + if [ -n "$STRIPE_LINES" ]; then + FINDINGS="${FINDINGS} [CRITICAL] Stripe API Key\n" + FINDINGS="${FINDINGS}$(echo "$STRIPE_LINES" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + fi + + # Check API keys + if grep -nEi "$API_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then + API_KEY_LINES=$(grep -nEi "$API_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") + if [ -n "$API_KEY_LINES" ]; then + FINDINGS="${FINDINGS} [WARNING] Possible API Key\n" + FINDINGS="${FINDINGS}$(echo "$API_KEY_LINES" | head -3 | sed 's/^/ /')\n" + FOUND_SECRETS=1 + TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) + fi + fi + + if [ -n "$FINDINGS" ]; then + echo -e "${RED}❌ ${FILE}:${NC}" + echo -e "$FINDINGS" + fi + done + + if [ $FOUND_SECRETS -eq 1 ]; then + echo -e "${RED}========================================${NC}" + echo -e "${RED}🚨 SECURITY ALERT: ${TOTAL_FINDINGS} secret(s) detected!${NC}\n" + echo -e "${YELLOW}⚠️ Do NOT merge this code!${NC}\n" + echo -e "Actions required:" + echo -e " 1. ${YELLOW}Remove${NC} all private keys and secrets from the code" + echo -e " 2. Use ${BLUE}.env${NC} files for sensitive data (add to ${BLUE}.gitignore${NC})" + echo -e " 3. Rotate any exposed credentials immediately" + echo -e " 4. Review git history for leaked secrets" + echo -e " 5. Consider using tools like ${BLUE}git-secrets${NC} or ${BLUE}truffleHog${NC}\n" + echo -e "${RED}========================================${NC}\n" + + # 设置 GitHub Actions 注解 + echo "::error::Private keys or secrets detected in the codebase. Please remove them before merging." + + exit 1 + fi + + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}✅ No private keys detected${NC}" + echo -e "${GREEN}✅ Security check passed!${NC}" + echo -e "${GREEN}========================================${NC}\n" + + exit 0 + + - name: Comment on PR (if secrets found) + if: failure() && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '🚨 **Security Alert**: Private keys or secrets detected in this PR!\n\n' + + '⚠️ Please remove all sensitive data before merging.\n\n' + + 'Check the Actions log for details about what was found.' + }) From 1ee2d2a07a842ad1124cf61fa34b8e0ec0cb8743 Mon Sep 17 00:00:00 2001 From: jhfnetboy Date: Wed, 24 Jun 2026 08:27:55 +0700 Subject: [PATCH 3/4] security: add optional git-history scrub script for the leaked keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/security/scrub-history.sh purges the 3 leaked credentials from git history via git-filter-repo. Secrets are extracted from history at runtime (not hardcoded), written to a .git-local replacements file, and deleted after. Dry-run by default; --run rewrites history (then force-push + collaborators re-clone). Clearly marked: rotation is mandatory regardless — scrubbing a public repo's history does NOT undo the exposure. --- scripts/security/scrub-history.sh | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100755 scripts/security/scrub-history.sh diff --git a/scripts/security/scrub-history.sh b/scripts/security/scrub-history.sh new file mode 100755 index 00000000..8cf5be9a --- /dev/null +++ b/scripts/security/scrub-history.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# +# scrub-history.sh — purge the leaked credentials from this repo's git history. +# +# ⚠️ ROTATION FIRST. This repo is public and the secrets are already cloned/cached/ +# indexed elsewhere. Rewriting history does NOT undo the exposure — you MUST still +# rotate every key (Infura, ETH_PRIVATE_KEY 0x075F227E…, Pimlico). This script is +# optional hygiene to stop re-leaking them on fresh clones, not a substitute for rotation. +# +# ⚠️ DESTRUCTIVE. It rewrites EVERY commit hash. After running you must force-push and +# every collaborator must re-clone (old clones keep the secrets and will reintroduce them). +# +# The leaked secrets are NOT hardcoded here — they're extracted from history at runtime +# into a .git-local (never-committed) replacements file, which is deleted afterward. +# +# Usage: +# bash scripts/security/scrub-history.sh # dry-run: show what would be scrubbed +# bash scripts/security/scrub-history.sh --run # actually rewrite history +# +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +if ! command -v git-filter-repo >/dev/null 2>&1; then + echo "❌ git-filter-repo not installed." + echo " brew install git-filter-repo (or: pip install git-filter-repo)" + exit 1 +fi + +# The commit that introduced the secrets (Dockerfile fallbacks). Adjust if needed. +SRC_COMMIT="ca83890" +REPL_FILE=".git/scrub-replacements.txt" +trap 'rm -f "$REPL_FILE"' EXIT + +# Extract the literal secrets from history (Infura key, ETH key, Pimlico key). +{ + git show "$SRC_COMMIT:Dockerfile" 2>/dev/null \ + | grep -oE '7051eb377c77[0-9a-f]+|0x72966a3f[0-9a-fA-F]{56}|pim_[A-Za-z0-9]{20,}' \ + | sort -u +} > "$REPL_FILE.raw" || true + +if [ ! -s "$REPL_FILE.raw" ]; then + echo "❌ No secrets found in $SRC_COMMIT — check the commit hash / patterns." + rm -f "$REPL_FILE.raw" + exit 1 +fi + +# Build the filter-repo replacements file: ==>***SCRUBBED*** +: > "$REPL_FILE" +while IFS= read -r secret; do + printf '%s==>***SCRUBBED***\n' "$secret" >> "$REPL_FILE" +done < "$REPL_FILE.raw" +COUNT=$(wc -l < "$REPL_FILE" | tr -d ' ') +rm -f "$REPL_FILE.raw" + +echo "🔎 Found $COUNT distinct secret(s) to scrub from history (values hidden)." + +if [ "${1:-}" != "--run" ]; then + echo "" + echo "Dry run. Re-run with --run to rewrite history. After --run you MUST:" + echo " 1) git push --force --all && git push --force --tags" + echo " 2) tell every collaborator to re-clone (old clones still leak)" + echo " 3) ROTATE the keys regardless — scrubbing doesn't undo the public exposure" + exit 0 +fi + +echo "⏳ Rewriting history with git-filter-repo…" +git filter-repo --replace-text "$REPL_FILE" --force + +echo "" +echo "✅ History rewritten. NOW:" +echo " 1) git push --force --all && git push --force --tags" +echo " 2) all collaborators re-clone" +echo " 3) ROTATE the keys (Infura / ETH 0x075F227E… / Pimlico) — mandatory, exposure already happened" From 57c4a7189ef7635118f70711deb23aaad75362e9 Mon Sep 17 00:00:00 2001 From: jhfnetboy Date: Wed, 24 Jun 2026 08:49:04 +0700 Subject: [PATCH 4/4] ci: drop check-secrets workflow for now (floods false positives on bytes32) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LeakShield's 0x[a-fA-F0-9]{64} pattern can't distinguish a private key from a legitimate bytes32 (hashes, storage slots, calldata, test fixtures) — it flagged many false positives across the repo, making the check permanently red. Keeping the Infura-key removal + history-scrub script here; the detection-quality fix (context-aware matching) goes upstream to LeakShield first, then we re-adopt. --- .github/workflows/check-secrets.yml | 257 ---------------------------- 1 file changed, 257 deletions(-) delete mode 100644 .github/workflows/check-secrets.yml diff --git a/.github/workflows/check-secrets.yml b/.github/workflows/check-secrets.yml deleted file mode 100644 index b49cfde1..00000000 --- a/.github/workflows/check-secrets.yml +++ /dev/null @@ -1,257 +0,0 @@ -name: Security - Check for Private Keys - -on: - pull_request: - branches: [ main, master, develop ] - push: - branches: [ main, master, develop ] - -jobs: - check-secrets: - runs-on: ubuntu-latest - name: Scan for Private Keys and Secrets - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check for private keys and sensitive data - run: | - #!/bin/bash - set -e - - RED='\033[0;31m' - GREEN='\033[0;32m' - YELLOW='\033[1;33m' - BLUE='\033[0;34m' - NC='\033[0m' - - # 排除的目录 - EXCLUDED_DIRS="node_modules|\.git|\.netlify|\.svelte-kit|build|dist|\.next|contracts/broadcast|contracts/cache|contracts/lib|test-results|playwright-report|\.auth|vendor|cache|out" - - # 扫描的文件扩展名 - SCAN_EXTENSIONS="\.(ts|tsx|js|jsx|svelte|sol|md|json|env|example|toml|yaml|yml)$" - - echo -e "${BLUE}🔒 GitHub Actions: Scanning repository for private keys...${NC}\n" - - # 获取所有文件(排除特定目录) - FILES_TO_SCAN=$(find . -type f | grep -E "$SCAN_EXTENSIONS" | grep -v -E "$EXCLUDED_DIRS" || true) - - if [ -z "$FILES_TO_SCAN" ]; then - echo -e "${GREEN}✅ No files to scan${NC}" - exit 0 - fi - - FILE_COUNT=$(echo "$FILES_TO_SCAN" | wc -l | tr -d ' ') - echo -e "${BLUE}📋 Scanning ${FILE_COUNT} file(s) for secrets...${NC}\n" - - # 检测模式 - # 1. Ethereum private key: 0x + 64 hex chars - ETHEREUM_KEY_PATTERN='0x[a-fA-F0-9]{64}' - - # 2. PEM format private keys - PEM_PATTERN='BEGIN.*PRIVATE KEY' - - # 3. AWS access key - AWS_ACCESS_PATTERN='AKIA[0-9A-Z]{16}' - - # 4. AWS secret key - AWS_SECRET_PATTERN='aws_secret_access_key[[:space:]]*=[[:space:]]*[a-zA-Z0-9/+]{40}' - - # 5. Private key with actual hex value - PRIVATE_KEY_WITH_VALUE_PATTERN='private[_-]key[[:space:]]*[:=][[:space:]]*0x[a-fA-F0-9]{32,}' - - # 6. OpenAI API keys (sk-... or sk-proj-...) - OPENAI_KEY_PATTERN='sk-[a-zA-Z0-9]{48,}' - OPENAI_PROJ_KEY_PATTERN='sk-proj-[a-zA-Z0-9_-]{48,}' - - # 7. Google AI (Gemini) API keys (AIza...) - GOOGLE_AI_KEY_PATTERN='AIza[a-zA-Z0-9_-]{35,}' - - # 8. Anthropic (Claude) API keys (sk-ant-...) - ANTHROPIC_KEY_PATTERN='sk-ant-[a-zA-Z0-9_-]{95,}' - - # 9. GitHub Personal Access Tokens (ghp_..., gho_..., ghs_...) - GITHUB_PAT_PATTERN='gh[pousr]_[a-zA-Z0-9]{36,}' - - # 10. Stripe API keys (sk_live_..., sk_test_...) - STRIPE_KEY_PATTERN='sk_(live|test)_[a-zA-Z0-9]{24,}' - - # 11. Generic API keys - API_KEY_PATTERN='api[_-]key[[:space:]]*[:=][[:space:]]*[\"\\x27][a-zA-Z0-9]{32,}[\"\\x27]' - - FOUND_SECRETS=0 - TOTAL_FINDINGS=0 - - for FILE in $FILES_TO_SCAN; do - if [ ! -f "$FILE" ]; then - continue - fi - - FINDINGS="" - - # Check Ethereum keys - if grep -nE "$ETHEREUM_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then - FINDINGS="${FINDINGS} [CRITICAL] Ethereum Private Key (256-bit hex)\n" - FINDINGS="${FINDINGS}$(grep -nE "$ETHEREUM_KEY_PATTERN" "$FILE" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - - # Check PEM keys - if grep -nE "$PEM_PATTERN" "$FILE" > /dev/null 2>&1; then - FINDINGS="${FINDINGS} [CRITICAL] PEM Private Key\n" - FINDINGS="${FINDINGS}$(grep -nE "$PEM_PATTERN" "$FILE" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - - # Check AWS access keys - if grep -nE "$AWS_ACCESS_PATTERN" "$FILE" > /dev/null 2>&1; then - FINDINGS="${FINDINGS} [CRITICAL] AWS Access Key\n" - FINDINGS="${FINDINGS}$(grep -nE "$AWS_ACCESS_PATTERN" "$FILE" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - - # Check AWS secret keys - if grep -nE "$AWS_SECRET_PATTERN" "$FILE" > /dev/null 2>&1; then - FINDINGS="${FINDINGS} [CRITICAL] AWS Secret Key\n" - FINDINGS="${FINDINGS}$(grep -nE "$AWS_SECRET_PATTERN" "$FILE" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - - # Check private keys with values - if grep -nEi "$PRIVATE_KEY_WITH_VALUE_PATTERN" "$FILE" > /dev/null 2>&1; then - PRIVATE_KEY_LINES=$(grep -nEi "$PRIVATE_KEY_WITH_VALUE_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") - if [ -n "$PRIVATE_KEY_LINES" ]; then - FINDINGS="${FINDINGS} [CRITICAL] Private Key with Hex Value\n" - FINDINGS="${FINDINGS}$(echo "$PRIVATE_KEY_LINES" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - fi - - # Check OpenAI API keys - if grep -nE "$OPENAI_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then - OPENAI_LINES=$(grep -nE "$OPENAI_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") - if [ -n "$OPENAI_LINES" ]; then - FINDINGS="${FINDINGS} [CRITICAL] OpenAI API Key\n" - FINDINGS="${FINDINGS}$(echo "$OPENAI_LINES" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - fi - - if grep -nE "$OPENAI_PROJ_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then - OPENAI_PROJ_LINES=$(grep -nE "$OPENAI_PROJ_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") - if [ -n "$OPENAI_PROJ_LINES" ]; then - FINDINGS="${FINDINGS} [CRITICAL] OpenAI Project API Key\n" - FINDINGS="${FINDINGS}$(echo "$OPENAI_PROJ_LINES" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - fi - - # Check Google AI (Gemini) API keys - if grep -nE "$GOOGLE_AI_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then - GOOGLE_LINES=$(grep -nE "$GOOGLE_AI_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") - if [ -n "$GOOGLE_LINES" ]; then - FINDINGS="${FINDINGS} [CRITICAL] Google AI (Gemini) API Key\n" - FINDINGS="${FINDINGS}$(echo "$GOOGLE_LINES" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - fi - - # Check Anthropic (Claude) API keys - if grep -nE "$ANTHROPIC_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then - ANTHROPIC_LINES=$(grep -nE "$ANTHROPIC_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") - if [ -n "$ANTHROPIC_LINES" ]; then - FINDINGS="${FINDINGS} [CRITICAL] Anthropic (Claude) API Key\n" - FINDINGS="${FINDINGS}$(echo "$ANTHROPIC_LINES" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - fi - - # Check GitHub tokens - if grep -nE "$GITHUB_PAT_PATTERN" "$FILE" > /dev/null 2>&1; then - GITHUB_LINES=$(grep -nE "$GITHUB_PAT_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") - if [ -n "$GITHUB_LINES" ]; then - FINDINGS="${FINDINGS} [CRITICAL] GitHub Personal Access Token\n" - FINDINGS="${FINDINGS}$(echo "$GITHUB_LINES" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - fi - - # Check Stripe API keys - if grep -nE "$STRIPE_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then - STRIPE_LINES=$(grep -nE "$STRIPE_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") - if [ -n "$STRIPE_LINES" ]; then - FINDINGS="${FINDINGS} [CRITICAL] Stripe API Key\n" - FINDINGS="${FINDINGS}$(echo "$STRIPE_LINES" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - fi - - # Check API keys - if grep -nEi "$API_KEY_PATTERN" "$FILE" > /dev/null 2>&1; then - API_KEY_LINES=$(grep -nEi "$API_KEY_PATTERN" "$FILE" | grep -v "^[[:space:]]*#" | grep -v "^[[:space:]]*//") - if [ -n "$API_KEY_LINES" ]; then - FINDINGS="${FINDINGS} [WARNING] Possible API Key\n" - FINDINGS="${FINDINGS}$(echo "$API_KEY_LINES" | head -3 | sed 's/^/ /')\n" - FOUND_SECRETS=1 - TOTAL_FINDINGS=$((TOTAL_FINDINGS + 1)) - fi - fi - - if [ -n "$FINDINGS" ]; then - echo -e "${RED}❌ ${FILE}:${NC}" - echo -e "$FINDINGS" - fi - done - - if [ $FOUND_SECRETS -eq 1 ]; then - echo -e "${RED}========================================${NC}" - echo -e "${RED}🚨 SECURITY ALERT: ${TOTAL_FINDINGS} secret(s) detected!${NC}\n" - echo -e "${YELLOW}⚠️ Do NOT merge this code!${NC}\n" - echo -e "Actions required:" - echo -e " 1. ${YELLOW}Remove${NC} all private keys and secrets from the code" - echo -e " 2. Use ${BLUE}.env${NC} files for sensitive data (add to ${BLUE}.gitignore${NC})" - echo -e " 3. Rotate any exposed credentials immediately" - echo -e " 4. Review git history for leaked secrets" - echo -e " 5. Consider using tools like ${BLUE}git-secrets${NC} or ${BLUE}truffleHog${NC}\n" - echo -e "${RED}========================================${NC}\n" - - # 设置 GitHub Actions 注解 - echo "::error::Private keys or secrets detected in the codebase. Please remove them before merging." - - exit 1 - fi - - echo -e "${GREEN}========================================${NC}" - echo -e "${GREEN}✅ No private keys detected${NC}" - echo -e "${GREEN}✅ Security check passed!${NC}" - echo -e "${GREEN}========================================${NC}\n" - - exit 0 - - - name: Comment on PR (if secrets found) - if: failure() && github.event_name == 'pull_request' - uses: actions/github-script@v7 - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: '🚨 **Security Alert**: Private keys or secrets detected in this PR!\n\n' + - '⚠️ Please remove all sensitive data before merging.\n\n' + - 'Check the Actions log for details about what was found.' - })