feat(desktop): support opening sessions in multiple windows #47644
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Supply Chain Audit | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| # No paths filter — the jobs must always run so required checks | |
| # report a status (path-gated workflows leave checks "pending" forever | |
| # when no matching files change, which blocks merge). | |
| permissions: | |
| pull-requests: write | |
| contents: read | |
| # Narrow, high-signal scanner. Only fires on critical indicators of supply | |
| # chain attacks (e.g. the litellm-style payloads). Low-signal heuristics | |
| # (plain base64, plain exec/eval, dependency/Dockerfile/workflow edits, | |
| # Actions version unpinning, outbound POST/PUT) were intentionally | |
| # removed — they fired on nearly every PR and trained reviewers to ignore | |
| # the scanner. Keep this file's checks ruthlessly narrow: if you find | |
| # yourself adding WARNING-tier patterns here again, make a separate | |
| # advisory-only workflow instead. | |
| jobs: | |
| # ── Path filter (shared by both scan and dep-bounds) ─────────────── | |
| changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| # True when any file the scanner cares about changed in this PR | |
| scan: ${{ steps.filter.outputs.scan }} | |
| # True when pyproject.toml changed in this PR | |
| deps: ${{ steps.filter.outputs.deps }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for relevant file changes | |
| id: filter | |
| run: | | |
| BASE="${{ github.event.pull_request.base.sha }}" | |
| HEAD="${{ github.event.pull_request.head.sha }}" | |
| SCAN_FILES=$(git diff --name-only "$BASE"..."$HEAD" -- \ | |
| '*.py' '**/*.py' '*.pth' '**/*.pth' \ | |
| 'setup.py' 'setup.cfg' \ | |
| 'sitecustomize.py' 'usercustomize.py' '__init__.pth' \ | |
| 'pyproject.toml' || true) | |
| if [ -n "$SCAN_FILES" ]; then | |
| echo "scan=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "scan=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| DEPS_FILES=$(git diff --name-only "$BASE"..."$HEAD" -- 'pyproject.toml' || true) | |
| if [ -n "$DEPS_FILES" ]; then | |
| echo "deps=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "deps=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| scan: | |
| name: Scan PR for critical supply chain risks | |
| needs: changes | |
| if: needs.changes.outputs.scan == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Scan diff for critical 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 }}" | |
| # Added lines only, excluding lockfiles. | |
| # Three-dot diff (base...head) diffs from the merge base to HEAD, | |
| # so only changes introduced by this PR are included — not changes | |
| # that landed on main after the PR branched off. | |
| DIFF=$(git diff "$BASE"..."$HEAD" -- . ':!uv.lock' ':!*.lock' ':!package-lock.json' ':!yarn.lock' || true) | |
| FINDINGS="" | |
| # --- .pth files (auto-execute on Python startup) --- | |
| # The exact mechanism used in the litellm supply chain attack: | |
| # https://github.com/BerriAI/litellm/issues/24512 | |
| PTH_FILES=$(git diff --name-only "$BASE"..."$HEAD" | grep '\.pth$' || true) | |
| if [ -n "$PTH_FILES" ]; then | |
| FINDINGS="${FINDINGS} | |
| ### 🚨 CRITICAL: .pth file added or modified | |
| Python \`.pth\` files in \`site-packages/\` execute automatically when the interpreter starts — no import required. | |
| **Files:** | |
| \`\`\` | |
| ${PTH_FILES} | |
| \`\`\` | |
| " | |
| fi | |
| # --- base64 decode + exec/eval on the same line (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 | |
| FINDINGS="${FINDINGS} | |
| ### 🚨 CRITICAL: base64 decode + exec/eval combo | |
| Base64-decoded strings passed directly to exec/eval — the signature of hidden credential-stealing payloads. | |
| **Matches:** | |
| \`\`\` | |
| ${B64_EXEC_HITS} | |
| \`\`\` | |
| " | |
| fi | |
| # --- subprocess with encoded/obfuscated command argument --- | |
| PROC_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -E 'subprocess\.(Popen|call|run)\s*\(' | grep -iE 'base64|\\x[0-9a-f]{2}|chr\(' | head -10 || true) | |
| if [ -n "$PROC_HITS" ]; then | |
| FINDINGS="${FINDINGS} | |
| ### 🚨 CRITICAL: subprocess with encoded/obfuscated command | |
| Subprocess calls whose command strings are base64- or hex-encoded are a strong indicator of payload execution. | |
| **Matches:** | |
| \`\`\` | |
| ${PROC_HITS} | |
| \`\`\` | |
| " | |
| fi | |
| # --- Install-hook files (setup.py/sitecustomize/usercustomize/__init__.pth) --- | |
| # These execute during pip install or interpreter startup. | |
| # Anchored at repo root: only the top-level setup.py/setup.cfg run during | |
| # `pip install`, and only top-level sitecustomize.py/usercustomize.py are | |
| # auto-loaded by the interpreter via site.py. Any nested file with the | |
| # same name (e.g. hermes_cli/setup.py — the CLI setup wizard) is unrelated | |
| # and produced false positives that trained reviewers to ignore the scanner. | |
| SETUP_HITS=$(git diff --name-only "$BASE"..."$HEAD" | grep -E '^(setup\.py|setup\.cfg|sitecustomize\.py|usercustomize\.py|__init__\.pth)$' || true) | |
| if [ -n "$SETUP_HITS" ]; then | |
| FINDINGS="${FINDINGS} | |
| ### 🚨 CRITICAL: Install-hook file added or modified | |
| These files can execute code during package installation or interpreter startup. | |
| **Files:** | |
| \`\`\` | |
| ${SETUP_HITS} | |
| \`\`\` | |
| " | |
| fi | |
| if [ -n "$FINDINGS" ]; then | |
| echo "found=true" >> "$GITHUB_OUTPUT" | |
| echo "$FINDINGS" > /tmp/findings.md | |
| else | |
| echo "found=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Post critical finding comment | |
| if: steps.scan.outputs.found == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| BODY="## 🚨 CRITICAL Supply Chain Risk Detected | |
| This PR contains a pattern that has been used in real supply chain attacks. A maintainer must review the flagged code carefully before merging. | |
| $(cat /tmp/findings.md) | |
| --- | |
| *Scanner only fires on high-signal indicators: .pth files, base64+exec/eval combos, subprocess with encoded commands, or install-hook files. Low-signal warnings were removed intentionally — if you're seeing this comment, the finding is worth inspecting.*" | |
| 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.found == 'true' | |
| run: | | |
| echo "::error::CRITICAL supply chain risk patterns detected in this PR. See the PR comment for details." | |
| exit 1 | |
| # Gate: reports success when scan was skipped (no relevant files changed). | |
| # This ensures the required check always gets a status. | |
| scan-gate: | |
| name: Scan PR for critical supply chain risks | |
| needs: changes | |
| # always() so the gate still reports SUCCESS even if `changes` fails/is | |
| # skipped — without it, a failed dependency would leave the required | |
| # check unreported (i.e. "pending"), the exact failure mode this fixes. | |
| if: always() && needs.changes.outputs.scan != 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: echo "No supply-chain-relevant files changed, skipping scan." | |
| dep-bounds: | |
| name: Check PyPI dependency upper bounds | |
| needs: changes | |
| if: needs.changes.outputs.deps == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for unbounded PyPI deps | |
| id: bounds | |
| run: | | |
| set -euo pipefail | |
| BASE="${{ github.event.pull_request.base.sha }}" | |
| HEAD="${{ github.event.pull_request.head.sha }}" | |
| # Only check added lines in pyproject.toml | |
| ADDED=$(git diff "$BASE"..."$HEAD" -- pyproject.toml | grep '^+' | grep -v '^+++' || true) | |
| if [ -z "$ADDED" ]; then | |
| echo "found=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Match PyPI dep specs that have >= but no < ceiling. | |
| # Pattern: "package>=version" without a following ",<" bound. | |
| # Excludes git+ URLs (which use commit SHAs) and comments. | |
| UNBOUNDED=$(echo "$ADDED" | grep -oE '"[a-zA-Z0-9_-]+(\[[^\]]*\])?>=[ 0-9.]+"' | grep -v ',<' || true) | |
| if [ -n "$UNBOUNDED" ]; then | |
| echo "found=true" >> "$GITHUB_OUTPUT" | |
| echo "$UNBOUNDED" > /tmp/unbounded.txt | |
| else | |
| echo "found=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Post unbounded dep warning | |
| if: steps.bounds.outputs.found == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| BODY="## ⚠️ Unbounded PyPI Dependency Detected | |
| This PR adds PyPI dependencies without a \`<next_major\` upper bound. Per our [supply chain policy](../blob/main/CONTRIBUTING.md#dependency-pinning-policy-supply-chain-hardening), all PyPI deps must be pinned as \`>=floor,<next_major\`. | |
| **Unbounded specs found:** | |
| \`\`\` | |
| $(cat /tmp/unbounded.txt) | |
| \`\`\` | |
| **Fix:** Add an upper bound, e.g. \`\"package>=1.2.0,<2\"\` | |
| --- | |
| *See PR #2810 and CONTRIBUTING.md for the full policy rationale.*" | |
| gh pr comment "${{ github.event.pull_request.number }}" --body "$BODY" || echo "::warning::Could not post PR comment (expected for fork PRs)" | |
| - name: Fail on unbounded deps | |
| if: steps.bounds.outputs.found == 'true' | |
| run: | | |
| echo "::error::PyPI dependencies without upper bounds detected. Add <next_major ceiling per CONTRIBUTING.md policy." | |
| exit 1 | |
| # Gate: reports success when dep-bounds was skipped (no pyproject.toml changed). | |
| # This ensures the required check always gets a status. | |
| dep-bounds-gate: | |
| name: Check PyPI dependency upper bounds | |
| needs: changes | |
| # always() so the gate still reports SUCCESS even if `changes` fails/is | |
| # skipped — without it, a failed dependency would leave the required | |
| # check unreported (i.e. "pending"), the exact failure mode this fixes. | |
| if: always() && needs.changes.outputs.deps != 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: echo "No pyproject.toml changes, skipping dependency bounds check." |