From 0e89d5f534654df92544e2b53902b72ed689c27a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 21:57:09 +0000 Subject: [PATCH 1/6] Initial plan From 2fc6eee3752ed07690c92814111bc999f894faf2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 22:00:27 +0000 Subject: [PATCH 2/6] Fix etherscan-apiv2.yml to prevent API key exposure in logs Co-authored-by: Kushmanmb <193178375+Kushmanmb@users.noreply.github.com> --- .github/workflows/etherscan-apiv2.yml | 34 +++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/.github/workflows/etherscan-apiv2.yml b/.github/workflows/etherscan-apiv2.yml index 9d8a3fa8f2f9..0017c6cfbd34 100644 --- a/.github/workflows/etherscan-apiv2.yml +++ b/.github/workflows/etherscan-apiv2.yml @@ -15,6 +15,15 @@ # This workflow is part of the kushmanmb-org/bitcoin repository # and is maintained by the repository owner and authorized contributors. # ═══════════════════════════════════════════════════════════════════ +# +# SECURITY NOTICE: +# This workflow handles sensitive API keys and follows security best practices: +# - API keys are passed via HTTP headers, not URL parameters +# - Shell debugging (set -x) is explicitly disabled +# - Secrets are masked using GitHub Actions ::add-mask:: command +# - All API requests use HTTPS +# - No sensitive data is logged or exposed in workflow outputs +# ═══════════════════════════════════════════════════════════════════ name: Etherscan API Integration (kushmanmb.eth) @@ -67,6 +76,16 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 + - name: Security - Disable shell debugging and mask secrets + run: | + # Ensure shell debugging is disabled to prevent secret exposure + set +x + # Mask API key if accidentally echoed (additional protection) + if [ -n "${{ secrets.ETHERSCAN_API_KEY }}" ]; then + echo "::add-mask::${{ secrets.ETHERSCAN_API_KEY }}" + fi + echo "✓ Security measures applied" + - name: Setup environment run: | echo "WORKFLOW_RUN_TIME=$(date -u +%Y-%m-%d-%H-%M-%S)" >> $GITHUB_ENV @@ -104,11 +123,12 @@ jobs: # Use Etherscan API to resolve ENS name echo "Attempting ENS resolution via Etherscan API V2..." - # Use Etherscan's ENS lookup API V2 with error handling - ENS_LOOKUP_URL="https://api.etherscan.io/v2/api?chainid=1&module=ens&action=getaddress&name=${ENS_NAME}&apikey=${{ secrets.ETHERSCAN_API_KEY }}" + # Security: Use header-based authentication to prevent API key exposure in URLs + # Build URL without API key + ENS_LOOKUP_URL="https://api.etherscan.io/v2/api?chainid=1&module=ens&action=getaddress&name=${ENS_NAME}" - # Fetch with error handling - if ! API_RESPONSE=$(curl -s -f "${ENS_LOOKUP_URL}"); then + # Fetch with error handling - API key passed as header, not in URL + if ! API_RESPONSE=$(curl -s -f -H "X-Api-Key: ${{ secrets.ETHERSCAN_API_KEY }}" "${ENS_LOOKUP_URL}"); then echo "⚠️ Failed to connect to Etherscan API" echo "ENS_NAME=${ENS_NAME}" >> $GITHUB_ENV echo "ENS_RESOLVED=false" >> $GITHUB_ENV @@ -229,9 +249,9 @@ jobs: # Build API URL - All endpoints now use V2 API api_url="${BASE_URL}?chainid=1&module=${MODULE}&action=${ACTION}${PARAMS}" - # Make request with API key from secret - # Key is only exposed during curl execution, not in logs - curl -s "${api_url}&apikey=${{ secrets.ETHERSCAN_API_KEY }}" \ + # Security: Make request with API key in header to prevent exposure in logs + # The API key is passed as a header, not in the URL, to avoid logging + curl -s -H "X-Api-Key: ${{ secrets.ETHERSCAN_API_KEY }}" "${api_url}" \ -o data/etherscan/latest.json || { echo '{"status":"0","message":"API request failed"}' \ > data/etherscan/latest.json From 9af4c6f0a0a2b33e0b723e37244ac37bbe9d5317 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 22:01:38 +0000 Subject: [PATCH 3/6] Add security masking for GitHub Actions runtime token in docker configuration Co-authored-by: Kushmanmb <193178375+Kushmanmb@users.noreply.github.com> --- .github/actions/configure-docker/action.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/actions/configure-docker/action.yml b/.github/actions/configure-docker/action.yml index 5c9531d35925..7ffd2508780f 100644 --- a/.github/actions/configure-docker/action.yml +++ b/.github/actions/configure-docker/action.yml @@ -1,5 +1,7 @@ name: 'Configure Docker' description: 'Set up Docker build driver and configure build cache args' +# Security: This action handles GitHub Actions cache tokens (ACTIONS_RUNTIME_TOKEN) +# which are automatically masked to prevent exposure in logs. inputs: cache-provider: description: 'gha or cirrus cache provider' @@ -28,6 +30,10 @@ runs: script: | Object.keys(process.env).forEach(function (key) { if (key.startsWith('ACTIONS_')) { + // Security: Mask the runtime token to prevent accidental exposure in logs + if (key === 'ACTIONS_RUNTIME_TOKEN') { + core.setSecret(process.env[key]); + } core.info(`Exporting ${key}`); core.exportVariable(key, process.env[key]); } From e3c4133d467750b28ac5f07a8bce63fbb41b6069 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 22:02:30 +0000 Subject: [PATCH 4/6] Add comprehensive workflow security documentation Co-authored-by: Kushmanmb <193178375+Kushmanmb@users.noreply.github.com> --- .github/WORKFLOW_SECURITY.md | 206 +++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 .github/WORKFLOW_SECURITY.md diff --git a/.github/WORKFLOW_SECURITY.md b/.github/WORKFLOW_SECURITY.md new file mode 100644 index 000000000000..6f50f2498c98 --- /dev/null +++ b/.github/WORKFLOW_SECURITY.md @@ -0,0 +1,206 @@ +# GitHub Actions Workflow Security Guide + +## Overview + +This document describes the security practices implemented in the GitHub Actions workflows for this repository to protect private data and sensitive information. + +## Security Measures Implemented + +### 1. API Key Protection + +**Location**: `.github/workflows/etherscan-apiv2.yml` + +**措施**: +- ✅ API keys are passed via HTTP headers (`X-Api-Key`) instead of URL parameters +- ✅ Secrets are masked using `::add-mask::` command at the start of jobs +- ✅ Shell debugging (`set -x`) is explicitly disabled to prevent command echoing +- ✅ All API requests use HTTPS +- ✅ No API keys are stored in environment variables that could be logged + +**Why**: URL parameters can be logged in various places (proxy logs, server logs, browser history), while HTTP headers are more secure. + +### 2. GitHub Actions Runtime Token Protection + +**Location**: `.github/actions/configure-docker/action.yml` + +**Measures**: +- ✅ `ACTIONS_RUNTIME_TOKEN` is explicitly masked using `core.setSecret()` +- ✅ Token is only exported for Docker buildkit cache operations +- ✅ Token is scoped to workflow run and expires automatically + +**Why**: The runtime token is sensitive and should not appear in logs, even though it's automatically scoped and expires. + +### 3. Git Credential Management + +**Location**: `.github/workflows/self-hosted-runner-setup.yml` + +**Measures**: +- ✅ Git credentials are cleared after each workflow run +- ✅ Credential helpers are explicitly unset +- ✅ No persistent credential storage + +**Why**: Self-hosted runners may persist data between runs, so credentials must be explicitly cleared. + +### 4. Secret Scanning + +**Locations**: +- `.github/workflows/bitcoin-ownership-announcement.yml` (lines 248-294) +- `.github/workflows/wiki-management.yml` (lines 47-83) +- `.github/workflows/deploy-website.yml` (lines 81-105) + +**Measures**: +- ✅ Automated scanning for common secret patterns +- ✅ Pattern matching for private keys, passwords, API keys +- ✅ Validation fails if potential secrets are detected +- ✅ Third-party security scanning tools (TruffleHog, Trivy) + +**Why**: Multiple layers of defense prevent accidental secret commits. + +### 5. Environment Variable Sanitization + +**Location**: `.github/workflows/self-hosted-runner-setup.yml` (line 139) + +**Measures**: +- ✅ Only safe environment variables are displayed +- ✅ Variables containing tokens, secrets, passwords are filtered out +- ✅ Explicit allow-list approach for environment display + +**Why**: Environment variables may contain sensitive data and should not be dumped to logs. + +### 6. Checkout Security + +**Locations**: Multiple workflows + +**Measures**: +- ✅ `persist-credentials: false` used in sensitive operations +- ✅ Clean checkouts to prevent cross-contamination +- ✅ Limited token scopes where possible + +**Why**: Persisting credentials can lead to accidental exposure in subsequent steps. + +### 7. Workspace Cleanup + +**Location**: `.github/workflows/self-hosted-runner-setup.yml` (lines 142-152, 191-198) + +**Measures**: +- ✅ Temporary files are removed after each run +- ✅ Private keys (`.pem`, `.key`) are explicitly deleted +- ✅ Environment files (`.env*`) are removed +- ✅ Git credential files are cleared + +**Why**: Self-hosted runners persist data, so cleanup is essential to prevent data leakage. + +## Secret Types Protected + +### Never Logged or Exposed: +1. **API Keys** (Etherscan, etc.) +2. **GitHub Tokens** (GITHUB_TOKEN, ACTIONS_RUNTIME_TOKEN) +3. **Private Keys** (SSH, GPG, cryptocurrency) +4. **Passwords and Credentials** +5. **Seed Phrases and Mnemonics** +6. **Personal Access Tokens** + +### Public Data (Safe to Log): +1. **ENS Names** (e.g., kushmanmb.eth) +2. **Public Blockchain Addresses** (after resolution) +3. **Repository Information** (commit SHAs, branch names) +4. **Workflow Run IDs** +5. **Public Configuration Values** + +## Best Practices for Contributors + +### When Adding New Workflows: + +1. **Never** put secrets in URL parameters + ```yaml + # ❌ BAD + curl "https://api.example.com/data?apikey=${{ secrets.API_KEY }}" + + # ✅ GOOD + curl -H "Authorization: Bearer ${{ secrets.API_KEY }}" "https://api.example.com/data" + ``` + +2. **Always** mask secrets at the start of jobs + ```yaml + - name: Security - Mask secrets + run: | + set +x # Disable shell debugging + echo "::add-mask::${{ secrets.MY_SECRET }}" + ``` + +3. **Never** echo or log secrets + ```yaml + # ❌ BAD + - run: echo "API Key is ${{ secrets.API_KEY }}" + + # ✅ GOOD + - run: echo "API Key configured successfully" + ``` + +4. **Use** header-based authentication + ```yaml + # ✅ GOOD + curl -H "X-Api-Key: ${{ secrets.API_KEY }}" "https://api.example.com/data" + ``` + +5. **Disable** shell debugging in sensitive operations + ```bash + set +x # Disable debugging + # sensitive operations here + ``` + +6. **Clear** credentials after use (especially on self-hosted runners) + ```bash + git config --unset-all credential.helper + rm -rf .git/credentials + ``` + +7. **Use** `persist-credentials: false` when checking out code + ```yaml + - uses: actions/checkout@v6 + with: + persist-credentials: false + ``` + +### When Reviewing Pull Requests: + +1. Check for hardcoded secrets or credentials +2. Verify API keys are passed via headers, not URLs +3. Ensure shell debugging is disabled for sensitive operations +4. Confirm credentials are cleared after use +5. Look for accidental secret logging (`echo`, `print`, etc.) + +## Security Checklist for New Workflows + +- [ ] Secrets are passed via HTTP headers, not URL parameters +- [ ] Secrets are masked using `::add-mask::` at job start +- [ ] Shell debugging is disabled (`set +x`) +- [ ] No secrets are echoed or logged +- [ ] Git credentials are cleared after use (if applicable) +- [ ] `persist-credentials: false` is used for checkout (if applicable) +- [ ] Workspace is cleaned up after job completion (for self-hosted) +- [ ] Security scanning is included (if handling user content) +- [ ] Timeouts are set to prevent runaway jobs + +## Reporting Security Issues + +If you discover a security vulnerability in our workflows: + +1. **Do NOT** open a public issue +2. Follow the process in [SECURITY.md](../SECURITY.md) +3. Include details about the vulnerability +4. Suggest a fix if possible + +## References + +- [GitHub Actions Security Best Practices](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) +- [Using Secrets in GitHub Actions](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) +- [Security Hardening for Self-Hosted Runners](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#hardening-for-self-hosted-runners) + +## Last Updated + +This document was last updated: 2026-02-15 + +--- + +**Note**: This is a living document. As new security measures are implemented or best practices evolve, this document will be updated accordingly. From e84d492231a0e260f345aee9e442afdb7580360f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 22:04:50 +0000 Subject: [PATCH 5/6] Fix: Use subshells with disabled echoing for Etherscan API key protection Co-authored-by: Kushmanmb <193178375+Kushmanmb@users.noreply.github.com> --- .github/WORKFLOW_SECURITY.md | 44 ++++++++++++++++++--------- .github/workflows/etherscan-apiv2.yml | 37 +++++++++++++++------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/.github/WORKFLOW_SECURITY.md b/.github/WORKFLOW_SECURITY.md index 6f50f2498c98..bf84eb0f1bef 100644 --- a/.github/WORKFLOW_SECURITY.md +++ b/.github/WORKFLOW_SECURITY.md @@ -10,14 +10,18 @@ This document describes the security practices implemented in the GitHub Actions **Location**: `.github/workflows/etherscan-apiv2.yml` -**措施**: -- ✅ API keys are passed via HTTP headers (`X-Api-Key`) instead of URL parameters +**Measures**: +- ✅ API keys are constructed in subshells with command echoing disabled (`set +x`) - ✅ Secrets are masked using `::add-mask::` command at the start of jobs -- ✅ Shell debugging (`set -x`) is explicitly disabled to prevent command echoing +- ✅ Shell debugging is explicitly disabled to prevent command echoing - ✅ All API requests use HTTPS -- ✅ No API keys are stored in environment variables that could be logged +- ✅ Temporary files are cleaned up immediately after use +- ✅ URL construction happens in isolated subshells to minimize exposure risk -**Why**: URL parameters can be logged in various places (proxy logs, server logs, browser history), while HTTP headers are more secure. +**Why**: While Etherscan requires API keys as URL parameters (not headers), we can still protect them by: +1. Constructing URLs in subshells with `set +x` to disable command echoing +2. Masking secrets at the job level using `::add-mask::` +3. Avoiding storage of URLs containing secrets in environment variables that could be logged ### 2. GitHub Actions Runtime Token Protection @@ -111,13 +115,20 @@ This document describes the security practices implemented in the GitHub Actions ### When Adding New Workflows: -1. **Never** put secrets in URL parameters +1. **Prefer** header-based authentication when the API supports it ```yaml - # ❌ BAD - curl "https://api.example.com/data?apikey=${{ secrets.API_KEY }}" - - # ✅ GOOD + # ✅ BEST (if API supports it) curl -H "Authorization: Bearer ${{ secrets.API_KEY }}" "https://api.example.com/data" + + # ✅ ACCEPTABLE (if API requires URL parameters) + # Use subshell with disabled echoing + ( + set +x 2>/dev/null + curl "https://api.example.com/data?apikey=${{ secrets.API_KEY }}" -o output.json + ) + + # ❌ BAD - Direct construction that could be logged + curl "https://api.example.com/data?apikey=${{ secrets.API_KEY }}" ``` 2. **Always** mask secrets at the start of jobs @@ -137,10 +148,13 @@ This document describes the security practices implemented in the GitHub Actions - run: echo "API Key configured successfully" ``` -4. **Use** header-based authentication - ```yaml - # ✅ GOOD - curl -H "X-Api-Key: ${{ secrets.API_KEY }}" "https://api.example.com/data" +4. **Use** subshells for API calls that require secrets in URLs + ```bash + # ✅ GOOD - Subshell with disabled echoing + ( + set +x 2>/dev/null # Disable debugging + curl "https://api.example.com/data?apikey=${{ secrets.API_KEY }}" -o output.json + ) ``` 5. **Disable** shell debugging in sensitive operations @@ -149,7 +163,7 @@ This document describes the security practices implemented in the GitHub Actions # sensitive operations here ``` -6. **Clear** credentials after use (especially on self-hosted runners) +6. **Clear** credentials and temporary files after use ```bash git config --unset-all credential.helper rm -rf .git/credentials diff --git a/.github/workflows/etherscan-apiv2.yml b/.github/workflows/etherscan-apiv2.yml index 0017c6cfbd34..690939f3658c 100644 --- a/.github/workflows/etherscan-apiv2.yml +++ b/.github/workflows/etherscan-apiv2.yml @@ -18,11 +18,12 @@ # # SECURITY NOTICE: # This workflow handles sensitive API keys and follows security best practices: -# - API keys are passed via HTTP headers, not URL parameters -# - Shell debugging (set -x) is explicitly disabled +# - API keys are constructed in subshells with command echoing disabled (set +x) # - Secrets are masked using GitHub Actions ::add-mask:: command +# - Shell debugging (set -x) is explicitly disabled at job start # - All API requests use HTTPS # - No sensitive data is logged or exposed in workflow outputs +# - Temporary files with API responses are cleaned up immediately # ═══════════════════════════════════════════════════════════════════ name: Etherscan API Integration (kushmanmb.eth) @@ -123,12 +124,21 @@ jobs: # Use Etherscan API to resolve ENS name echo "Attempting ENS resolution via Etherscan API V2..." - # Security: Use header-based authentication to prevent API key exposure in URLs - # Build URL without API key - ENS_LOOKUP_URL="https://api.etherscan.io/v2/api?chainid=1&module=ens&action=getaddress&name=${ENS_NAME}" + # Security: Build the URL carefully to prevent API key exposure + # Etherscan requires apikey as URL parameter, so we must prevent logging + # Use set +x and build URL in a subshell to minimize exposure risk + ( + set +x 2>/dev/null # Disable command echoing + ENS_LOOKUP_URL="https://api.etherscan.io/v2/api?chainid=1&module=ens&action=getaddress&name=${ENS_NAME}&apikey=${{ secrets.ETHERSCAN_API_KEY }}" + + # Fetch with error handling - URL construction happens in subshell + curl -s -f "${ENS_LOOKUP_URL}" -o /tmp/ens_response.json + ) - # Fetch with error handling - API key passed as header, not in URL - if ! API_RESPONSE=$(curl -s -f -H "X-Api-Key: ${{ secrets.ETHERSCAN_API_KEY }}" "${ENS_LOOKUP_URL}"); then + if [ ! -f /tmp/ens_response.json ]; then + ) + + if [ ! -f /tmp/ens_response.json ]; then echo "⚠️ Failed to connect to Etherscan API" echo "ENS_NAME=${ENS_NAME}" >> $GITHUB_ENV echo "ENS_RESOLVED=false" >> $GITHUB_ENV @@ -137,6 +147,8 @@ jobs: fi # Extract address with error handling + API_RESPONSE=$(cat /tmp/ens_response.json) + rm -f /tmp/ens_response.json RESOLVED_ADDRESS=$(echo "$API_RESPONSE" | jq -r '.result // empty' 2>/dev/null || echo "") # Check if we got a valid address (simplified condition) @@ -249,10 +261,13 @@ jobs: # Build API URL - All endpoints now use V2 API api_url="${BASE_URL}?chainid=1&module=${MODULE}&action=${ACTION}${PARAMS}" - # Security: Make request with API key in header to prevent exposure in logs - # The API key is passed as a header, not in the URL, to avoid logging - curl -s -H "X-Api-Key: ${{ secrets.ETHERSCAN_API_KEY }}" "${api_url}" \ - -o data/etherscan/latest.json || { + # Security: Make request with API key as URL parameter (Etherscan requirement) + # Use subshell with disabled command echoing to prevent API key logging + ( + set +x 2>/dev/null # Disable command echoing + full_url="${api_url}&apikey=${{ secrets.ETHERSCAN_API_KEY }}" + curl -s "${full_url}" -o data/etherscan/latest.json + ) || { echo '{"status":"0","message":"API request failed"}' \ > data/etherscan/latest.json } From 128f1e49acd9699980a8d9ca149db4c311f030af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 22:05:36 +0000 Subject: [PATCH 6/6] Fix syntax error: Remove duplicate lines in etherscan workflow --- .github/workflows/etherscan-apiv2.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/etherscan-apiv2.yml b/.github/workflows/etherscan-apiv2.yml index 690939f3658c..34db9369ede6 100644 --- a/.github/workflows/etherscan-apiv2.yml +++ b/.github/workflows/etherscan-apiv2.yml @@ -135,9 +135,6 @@ jobs: curl -s -f "${ENS_LOOKUP_URL}" -o /tmp/ens_response.json ) - if [ ! -f /tmp/ens_response.json ]; then - ) - if [ ! -f /tmp/ens_response.json ]; then echo "⚠️ Failed to connect to Etherscan API" echo "ENS_NAME=${ENS_NAME}" >> $GITHUB_ENV