diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index f9e846e68..36e82a67b 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -3,8 +3,13 @@ name: Docker Build and Publish on: push: branches: [main] - pull_request: - branches: [main] + paths: + - '**/*.py' + - 'pyproject.toml' + - 'uv.lock' + - 'Dockerfile' + - 'docker/**' + - '.github/workflows/docker-publish.yml' release: types: [published] diff --git a/.github/workflows/supply-chain-audit.yml b/.github/workflows/supply-chain-audit.yml deleted file mode 100644 index 4aa0fd321..000000000 --- a/.github/workflows/supply-chain-audit.yml +++ /dev/null @@ -1,248 +0,0 @@ -name: Supply Chain Audit - -on: - pull_request: - types: [opened, synchronize, reopened] - -permissions: - pull-requests: write - contents: read - -jobs: - scan: - name: Scan PR for supply chain risks - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - fetch-depth: 0 - - - name: Scan diff for suspicious patterns - id: scan - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -euo pipefail - - BASE="${{ github.event.pull_request.base.sha }}" - HEAD="${{ github.event.pull_request.head.sha }}" - - # Get the full diff (added lines only) - DIFF=$(git diff "$BASE".."$HEAD" -- . ':!uv.lock' ':!*.lock' ':!package-lock.json' ':!yarn.lock' || true) - - FINDINGS="" - CRITICAL=false - - # --- .pth files (auto-execute on Python startup) --- - PTH_FILES=$(git diff --name-only "$BASE".."$HEAD" | grep '\.pth$' || true) - if [ -n "$PTH_FILES" ]; then - CRITICAL=true - FINDINGS="${FINDINGS} - ### 🚨 CRITICAL: .pth file added or modified - Python \`.pth\` files in \`site-packages/\` execute automatically when the interpreter starts — no import required. This is the exact mechanism used in the [litellm supply chain attack](https://github.com/BerriAI/litellm/issues/24512). - - **Files:** - \`\`\` - ${PTH_FILES} - \`\`\` - " - fi - - # --- base64 + exec/eval combo (the litellm attack pattern) --- - B64_EXEC_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -iE 'base64\.(b64decode|decodebytes|urlsafe_b64decode)' | grep -iE 'exec\(|eval\(' | head -10 || true) - if [ -n "$B64_EXEC_HITS" ]; then - CRITICAL=true - FINDINGS="${FINDINGS} - ### 🚨 CRITICAL: base64 decode + exec/eval combo - This is the exact pattern used in the [litellm supply chain attack](https://github.com/BerriAI/litellm/issues/24512) — base64-decoded strings passed to exec/eval to hide credential-stealing payloads. - - **Matches:** - \`\`\` - ${B64_EXEC_HITS} - \`\`\` - " - fi - - # --- base64 decode/encode (alone — legitimate uses exist) --- - B64_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -iE 'base64\.(b64decode|b64encode|decodebytes|encodebytes|urlsafe_b64decode)|atob\(|btoa\(|Buffer\.from\(.*base64' | head -20 || true) - if [ -n "$B64_HITS" ]; then - FINDINGS="${FINDINGS} - ### ⚠️ WARNING: base64 encoding/decoding detected - Base64 has legitimate uses (images, JWT, etc.) but is also commonly used to obfuscate malicious payloads. Verify the usage is appropriate. - - **Matches (first 20):** - \`\`\` - ${B64_HITS} - \`\`\` - " - fi - - # --- exec/eval with string arguments --- - EXEC_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -E '(exec|eval)\s*\(' | grep -v '^\+\s*#' | grep -v 'test_\|mock\|assert\|# ' | head -20 || true) - if [ -n "$EXEC_HITS" ]; then - FINDINGS="${FINDINGS} - ### ⚠️ WARNING: exec() or eval() usage - Dynamic code execution can hide malicious behavior, especially when combined with base64 or network fetches. - - **Matches (first 20):** - \`\`\` - ${EXEC_HITS} - \`\`\` - " - fi - - # --- subprocess with encoded/obfuscated commands --- - PROC_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -E 'subprocess\.(Popen|call|run)\s*\(' | grep -iE 'base64|decode|encode|\\x|chr\(' | head -10 || true) - if [ -n "$PROC_HITS" ]; then - CRITICAL=true - FINDINGS="${FINDINGS} - ### 🚨 CRITICAL: subprocess with encoded/obfuscated command - Subprocess calls with encoded arguments are a strong indicator of payload execution. - - **Matches:** - \`\`\` - ${PROC_HITS} - \`\`\` - " - fi - - # --- Network calls to non-standard domains --- - EXFIL_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -iE 'requests\.(post|put)\(|httpx\.(post|put)\(|urllib\.request\.urlopen' | grep -v '^\+\s*#' | grep -v 'test_\|mock\|assert' | head -10 || true) - if [ -n "$EXFIL_HITS" ]; then - FINDINGS="${FINDINGS} - ### ⚠️ WARNING: Outbound network calls (POST/PUT) - Outbound POST/PUT requests in new code could be data exfiltration. Verify the destination URLs are legitimate. - - **Matches (first 10):** - \`\`\` - ${EXFIL_HITS} - \`\`\` - " - fi - - # --- setup.py / setup.cfg install hooks --- - SETUP_HITS=$(git diff --name-only "$BASE".."$HEAD" | grep -E '(setup\.py|setup\.cfg|__init__\.pth|sitecustomize\.py|usercustomize\.py)$' || true) - if [ -n "$SETUP_HITS" ]; then - FINDINGS="${FINDINGS} - ### ⚠️ WARNING: Install hook files modified - These files can execute code during package installation or interpreter startup. - - **Files:** - \`\`\` - ${SETUP_HITS} - \`\`\` - " - fi - - # --- Compile/marshal/pickle (code object injection) --- - MARSHAL_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -iE 'marshal\.loads|pickle\.loads|compile\(' | grep -v '^\+\s*#' | grep -v 'test_\|re\.compile\|ast\.compile' | head -10 || true) - if [ -n "$MARSHAL_HITS" ]; then - FINDINGS="${FINDINGS} - ### ⚠️ WARNING: marshal/pickle/compile usage - These can deserialize or construct executable code objects. - - **Matches:** - \`\`\` - ${MARSHAL_HITS} - \`\`\` - " - fi - - # --- CI/CD workflow files modified --- - WORKFLOW_HITS=$(git diff --name-only "$BASE".."$HEAD" | grep -E '\.github/workflows/.*\.ya?ml$' || true) - if [ -n "$WORKFLOW_HITS" ]; then - FINDINGS="${FINDINGS} - ### ⚠️ WARNING: CI/CD workflow files modified - Changes to workflow files can alter build pipelines, inject steps, or modify permissions. Verify no unauthorized actions or secrets access were added. - - **Files:** - \`\`\` - ${WORKFLOW_HITS} - \`\`\` - " - fi - - # --- Dockerfile / container build files modified --- - DOCKER_HITS=$(git diff --name-only "$BASE".."$HEAD" | grep -iE '(Dockerfile|\.dockerignore|docker-compose)' || true) - if [ -n "$DOCKER_HITS" ]; then - FINDINGS="${FINDINGS} - ### ⚠️ WARNING: Container build files modified - Changes to Dockerfiles or compose files can alter base images, add build steps, or expose ports. Verify base image pins and build commands. - - **Files:** - \`\`\` - ${DOCKER_HITS} - \`\`\` - " - fi - - # --- Dependency manifest files modified --- - DEP_HITS=$(git diff --name-only "$BASE".."$HEAD" | grep -E '(pyproject\.toml|requirements.*\.txt|package\.json|Gemfile|go\.mod|Cargo\.toml)$' || true) - if [ -n "$DEP_HITS" ]; then - FINDINGS="${FINDINGS} - ### ⚠️ WARNING: Dependency manifest files modified - Changes to dependency files can introduce new packages or change version pins. Verify all dependency changes are intentional and from trusted sources. - - **Files:** - \`\`\` - ${DEP_HITS} - \`\`\` - " - fi - - # --- GitHub Actions version unpinning (mutable tags instead of SHAs) --- - ACTIONS_UNPIN=$(echo "$DIFF" | grep -n '^\+' | grep 'uses:' | grep -v '#' | grep -E '@v[0-9]' | head -10 || true) - if [ -n "$ACTIONS_UNPIN" ]; then - FINDINGS="${FINDINGS} - ### ⚠️ WARNING: GitHub Actions with mutable version tags - Actions should be pinned to full commit SHAs (not \`@v4\`, \`@v5\`). Mutable tags can be retargeted silently if a maintainer account is compromised. - - **Matches:** - \`\`\` - ${ACTIONS_UNPIN} - \`\`\` - " - fi - - # --- Output results --- - if [ -n "$FINDINGS" ]; then - echo "found=true" >> "$GITHUB_OUTPUT" - if [ "$CRITICAL" = true ]; then - echo "critical=true" >> "$GITHUB_OUTPUT" - else - echo "critical=false" >> "$GITHUB_OUTPUT" - fi - # Write findings to a file (multiline env vars are fragile) - echo "$FINDINGS" > /tmp/findings.md - else - echo "found=false" >> "$GITHUB_OUTPUT" - echo "critical=false" >> "$GITHUB_OUTPUT" - fi - - - name: Post warning comment - if: steps.scan.outputs.found == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - SEVERITY="⚠️ Supply Chain Risk Detected" - if [ "${{ steps.scan.outputs.critical }}" = "true" ]; then - SEVERITY="🚨 CRITICAL Supply Chain Risk Detected" - fi - - BODY="## ${SEVERITY} - - This PR contains patterns commonly associated with supply chain attacks. This does **not** mean the PR is malicious — but these patterns require careful human review before merging. - - $(cat /tmp/findings.md) - - --- - *Automated scan triggered by [supply-chain-audit](/.github/workflows/supply-chain-audit.yml). If this is a false positive, a maintainer can approve after manual review.*" - - gh pr comment "${{ github.event.pull_request.number }}" --body "$BODY" || echo "::warning::Could not post PR comment (expected for fork PRs — GITHUB_TOKEN is read-only)" - - - name: Fail on critical findings - if: steps.scan.outputs.critical == 'true' - run: | - echo "::error::CRITICAL supply chain risk patterns detected in this PR. See the PR comment for details." - exit 1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7d0822690..02248d250 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,8 +3,14 @@ name: Tests on: push: branches: [main] + paths-ignore: + - '**/*.md' + - 'docs/**' pull_request: branches: [main] + paths-ignore: + - '**/*.md' + - 'docs/**' permissions: contents: read