From 85859f3d5b3d6c7d09e9d456578a51094d928c59 Mon Sep 17 00:00:00 2001 From: fvegiard Date: Sun, 21 Jun 2026 00:25:59 -0400 Subject: [PATCH 01/21] fix(ci): make gh auth status non-fatal + disable telemetry in sandboxed runners --- .github/workflows/sisyphus-agent.yml | 1135 +++++++++++++------------- 1 file changed, 570 insertions(+), 565 deletions(-) diff --git a/.github/workflows/sisyphus-agent.yml b/.github/workflows/sisyphus-agent.yml index 3ca82f99151..95b4611f649 100644 --- a/.github/workflows/sisyphus-agent.yml +++ b/.github/workflows/sisyphus-agent.yml @@ -1,565 +1,570 @@ -name: Sisyphus Agent - -on: - workflow_dispatch: - inputs: - prompt: - description: "Custom prompt" - required: false - # Only issue_comment works for fork PRs (secrets available) - # pull_request_review/pull_request_review_comment do NOT get secrets for fork PRs - issue_comment: - types: [created] - -jobs: - agent: - runs-on: ubuntu-latest - # @sisyphus-dev-ai mention only (maintainers, exclude self) - if: >- - github.event_name == 'workflow_dispatch' || - (github.event_name == 'issue_comment' && - contains(github.event.comment.body || '', '@sisyphus-dev-ai') && - (github.event.comment.user.login || '') != 'sisyphus-dev-ai' && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || '')) - - permissions: - contents: read - - steps: - # Checkout with sisyphus-dev-ai's PAT - - uses: actions/checkout@v5 - with: - token: ${{ secrets.GH_PAT }} - fetch-depth: 0 - - # Git config - commits as sisyphus-dev-ai - - name: Configure Git as sisyphus-dev-ai - run: | - git config user.name "sisyphus-dev-ai" - git config user.email "sisyphus-dev-ai@users.noreply.github.com" - - # gh CLI auth as sisyphus-dev-ai - - name: Authenticate gh CLI as sisyphus-dev-ai - env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - run: | - echo "$GITHUB_TOKEN" | gh auth login --with-token - gh auth status - - - name: Ensure tmux is available (Linux) - if: runner.os == 'Linux' - run: | - set -euo pipefail - if ! command -v tmux >/dev/null 2>&1; then - sudo apt-get update - sudo apt-get install -y --no-install-recommends tmux - fi - tmux -V - - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: "1.3.12" - - - name: Cache Bun dependencies - uses: actions/cache@v5 - with: - path: | - ~/.bun/install/cache - node_modules - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- - - # Build local oh-my-opencode - - name: Build oh-my-opencode - run: | - bun install --frozen-lockfile - bun run build - - # Install OpenCode + configure local plugin + auth in single step - - name: Setup OpenCode with oh-my-opencode - env: - OPENCODE_AUTH_JSON: ${{ secrets.OPENCODE_AUTH_JSON }} - ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - run: | - export PATH="$HOME/.opencode/bin:$PATH" - - # Install OpenCode (skip if cached) - if ! command -v opencode &>/dev/null; then - echo "Installing OpenCode..." - curl -fsSL https://opencode.ai/install -o /tmp/opencode-install.sh - - # Try default installer first, fallback to re-download if it fails - if file /tmp/opencode-install.sh | grep -q "shell script\|text"; then - if ! bash /tmp/opencode-install.sh 2>&1; then - echo "Default installer failed, trying direct install..." - bash <(curl -fsSL https://opencode.ai/install) - fi - else - echo "Download corrupted, trying direct install..." - bash <(curl -fsSL https://opencode.ai/install) - fi - fi - opencode --version - - # Run local oh-my-opencode install (uses built dist) - bun run dist/cli/index.js install --no-tui --claude=max20 --openai=no --gemini=no --copilot=no - - # Override plugin to use local file reference - OPENCODE_JSON=~/.config/opencode/opencode.json - REPO_PATH=$(pwd) - jq --arg path "file://$REPO_PATH/packages/omo-opencode/src/index.ts" ' - .plugin = [.plugin[] | select(. != "oh-my-opencode")] + [$path] - ' "$OPENCODE_JSON" > /tmp/oc.json && mv /tmp/oc.json "$OPENCODE_JSON" - - OPENCODE_JSON=~/.config/opencode/opencode.json - jq --arg baseURL "$ANTHROPIC_BASE_URL" --arg apiKey "$ANTHROPIC_API_KEY" ' - .model = "anthropic/claude-opus-4-5" | - .provider.anthropic = { - "name": "Anthropic", - "npm": "@ai-sdk/anthropic", - "options": { - "baseURL": $baseURL, - "apiKey": $apiKey - }, - "models": { - "claude-opus-4-5": { - "id": "claude-opus-4-5-20251101", - "name": "Opus 4.5", - "limit": { "context": 190000, "output": 64000 }, - "options": { "effort": "high" } - }, - "claude-opus-4-5-high": { - "id": "claude-opus-4-5-20251101", - "name": "Opus 4.5 High", - "limit": { "context": 190000, "output": 128000 }, - "options": { "effort": "high", "thinking": { "type": "enabled", "budgetTokens": 64000 } } - }, - "claude-sonnet-4-6": { - "id": "claude-sonnet-4-6-20250929", - "name": "Sonnet 4.6", - "limit": { "context": 200000, "output": 64000 } - }, - "claude-sonnet-4-6-high": { - "id": "claude-sonnet-4-6-20250929", - "name": "Sonnet 4.6 High", - "limit": { "context": 200000, "output": 128000 }, - "options": { "thinking": { "type": "enabled", "budgetTokens": 64000 } } - }, - "claude-haiku-4-5": { - "id": "claude-haiku-4-5-20251001", - "name": "Haiku 4.5", - "limit": { "context": 200000, "output": 64000 } - } - } - } | - .provider["zai-coding-plan"] = { - "name": "Z.AI Coding Plan", - "npm": "@ai-sdk/openai-compatible", - "options": { - "baseURL": "https://api.z.ai/api/paas/v4" - }, - "models": { - "glm-4.7": { - "id": "glm-4.7", - "name": "GLM 4.7", - "limit": { "context": 128000, "output": 16000 } - }, - "glm-4.6v": { - "id": "glm-4.6v", - "name": "GLM 4.6 Vision", - "limit": { "context": 128000, "output": 16000 } - } - } - } | - .provider.openai = { - "name": "OpenAI", - "npm": "@ai-sdk/openai", - "models": { - "gpt-5.5": { - "id": "gpt-5.5", - "name": "GPT-5.5", - "limit": { "context": 128000, "output": 16000 } - }, - "gpt-5.5-codex": { - "id": "gpt-5.5-codex", - "name": "GPT-5.5 Codex", - "limit": { "context": 128000, "output": 32000 } - } - } - } - ' "$OPENCODE_JSON" > /tmp/oc.json && mv /tmp/oc.json "$OPENCODE_JSON" - - OMO_JSON=~/.config/opencode/oh-my-opencode.json - PROMPT_APPEND=$(cat << 'PROMPT_EOF' - - [CODE RED] Maximum precision required. Ultrathink before acting. - - YOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL. - TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST. - - ## AGENT UTILIZATION PRINCIPLES (by capability, not by name) - - **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS for file patterns, internal implementations, project structure - - **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS for API references, examples, external library docs - - **Planning & Strategy**: For implementation tasks, spawn a dedicated planning agent for work breakdown (not needed for simple questions/investigations) - - **High-IQ Reasoning**: Leverage specialized agents for architecture decisions, code review, strategic planning - - **Frontend/UI Tasks**: Delegate to UI-specialized agents for design and implementation - - ## EXECUTION RULES - - **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each. - - **PARALLEL**: Fire independent agent calls simultaneously via background_task - NEVER wait sequentially. - - **BACKGROUND FIRST**: Use background_task for exploration/research agents (10+ concurrent if needed). - - **VERIFY**: Re-read request after completion. Check ALL requirements met before reporting done. - - **DELEGATE**: Don't do everything yourself - orchestrate specialized agents for their strengths. - - ## WORKFLOW - 1. Analyze the request and identify required capabilities - 2. Spawn exploration/librarian agents via background_task in PARALLEL (10+ if needed) - 3. Always Use Plan agent with gathered context to create detailed work breakdown - 4. Execute with continuous verification against original requirements - - ## TDD (if test infrastructure exists) - - 1. Write spec (requirements) - 2. Write tests (failing) - 3. RED: tests fail - 4. Implement minimal code - 5. GREEN: tests pass - 6. Refactor if needed (must stay green) - 7. Next feature, repeat - - ## ZERO TOLERANCE FAILURES - - **NO Scope Reduction**: Never make "demo", "skeleton", "simplified", "basic" versions - deliver FULL implementation - - **NO MockUp Work**: When user asked you to do "port A", you must "port A", fully, 100%. No Extra feature, No reduced feature, no mock data, fully working 100% port. - - **NO Partial Completion**: Never stop at 60-80% saying "you can extend this..." - finish 100% - - **NO Assumed Shortcuts**: Never skip requirements you deem "optional" or "can be added later" - - **NO Premature Stopping**: Never declare done until ALL TODOs are completed and verified - - **NO TEST DELETION**: Never delete or skip failing tests to make the build pass. Fix the code, not the tests. - - THE USER ASKED FOR X. DELIVER EXACTLY X. NOT A SUBSET. NOT A DEMO. NOT A STARTING POINT. - - - - --- - - - [analyze-mode] - ANALYSIS MODE. Gather context before diving deep: - - CONTEXT GATHERING (parallel): - - 1-2 explore agents (codebase patterns, implementations) - - 1-2 librarian agents (if external library involved) - - Direct tools: Grep, AST-grep, LSP for targeted searches - - IF COMPLEX (architecture, multi-system, debugging after 2+ failures): - - Consult oracle for strategic guidance - - SYNTHESIZE findings before proceeding. - - --- - - ## GitHub Actions Environment - - You are `sisyphus-dev-ai` in GitHub Actions. - - ### CRITICAL: GitHub Comments = Your ONLY Output - - User CANNOT see console. Post everything via `gh issue comment` or `gh pr comment`. - - ### Comment Formatting (CRITICAL) - - **ALWAYS use heredoc syntax for comments containing code references, backticks, or multiline content:** - - ```bash - gh issue comment --body "$(cat <<'EOF' - Your comment with `backticks` and code references preserved here. - Multiple lines work perfectly. - EOF - )" - ``` - - **NEVER use direct quotes with backticks** (shell will interpret them as command substitution): - ```bash - # WRONG - backticks disappear: - gh issue comment 123 --body "text with `code`" - - # CORRECT - backticks preserved: - gh issue comment 123 --body "$(cat <<'EOF' - text with `code` - EOF - )" - ``` - - ### GitHub Markdown Rules (MUST FOLLOW) - - **Code blocks MUST have EXACTLY 3 backticks and language identifier:** - - CORRECT: ` ```bash ` ... ` ``` ` - - WRONG: ` ``` ` (no language), ` ```` ` (4 backticks), ` `` ` (2 backticks) - - **Every opening ` ``` ` MUST have a closing ` ``` ` on its own line:** - ``` - ```bash - code here - ``` - ``` - - **NO trailing backticks or spaces after closing ` ``` `** - - **For inline code, use SINGLE backticks:** `code` not ```code``` - - **Lists inside code blocks break rendering - avoid them or use plain text** - - ### Rules - - EVERY response = GitHub comment (use heredoc for proper escaping) - - Code changes = PR (never push main/master) - - Setup: bun install first - - Acknowledge immediately, report when done - - ### Git Config - - user.name: sisyphus-dev-ai - - user.email: sisyphus-dev-ai@users.noreply.github.com - PROMPT_EOF - ) - jq --arg append "$PROMPT_APPEND" '.agents.Sisyphus.prompt_append = $append' "$OMO_JSON" > /tmp/omo.json && mv /tmp/omo.json "$OMO_JSON" - - # Add categories configuration for unspecified-low to use GLM 4.7 - jq '.categories["unspecified-low"] = { "model": "zai-coding-plan/glm-4.7" }' "$OMO_JSON" > /tmp/omo.json && mv /tmp/omo.json "$OMO_JSON" - - mkdir -p ~/.local/share/opencode - echo "$OPENCODE_AUTH_JSON" > ~/.local/share/opencode/auth.json - chmod 600 ~/.local/share/opencode/auth.json - - cat "$OPENCODE_JSON" - - # Collect context - - name: Collect Context - id: context - env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - EVENT_NAME: ${{ github.event_name }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - COMMENT_BODY: ${{ github.event.comment.body }} - COMMENT_AUTHOR: ${{ github.event.comment.user.login }} - COMMENT_ID_VAL: ${{ github.event.comment.id }} - REPO: ${{ github.repository }} - run: | - { - if [[ "$EVENT_NAME" == "issue_comment" ]]; then - ISSUE_NUM="$ISSUE_NUMBER" - AUTHOR="$COMMENT_AUTHOR" - COMMENT_ID="$COMMENT_ID_VAL" - - # Check if PR or Issue and get title - ISSUE_DATA=$(gh api "repos/$REPO/issues/${ISSUE_NUM}") - TITLE=$(echo "$ISSUE_DATA" | jq -r '.title') - if echo "$ISSUE_DATA" | jq -e '.pull_request' > /dev/null; then - echo "type=pr" - echo "number=${ISSUE_NUM}" - else - echo "type=issue" - echo "number=${ISSUE_NUM}" - fi - echo "title=${TITLE}" - fi - - echo "comment<> "$GITHUB_OUTPUT" - - # Add :eyes: reaction (as sisyphus-dev-ai) - - name: Add eyes reaction - if: steps.context.outputs.comment_id != '' - env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - REPOSITORY: ${{ github.repository }} - COMMENT_ID: ${{ steps.context.outputs.comment_id }} - run: | - gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \ - -X POST -f content="eyes" || true - - - name: Add working label - if: steps.context.outputs.number != '' - env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - REPOSITORY: ${{ github.repository }} - CONTEXT_TYPE: ${{ steps.context.outputs.type }} - CONTEXT_NUMBER: ${{ steps.context.outputs.number }} - run: | - gh label create "sisyphus: working" \ - --repo "$REPOSITORY" \ - --color "fcf2e1" \ - --description "Sisyphus is currently working on this" \ - --force || true - - if [[ "$CONTEXT_TYPE" == "pr" ]]; then - gh pr edit "$CONTEXT_NUMBER" \ - --repo "$REPOSITORY" \ - --add-label "sisyphus: working" || true - else - gh issue edit "$CONTEXT_NUMBER" \ - --repo "$REPOSITORY" \ - --add-label "sisyphus: working" || true - fi - - - name: Run oh-my-opencode - env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - USER_COMMENT: ${{ steps.context.outputs.comment }} - COMMENT_AUTHOR: ${{ steps.context.outputs.author }} - CONTEXT_TYPE: ${{ steps.context.outputs.type }} - CONTEXT_NUMBER: ${{ steps.context.outputs.number }} - CONTEXT_TITLE: ${{ steps.context.outputs.title }} - REPO_NAME: ${{ github.repository }} - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - run: | - export PATH="$HOME/.opencode/bin:$PATH" - - PROMPT=$(cat <<'PROMPT_EOF' - [analyze-mode] - ANALYSIS MODE. Gather context before diving deep: - - CONTEXT GATHERING (parallel): - - 1-2 explore agents (codebase patterns, implementations) - - 1-2 librarian agents (if external library involved) - - Direct tools: Grep, AST-grep, LSP for targeted searches - - IF COMPLEX (architecture, multi-system, debugging after 2+ failures): - - Consult oracle for strategic guidance - - SYNTHESIZE findings before proceeding. - - --- - - Your username is @sisyphus-dev-ai, mentioned by @AUTHOR_PLACEHOLDER in REPO_PLACEHOLDER. - - ## Context - - Title: TITLE_PLACEHOLDER - - Type: TYPE_PLACEHOLDER - - Number: #NUMBER_PLACEHOLDER - - Repository: REPO_PLACEHOLDER - - Default Branch: BRANCH_PLACEHOLDER - - ## User's Request - COMMENT_PLACEHOLDER - - --- - - ## CRITICAL: First Steps (MUST DO BEFORE ANYTHING ELSE) - - ### [CODE RED] MANDATORY CONTEXT READING - ZERO EXCEPTIONS - - **YOU MUST READ ALL CONTENT. NOT SOME. NOT MOST. ALL.** - - 1. **READ FULL CONVERSATION** - Execute ALL commands below before ANY other action: - - **Issues**: `gh issue view NUMBER_PLACEHOLDER --comments` - - **PRs**: Use ALL THREE commands to get COMPLETE context: - ```bash - gh pr view NUMBER_PLACEHOLDER --comments - gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/comments - gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/reviews - ``` - - **WHAT TO EXTRACT FROM THE CONVERSATION:** - - The ORIGINAL issue/PR description (first message) - this is often the TRUE requirement - - ALL previous attempts and their outcomes - - ALL decisions made and their reasoning - - ALL feedback, criticism, and rejection reasons - - ANY linked issues, PRs, or external references - - The EXACT ask from the user who mentioned you - - **FAILURE TO READ EVERYTHING = GUARANTEED FAILURE** - You WILL make wrong assumptions. You WILL repeat past mistakes. You WILL miss critical context. - - 2. **CREATE TODOS IMMEDIATELY**: Right after reading, create your todo list using todo tools. - - First todo: "Summarize issue/PR context and requirements" - - Break down ALL work into atomic, verifiable steps - - **GIT WORKFLOW (MANDATORY for implementation tasks)**: ALWAYS include these final todos: - - "Create new branch from origin/BRANCH_PLACEHOLDER (NEVER push directly to BRANCH_PLACEHOLDER)" - - "Commit changes" - - "Create PR to BRANCH_PLACEHOLDER branch" - - Plan everything BEFORE starting any work - - --- - - - Plan everything using todo tools. - Then investigate and satisfy the request. Only if user requested to you to work explicitly, then use plan agent to plan, todo obsessively then create a PR to `BRANCH_PLACEHOLDER` branch. - When done, report the result to the issue/PR with `gh issue comment NUMBER_PLACEHOLDER` or `gh pr comment NUMBER_PLACEHOLDER`. - PROMPT_EOF - ) - - PROMPT="${PROMPT//AUTHOR_PLACEHOLDER/$COMMENT_AUTHOR}" - PROMPT="${PROMPT//REPO_PLACEHOLDER/$REPO_NAME}" - PROMPT="${PROMPT//TYPE_PLACEHOLDER/$CONTEXT_TYPE}" - PROMPT="${PROMPT//NUMBER_PLACEHOLDER/$CONTEXT_NUMBER}" - PROMPT="${PROMPT//TITLE_PLACEHOLDER/$CONTEXT_TITLE}" - PROMPT="${PROMPT//BRANCH_PLACEHOLDER/$DEFAULT_BRANCH}" - PROMPT="${PROMPT//COMMENT_PLACEHOLDER/$USER_COMMENT}" - - stdbuf -oL -eL bun run dist/cli/index.js run "$PROMPT" - - # Push changes (as sisyphus-dev-ai) - - name: Push changes - if: always() - env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - run: | - if [[ -n "$(git status --porcelain)" ]]; then - git add -A - git commit -m "chore: changes by sisyphus-dev-ai" || true - fi - - BRANCH=$(git branch --show-current) - if [[ "$BRANCH" != "main" && "$BRANCH" != "master" ]]; then - git push origin "$BRANCH" || true - fi - - - name: Update reaction and remove label - if: always() - env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - REPOSITORY: ${{ github.repository }} - COMMENT_ID: ${{ steps.context.outputs.comment_id }} - CONTEXT_NUMBER: ${{ steps.context.outputs.number }} - CONTEXT_TYPE: ${{ steps.context.outputs.type }} - run: | - if [[ -n "$COMMENT_ID" ]]; then - REACTION_ID=$(gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \ - --jq '.[] | select(.content == "eyes" and .user.login == "sisyphus-dev-ai") | .id' | head -1) - if [[ -n "$REACTION_ID" ]]; then - gh api -X DELETE "/repos/${REPOSITORY}/reactions/${REACTION_ID}" || true - fi - - gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \ - -X POST -f content="+1" || true - fi - - if [[ -n "$CONTEXT_NUMBER" ]]; then - if [[ "$CONTEXT_TYPE" == "pr" ]]; then - gh pr edit "$CONTEXT_NUMBER" \ - --repo "$REPOSITORY" \ - --remove-label "sisyphus: working" || true - else - gh issue edit "$CONTEXT_NUMBER" \ - --repo "$REPOSITORY" \ - --remove-label "sisyphus: working" || true - fi - fi - - - name: Write job summary - if: always() - shell: bash - env: - JOB_SUMMARY_TITLE: Sisyphus agent run - JOB_SUMMARY_STATUS: ${{ job.status }} - JOB_SUMMARY_DETAILS: | - - Builds local oh-my-opencode. - - Configures OpenCode and runs the Sisyphus agent against the issue or PR context. - - Pushes agent changes when the working branch is safe to push. - JOB_SUMMARY_NEXT: Check setup/auth first, then the `Run oh-my-opencode` step and pushed branch output. - run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh +name: Sisyphus Agent + +on: + workflow_dispatch: + inputs: + prompt: + description: "Custom prompt" + required: false + # Only issue_comment works for fork PRs (secrets available) + # pull_request_review/pull_request_review_comment do NOT get secrets for fork PRs + issue_comment: + types: [created] + +jobs: + agent: + runs-on: ubuntu-latest + # @sisyphus-dev-ai mention only (maintainers, exclude self) + if: >- + github.event_name == 'workflow_dispatch' || + (github.event_name == 'issue_comment' && + contains(github.event.comment.body || '', '@sisyphus-dev-ai') && + (github.event.comment.user.login || '') != 'sisyphus-dev-ai' && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || '')) + + permissions: + contents: read + + env: + DO_NOT_TRACK: "1" + POSTHOG_DISABLED: "1" + ANTHROPIC_DISABLE_TELEMETRY: "1" + + steps: + # Checkout with sisyphus-dev-ai's PAT + - uses: actions/checkout@v5 + with: + token: ${{ secrets.GH_PAT }} + fetch-depth: 0 + + # Git config - commits as sisyphus-dev-ai + - name: Configure Git as sisyphus-dev-ai + run: | + git config user.name "sisyphus-dev-ai" + git config user.email "sisyphus-dev-ai@users.noreply.github.com" + + # gh CLI auth as sisyphus-dev-ai + - name: Authenticate gh CLI as sisyphus-dev-ai + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + run: | + echo "$GITHUB_TOKEN" | gh auth login --with-token + gh auth status || echo "::warning::gh auth status blocked — sandboxed runner (Codex/restricted env); login token already set via --with-token" + + - name: Ensure tmux is available (Linux) + if: runner.os == 'Linux' + run: | + set -euo pipefail + if ! command -v tmux >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y --no-install-recommends tmux + fi + tmux -V + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: "1.3.12" + + - name: Cache Bun dependencies + uses: actions/cache@v5 + with: + path: | + ~/.bun/install/cache + node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + + # Build local oh-my-opencode + - name: Build oh-my-opencode + run: | + bun install --frozen-lockfile + bun run build + + # Install OpenCode + configure local plugin + auth in single step + - name: Setup OpenCode with oh-my-opencode + env: + OPENCODE_AUTH_JSON: ${{ secrets.OPENCODE_AUTH_JSON }} + ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: | + export PATH="$HOME/.opencode/bin:$PATH" + + # Install OpenCode (skip if cached) + if ! command -v opencode &>/dev/null; then + echo "Installing OpenCode..." + curl -fsSL https://opencode.ai/install -o /tmp/opencode-install.sh + + # Try default installer first, fallback to re-download if it fails + if file /tmp/opencode-install.sh | grep -q "shell script\|text"; then + if ! bash /tmp/opencode-install.sh 2>&1; then + echo "Default installer failed, trying direct install..." + bash <(curl -fsSL https://opencode.ai/install) + fi + else + echo "Download corrupted, trying direct install..." + bash <(curl -fsSL https://opencode.ai/install) + fi + fi + opencode --version + + # Run local oh-my-opencode install (uses built dist) + bun run dist/cli/index.js install --no-tui --claude=max20 --openai=no --gemini=no --copilot=no + + # Override plugin to use local file reference + OPENCODE_JSON=~/.config/opencode/opencode.json + REPO_PATH=$(pwd) + jq --arg path "file://$REPO_PATH/packages/omo-opencode/src/index.ts" ' + .plugin = [.plugin[] | select(. != "oh-my-opencode")] + [$path] + ' "$OPENCODE_JSON" > /tmp/oc.json && mv /tmp/oc.json "$OPENCODE_JSON" + + OPENCODE_JSON=~/.config/opencode/opencode.json + jq --arg baseURL "$ANTHROPIC_BASE_URL" --arg apiKey "$ANTHROPIC_API_KEY" ' + .model = "anthropic/claude-opus-4-5" | + .provider.anthropic = { + "name": "Anthropic", + "npm": "@ai-sdk/anthropic", + "options": { + "baseURL": $baseURL, + "apiKey": $apiKey + }, + "models": { + "claude-opus-4-5": { + "id": "claude-opus-4-5-20251101", + "name": "Opus 4.5", + "limit": { "context": 190000, "output": 64000 }, + "options": { "effort": "high" } + }, + "claude-opus-4-5-high": { + "id": "claude-opus-4-5-20251101", + "name": "Opus 4.5 High", + "limit": { "context": 190000, "output": 128000 }, + "options": { "effort": "high", "thinking": { "type": "enabled", "budgetTokens": 64000 } } + }, + "claude-sonnet-4-6": { + "id": "claude-sonnet-4-6-20250929", + "name": "Sonnet 4.6", + "limit": { "context": 200000, "output": 64000 } + }, + "claude-sonnet-4-6-high": { + "id": "claude-sonnet-4-6-20250929", + "name": "Sonnet 4.6 High", + "limit": { "context": 200000, "output": 128000 }, + "options": { "thinking": { "type": "enabled", "budgetTokens": 64000 } } + }, + "claude-haiku-4-5": { + "id": "claude-haiku-4-5-20251001", + "name": "Haiku 4.5", + "limit": { "context": 200000, "output": 64000 } + } + } + } | + .provider["zai-coding-plan"] = { + "name": "Z.AI Coding Plan", + "npm": "@ai-sdk/openai-compatible", + "options": { + "baseURL": "https://api.z.ai/api/paas/v4" + }, + "models": { + "glm-4.7": { + "id": "glm-4.7", + "name": "GLM 4.7", + "limit": { "context": 128000, "output": 16000 } + }, + "glm-4.6v": { + "id": "glm-4.6v", + "name": "GLM 4.6 Vision", + "limit": { "context": 128000, "output": 16000 } + } + } + } | + .provider.openai = { + "name": "OpenAI", + "npm": "@ai-sdk/openai", + "models": { + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", + "limit": { "context": 128000, "output": 16000 } + }, + "gpt-5.5-codex": { + "id": "gpt-5.5-codex", + "name": "GPT-5.5 Codex", + "limit": { "context": 128000, "output": 32000 } + } + } + } + ' "$OPENCODE_JSON" > /tmp/oc.json && mv /tmp/oc.json "$OPENCODE_JSON" + + OMO_JSON=~/.config/opencode/oh-my-opencode.json + PROMPT_APPEND=$(cat << 'PROMPT_EOF' + + [CODE RED] Maximum precision required. Ultrathink before acting. + + YOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL. + TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST. + + ## AGENT UTILIZATION PRINCIPLES (by capability, not by name) + - **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS for file patterns, internal implementations, project structure + - **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS for API references, examples, external library docs + - **Planning & Strategy**: For implementation tasks, spawn a dedicated planning agent for work breakdown (not needed for simple questions/investigations) + - **High-IQ Reasoning**: Leverage specialized agents for architecture decisions, code review, strategic planning + - **Frontend/UI Tasks**: Delegate to UI-specialized agents for design and implementation + + ## EXECUTION RULES + - **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each. + - **PARALLEL**: Fire independent agent calls simultaneously via background_task - NEVER wait sequentially. + - **BACKGROUND FIRST**: Use background_task for exploration/research agents (10+ concurrent if needed). + - **VERIFY**: Re-read request after completion. Check ALL requirements met before reporting done. + - **DELEGATE**: Don't do everything yourself - orchestrate specialized agents for their strengths. + + ## WORKFLOW + 1. Analyze the request and identify required capabilities + 2. Spawn exploration/librarian agents via background_task in PARALLEL (10+ if needed) + 3. Always Use Plan agent with gathered context to create detailed work breakdown + 4. Execute with continuous verification against original requirements + + ## TDD (if test infrastructure exists) + + 1. Write spec (requirements) + 2. Write tests (failing) + 3. RED: tests fail + 4. Implement minimal code + 5. GREEN: tests pass + 6. Refactor if needed (must stay green) + 7. Next feature, repeat + + ## ZERO TOLERANCE FAILURES + - **NO Scope Reduction**: Never make "demo", "skeleton", "simplified", "basic" versions - deliver FULL implementation + - **NO MockUp Work**: When user asked you to do "port A", you must "port A", fully, 100%. No Extra feature, No reduced feature, no mock data, fully working 100% port. + - **NO Partial Completion**: Never stop at 60-80% saying "you can extend this..." - finish 100% + - **NO Assumed Shortcuts**: Never skip requirements you deem "optional" or "can be added later" + - **NO Premature Stopping**: Never declare done until ALL TODOs are completed and verified + - **NO TEST DELETION**: Never delete or skip failing tests to make the build pass. Fix the code, not the tests. + + THE USER ASKED FOR X. DELIVER EXACTLY X. NOT A SUBSET. NOT A DEMO. NOT A STARTING POINT. + + + + --- + + + [analyze-mode] + ANALYSIS MODE. Gather context before diving deep: + + CONTEXT GATHERING (parallel): + - 1-2 explore agents (codebase patterns, implementations) + - 1-2 librarian agents (if external library involved) + - Direct tools: Grep, AST-grep, LSP for targeted searches + + IF COMPLEX (architecture, multi-system, debugging after 2+ failures): + - Consult oracle for strategic guidance + + SYNTHESIZE findings before proceeding. + + --- + + ## GitHub Actions Environment + + You are `sisyphus-dev-ai` in GitHub Actions. + + ### CRITICAL: GitHub Comments = Your ONLY Output + + User CANNOT see console. Post everything via `gh issue comment` or `gh pr comment`. + + ### Comment Formatting (CRITICAL) + + **ALWAYS use heredoc syntax for comments containing code references, backticks, or multiline content:** + + ```bash + gh issue comment --body "$(cat <<'EOF' + Your comment with `backticks` and code references preserved here. + Multiple lines work perfectly. + EOF + )" + ``` + + **NEVER use direct quotes with backticks** (shell will interpret them as command substitution): + ```bash + # WRONG - backticks disappear: + gh issue comment 123 --body "text with `code`" + + # CORRECT - backticks preserved: + gh issue comment 123 --body "$(cat <<'EOF' + text with `code` + EOF + )" + ``` + + ### GitHub Markdown Rules (MUST FOLLOW) + + **Code blocks MUST have EXACTLY 3 backticks and language identifier:** + - CORRECT: ` ```bash ` ... ` ``` ` + - WRONG: ` ``` ` (no language), ` ```` ` (4 backticks), ` `` ` (2 backticks) + + **Every opening ` ``` ` MUST have a closing ` ``` ` on its own line:** + ``` + ```bash + code here + ``` + ``` + + **NO trailing backticks or spaces after closing ` ``` `** + + **For inline code, use SINGLE backticks:** `code` not ```code``` + + **Lists inside code blocks break rendering - avoid them or use plain text** + + ### Rules + - EVERY response = GitHub comment (use heredoc for proper escaping) + - Code changes = PR (never push main/master) + - Setup: bun install first + - Acknowledge immediately, report when done + + ### Git Config + - user.name: sisyphus-dev-ai + - user.email: sisyphus-dev-ai@users.noreply.github.com + PROMPT_EOF + ) + jq --arg append "$PROMPT_APPEND" '.agents.Sisyphus.prompt_append = $append' "$OMO_JSON" > /tmp/omo.json && mv /tmp/omo.json "$OMO_JSON" + + # Add categories configuration for unspecified-low to use GLM 4.7 + jq '.categories["unspecified-low"] = { "model": "zai-coding-plan/glm-4.7" }' "$OMO_JSON" > /tmp/omo.json && mv /tmp/omo.json "$OMO_JSON" + + mkdir -p ~/.local/share/opencode + echo "$OPENCODE_AUTH_JSON" > ~/.local/share/opencode/auth.json + chmod 600 ~/.local/share/opencode/auth.json + + cat "$OPENCODE_JSON" + + # Collect context + - name: Collect Context + id: context + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + EVENT_NAME: ${{ github.event_name }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + COMMENT_BODY: ${{ github.event.comment.body }} + COMMENT_AUTHOR: ${{ github.event.comment.user.login }} + COMMENT_ID_VAL: ${{ github.event.comment.id }} + REPO: ${{ github.repository }} + run: | + { + if [[ "$EVENT_NAME" == "issue_comment" ]]; then + ISSUE_NUM="$ISSUE_NUMBER" + AUTHOR="$COMMENT_AUTHOR" + COMMENT_ID="$COMMENT_ID_VAL" + + # Check if PR or Issue and get title + ISSUE_DATA=$(gh api "repos/$REPO/issues/${ISSUE_NUM}") + TITLE=$(echo "$ISSUE_DATA" | jq -r '.title') + if echo "$ISSUE_DATA" | jq -e '.pull_request' > /dev/null; then + echo "type=pr" + echo "number=${ISSUE_NUM}" + else + echo "type=issue" + echo "number=${ISSUE_NUM}" + fi + echo "title=${TITLE}" + fi + + echo "comment<> "$GITHUB_OUTPUT" + + # Add :eyes: reaction (as sisyphus-dev-ai) + - name: Add eyes reaction + if: steps.context.outputs.comment_id != '' + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + REPOSITORY: ${{ github.repository }} + COMMENT_ID: ${{ steps.context.outputs.comment_id }} + run: | + gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \ + -X POST -f content="eyes" || true + + - name: Add working label + if: steps.context.outputs.number != '' + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + REPOSITORY: ${{ github.repository }} + CONTEXT_TYPE: ${{ steps.context.outputs.type }} + CONTEXT_NUMBER: ${{ steps.context.outputs.number }} + run: | + gh label create "sisyphus: working" \ + --repo "$REPOSITORY" \ + --color "fcf2e1" \ + --description "Sisyphus is currently working on this" \ + --force || true + + if [[ "$CONTEXT_TYPE" == "pr" ]]; then + gh pr edit "$CONTEXT_NUMBER" \ + --repo "$REPOSITORY" \ + --add-label "sisyphus: working" || true + else + gh issue edit "$CONTEXT_NUMBER" \ + --repo "$REPOSITORY" \ + --add-label "sisyphus: working" || true + fi + + - name: Run oh-my-opencode + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + USER_COMMENT: ${{ steps.context.outputs.comment }} + COMMENT_AUTHOR: ${{ steps.context.outputs.author }} + CONTEXT_TYPE: ${{ steps.context.outputs.type }} + CONTEXT_NUMBER: ${{ steps.context.outputs.number }} + CONTEXT_TITLE: ${{ steps.context.outputs.title }} + REPO_NAME: ${{ github.repository }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + run: | + export PATH="$HOME/.opencode/bin:$PATH" + + PROMPT=$(cat <<'PROMPT_EOF' + [analyze-mode] + ANALYSIS MODE. Gather context before diving deep: + + CONTEXT GATHERING (parallel): + - 1-2 explore agents (codebase patterns, implementations) + - 1-2 librarian agents (if external library involved) + - Direct tools: Grep, AST-grep, LSP for targeted searches + + IF COMPLEX (architecture, multi-system, debugging after 2+ failures): + - Consult oracle for strategic guidance + + SYNTHESIZE findings before proceeding. + + --- + + Your username is @sisyphus-dev-ai, mentioned by @AUTHOR_PLACEHOLDER in REPO_PLACEHOLDER. + + ## Context + - Title: TITLE_PLACEHOLDER + - Type: TYPE_PLACEHOLDER + - Number: #NUMBER_PLACEHOLDER + - Repository: REPO_PLACEHOLDER + - Default Branch: BRANCH_PLACEHOLDER + + ## User's Request + COMMENT_PLACEHOLDER + + --- + + ## CRITICAL: First Steps (MUST DO BEFORE ANYTHING ELSE) + + ### [CODE RED] MANDATORY CONTEXT READING - ZERO EXCEPTIONS + + **YOU MUST READ ALL CONTENT. NOT SOME. NOT MOST. ALL.** + + 1. **READ FULL CONVERSATION** - Execute ALL commands below before ANY other action: + - **Issues**: `gh issue view NUMBER_PLACEHOLDER --comments` + - **PRs**: Use ALL THREE commands to get COMPLETE context: + ```bash + gh pr view NUMBER_PLACEHOLDER --comments + gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/comments + gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/reviews + ``` + + **WHAT TO EXTRACT FROM THE CONVERSATION:** + - The ORIGINAL issue/PR description (first message) - this is often the TRUE requirement + - ALL previous attempts and their outcomes + - ALL decisions made and their reasoning + - ALL feedback, criticism, and rejection reasons + - ANY linked issues, PRs, or external references + - The EXACT ask from the user who mentioned you + + **FAILURE TO READ EVERYTHING = GUARANTEED FAILURE** + You WILL make wrong assumptions. You WILL repeat past mistakes. You WILL miss critical context. + + 2. **CREATE TODOS IMMEDIATELY**: Right after reading, create your todo list using todo tools. + - First todo: "Summarize issue/PR context and requirements" + - Break down ALL work into atomic, verifiable steps + - **GIT WORKFLOW (MANDATORY for implementation tasks)**: ALWAYS include these final todos: + - "Create new branch from origin/BRANCH_PLACEHOLDER (NEVER push directly to BRANCH_PLACEHOLDER)" + - "Commit changes" + - "Create PR to BRANCH_PLACEHOLDER branch" + - Plan everything BEFORE starting any work + + --- + + + Plan everything using todo tools. + Then investigate and satisfy the request. Only if user requested to you to work explicitly, then use plan agent to plan, todo obsessively then create a PR to `BRANCH_PLACEHOLDER` branch. + When done, report the result to the issue/PR with `gh issue comment NUMBER_PLACEHOLDER` or `gh pr comment NUMBER_PLACEHOLDER`. + PROMPT_EOF + ) + + PROMPT="${PROMPT//AUTHOR_PLACEHOLDER/$COMMENT_AUTHOR}" + PROMPT="${PROMPT//REPO_PLACEHOLDER/$REPO_NAME}" + PROMPT="${PROMPT//TYPE_PLACEHOLDER/$CONTEXT_TYPE}" + PROMPT="${PROMPT//NUMBER_PLACEHOLDER/$CONTEXT_NUMBER}" + PROMPT="${PROMPT//TITLE_PLACEHOLDER/$CONTEXT_TITLE}" + PROMPT="${PROMPT//BRANCH_PLACEHOLDER/$DEFAULT_BRANCH}" + PROMPT="${PROMPT//COMMENT_PLACEHOLDER/$USER_COMMENT}" + + stdbuf -oL -eL bun run dist/cli/index.js run "$PROMPT" + + # Push changes (as sisyphus-dev-ai) + - name: Push changes + if: always() + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + run: | + if [[ -n "$(git status --porcelain)" ]]; then + git add -A + git commit -m "chore: changes by sisyphus-dev-ai" || true + fi + + BRANCH=$(git branch --show-current) + if [[ "$BRANCH" != "main" && "$BRANCH" != "master" ]]; then + git push origin "$BRANCH" || true + fi + + - name: Update reaction and remove label + if: always() + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + REPOSITORY: ${{ github.repository }} + COMMENT_ID: ${{ steps.context.outputs.comment_id }} + CONTEXT_NUMBER: ${{ steps.context.outputs.number }} + CONTEXT_TYPE: ${{ steps.context.outputs.type }} + run: | + if [[ -n "$COMMENT_ID" ]]; then + REACTION_ID=$(gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \ + --jq '.[] | select(.content == "eyes" and .user.login == "sisyphus-dev-ai") | .id' | head -1) + if [[ -n "$REACTION_ID" ]]; then + gh api -X DELETE "/repos/${REPOSITORY}/reactions/${REACTION_ID}" || true + fi + + gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \ + -X POST -f content="+1" || true + fi + + if [[ -n "$CONTEXT_NUMBER" ]]; then + if [[ "$CONTEXT_TYPE" == "pr" ]]; then + gh pr edit "$CONTEXT_NUMBER" \ + --repo "$REPOSITORY" \ + --remove-label "sisyphus: working" || true + else + gh issue edit "$CONTEXT_NUMBER" \ + --repo "$REPOSITORY" \ + --remove-label "sisyphus: working" || true + fi + fi + + - name: Write job summary + if: always() + shell: bash + env: + JOB_SUMMARY_TITLE: Sisyphus agent run + JOB_SUMMARY_STATUS: ${{ job.status }} + JOB_SUMMARY_DETAILS: | + - Builds local oh-my-opencode. + - Configures OpenCode and runs the Sisyphus agent against the issue or PR context. + - Pushes agent changes when the working branch is safe to push. + JOB_SUMMARY_NEXT: Check setup/auth first, then the `Run oh-my-opencode` step and pushed branch output. + run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh From e031810d94df42f1e6282707e8e67cb79f8d98df Mon Sep 17 00:00:00 2001 From: fvegiard Date: Sun, 21 Jun 2026 00:26:09 -0400 Subject: [PATCH 02/21] feat(ci): add proactive network-firewall-guard workflow --- .github/workflows/network-firewall-guard.yml | 131 +++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 .github/workflows/network-firewall-guard.yml diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml new file mode 100644 index 00000000000..3f9d270292a --- /dev/null +++ b/.github/workflows/network-firewall-guard.yml @@ -0,0 +1,131 @@ +name: Network Firewall Guard + +on: + pull_request: + paths: + - '.github/workflows/**' + push: + branches: [master, dev] + paths: + - '.github/workflows/**' + +jobs: + guard: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@v5 + + - name: Scan workflows for sandbox-blocked patterns + id: scan + shell: bash + run: | + set -euo pipefail + + FINDINGS="" + FILES=$(find .github/workflows -name "*.yml" | sort) + + # Patterns known to fail in Codex/restricted sandboxes + declare -A PATTERNS + PATTERNS["gh auth status"]="gh auth status makes an API call to api.github.com — use '|| true' or remove; login via --with-token is sufficient" + PATTERNS["posthog"]="PostHog telemetry (us.i.posthog.com) is blocked in Codex sandboxes — set POSTHOG_DISABLED=1" + PATTERNS["DO_NOT_TRACK"]="" # presence = good, skip + PATTERNS["npx -y lazycodex"]="lazycodex-ai npx call triggers PostHog telemetry — ensure DO_NOT_TRACK=1 is set" + PATTERNS["curl.*opencode.ai/install"]="opencode install script makes outbound calls — may be blocked in locked-down runners" + PATTERNS["forceExit"]="forceExit in test runner = open handles (usually from unresolved telemetry connections)" + + for FILE in $FILES; do + for PATTERN in "gh auth status" "us.i.posthog.com" "npx -y lazycodex" "curl.*opencode.ai/install"; do + if grep -qiE "$PATTERN" "$FILE"; then + # Check if DO_NOT_TRACK already set in same file + if [[ "$PATTERN" == *"posthog"* ]] || [[ "$PATTERN" == *"lazycodex"* ]]; then + if grep -q "DO_NOT_TRACK" "$FILE" && grep -q "POSTHOG_DISABLED" "$FILE"; then + continue # already mitigated + fi + fi + LINE=$(grep -inE "$PATTERN" "$FILE" | head -1) + FINDINGS="${FINDINGS}\n🚨 \`${FILE}\`: \`${LINE}\`" + case "$PATTERN" in + "gh auth status") + FINDINGS="${FINDINGS}\n → **Fix**: append \`|| echo '::warning::...'\` (non-fatal) — api.github.com is blocked in Codex sandbox" + ;; + *"posthog"*) + FINDINGS="${FINDINGS}\n → **Fix**: add \`POSTHOG_DISABLED: \"1\"\` and \`DO_NOT_TRACK: \"1\"\` to job \`env:\`" + ;; + *"lazycodex"*) + FINDINGS="${FINDINGS}\n → **Fix**: add \`DO_NOT_TRACK: \"1\"\` to this job's \`env:\` to suppress telemetry" + ;; + *"opencode.ai/install"*) + FINDINGS="${FINDINGS}\n → **Info**: ensure outbound curl is allowed or use cached install step" + ;; + esac + fi + done + done + + if [[ -z "$FINDINGS" ]]; then + echo "findings=" >> "$GITHUB_OUTPUT" + echo "clean=true" >> "$GITHUB_OUTPUT" + else + # Write multiline output safely + { + echo "findings<> "$GITHUB_OUTPUT" + echo "clean=false" >> "$GITHUB_OUTPUT" + fi + + - name: Write summary + if: always() + run: | + { + echo "## 🔍 Network Firewall Guard" + echo "" + if [[ "${{ steps.scan.outputs.clean }}" == "true" ]]; then + echo "✅ **No sandbox-blocking patterns detected** in workflow files." + else + echo "⚠️ **Potential sandbox-blocking patterns found:**" + echo "" + echo -e "${{ steps.scan.outputs.findings }}" + echo "" + echo "### Why this matters" + echo "GitHub Codex / restricted runners block outbound calls to \`api.github.com\`, \`us.i.posthog.com\`, etc." + echo "These calls cause \`exit code 1\` and \`forceExit\` failures that look like test failures but are network issues." + echo "" + echo "### Standard fixes" + echo "- \`gh auth status\` → append \`|| echo '::warning::blocked'\`" + echo "- Telemetry → add \`DO_NOT_TRACK: \"1\"\` and \`POSTHOG_DISABLED: \"1\"\` to job \`env:\`" + echo "- PostHog in test runner → set \`ANTHROPIC_DISABLE_TELEMETRY: \"1\"\`" + fi + } >> "$GITHUB_STEP_SUMMARY" + + - name: Comment on PR with findings + if: github.event_name == 'pull_request' && steps.scan.outputs.clean == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_URL: ${{ github.event.pull_request.html_url }} + FINDINGS: ${{ steps.scan.outputs.findings }} + run: | + gh pr comment "$PR_URL" --body "$(cat <<'EOF' + ## ⚠️ Network Firewall Guard — Sandbox-blocking patterns detected + + The following patterns in your workflow files are known to fail in Codex / restricted GitHub Actions runners: + + ${{ steps.scan.outputs.findings }} + + ### Standard fixes + - `gh auth status` → append `|| echo '::warning::blocked'` (non-fatal) + - Telemetry calls → add `DO_NOT_TRACK: "1"` and `POSTHOG_DISABLED: "1"` to job `env:` + + > Auto-detected by `.github/workflows/network-firewall-guard.yml` + EOF + )" + + - name: Fail if blocking patterns found + if: steps.scan.outputs.clean == 'false' + run: | + echo "::error::Sandbox-blocking network patterns found in workflow files. See summary for details." + exit 1 From d7a6075457d5ad61fa3613c00e3a19efdcd0321f Mon Sep 17 00:00:00 2001 From: fvegiard Date: Sun, 21 Jun 2026 00:36:10 -0400 Subject: [PATCH 03/21] fix(ci): add OMO telemetry vars + remove em dash (reviewer feedback) --- .github/workflows/sisyphus-agent.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sisyphus-agent.yml b/.github/workflows/sisyphus-agent.yml index 95b4611f649..a33f1ba3b01 100644 --- a/.github/workflows/sisyphus-agent.yml +++ b/.github/workflows/sisyphus-agent.yml @@ -29,6 +29,8 @@ jobs: DO_NOT_TRACK: "1" POSTHOG_DISABLED: "1" ANTHROPIC_DISABLE_TELEMETRY: "1" + OMO_DISABLE_POSTHOG: "1" + OMO_SEND_ANONYMOUS_TELEMETRY: "0" steps: # Checkout with sisyphus-dev-ai's PAT @@ -49,7 +51,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GH_PAT }} run: | echo "$GITHUB_TOKEN" | gh auth login --with-token - gh auth status || echo "::warning::gh auth status blocked — sandboxed runner (Codex/restricted env); login token already set via --with-token" + gh auth status || echo "::warning::gh auth status blocked - sandboxed runner (Codex/restricted env); login token already set via --with-token" - name: Ensure tmux is available (Linux) if: runner.os == 'Linux' From f2d16aeeeab42286e5b9a096d1409971a42df156 Mon Sep 17 00:00:00 2001 From: fvegiard Date: Sun, 21 Jun 2026 00:36:21 -0400 Subject: [PATCH 04/21] fix(ci): rewrite network-firewall-guard - no self-scan, recognize mitigations, PR-only diff, repo summary pattern --- .github/workflows/network-firewall-guard.yml | 146 ++++++++++--------- 1 file changed, 79 insertions(+), 67 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 3f9d270292a..8ee83ffc1bf 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -17,6 +17,8 @@ jobs: pull-requests: write steps: - uses: actions/checkout@v5 + with: + fetch-depth: 0 - name: Scan workflows for sandbox-blocked patterns id: scan @@ -24,108 +26,118 @@ jobs: run: | set -euo pipefail - FINDINGS="" - FILES=$(find .github/workflows -name "*.yml" | sort) + THIS_FILE=".github/workflows/network-firewall-guard.yml" - # Patterns known to fail in Codex/restricted sandboxes - declare -A PATTERNS - PATTERNS["gh auth status"]="gh auth status makes an API call to api.github.com — use '|| true' or remove; login via --with-token is sufficient" - PATTERNS["posthog"]="PostHog telemetry (us.i.posthog.com) is blocked in Codex sandboxes — set POSTHOG_DISABLED=1" - PATTERNS["DO_NOT_TRACK"]="" # presence = good, skip - PATTERNS["npx -y lazycodex"]="lazycodex-ai npx call triggers PostHog telemetry — ensure DO_NOT_TRACK=1 is set" - PATTERNS["curl.*opencode.ai/install"]="opencode install script makes outbound calls — may be blocked in locked-down runners" - PATTERNS["forceExit"]="forceExit in test runner = open handles (usually from unresolved telemetry connections)" + # On PRs scan only changed workflow files; on push scan all (except self) + if [ "${{ github.event_name }}" = "pull_request" ]; then + FILES=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" \ + | grep '^\.github/workflows/.*\.yml$' || true) + else + FILES=$(find .github/workflows -name "*.yml" | sort) + fi + + BLOCK_FINDINGS="" + INFO_FINDINGS="" for FILE in $FILES; do - for PATTERN in "gh auth status" "us.i.posthog.com" "npx -y lazycodex" "curl.*opencode.ai/install"; do - if grep -qiE "$PATTERN" "$FILE"; then - # Check if DO_NOT_TRACK already set in same file - if [[ "$PATTERN" == *"posthog"* ]] || [[ "$PATTERN" == *"lazycodex"* ]]; then - if grep -q "DO_NOT_TRACK" "$FILE" && grep -q "POSTHOG_DISABLED" "$FILE"; then - continue # already mitigated - fi + [ "$FILE" = "$THIS_FILE" ] && continue + [ -f "$FILE" ] || continue + + # gh auth status - flag only when NOT already made non-fatal (no || after it) + while IFS= read -r line; do + if echo "$line" | grep -qE "gh auth status"; then + if echo "$line" | grep -qE "\|\|"; then + : # already mitigated + else + LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: ${line}\n Fix: append '|| echo ::warning::blocked' - api.github.com is blocked in Codex sandbox\n" fi - LINE=$(grep -inE "$PATTERN" "$FILE" | head -1) - FINDINGS="${FINDINGS}\n🚨 \`${FILE}\`: \`${LINE}\`" - case "$PATTERN" in - "gh auth status") - FINDINGS="${FINDINGS}\n → **Fix**: append \`|| echo '::warning::...'\` (non-fatal) — api.github.com is blocked in Codex sandbox" - ;; - *"posthog"*) - FINDINGS="${FINDINGS}\n → **Fix**: add \`POSTHOG_DISABLED: \"1\"\` and \`DO_NOT_TRACK: \"1\"\` to job \`env:\`" - ;; - *"lazycodex"*) - FINDINGS="${FINDINGS}\n → **Fix**: add \`DO_NOT_TRACK: \"1\"\` to this job's \`env:\` to suppress telemetry" - ;; - *"opencode.ai/install"*) - FINDINGS="${FINDINGS}\n → **Info**: ensure outbound curl is allowed or use cached install step" - ;; - esac fi - done + done < <(grep -E "gh auth status" "$FILE" || true) + + # PostHog / telemetry - flag only when OMO opt-out not present in same file + if grep -qiE "us\.i\.posthog\.com|posthog" "$FILE"; then + if grep -qE "OMO_DISABLE_POSTHOG|OMO_SEND_ANONYMOUS_TELEMETRY|DO_NOT_TRACK" "$FILE"; then + : # already mitigated + else + LINENUM=$(grep -niE "posthog" "$FILE" | head -1 | cut -d: -f1) + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: posthog call detected\n Fix: add OMO_DISABLE_POSTHOG=1 and OMO_SEND_ANONYMOUS_TELEMETRY=0 to job env:\n" + fi + fi + + # npx lazycodex without DO_NOT_TRACK + if grep -qE "npx.*lazycodex" "$FILE"; then + if grep -qE "DO_NOT_TRACK|OMO_DISABLE_POSTHOG" "$FILE"; then + : # already mitigated + else + LINENUM=$(grep -nE "npx.*lazycodex" "$FILE" | head -1 | cut -d: -f1) + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: npx lazycodex without telemetry opt-out\n Fix: add DO_NOT_TRACK=1 to job env:\n" + fi + fi + + # curl to opencode.ai - info only, not a hard block + if grep -qE "curl.*opencode\.ai" "$FILE"; then + LINENUM=$(grep -nE "curl.*opencode\.ai" "$FILE" | head -1 | cut -d: -f1) + INFO_FINDINGS="${INFO_FINDINGS}INFO ${FILE}:${LINENUM}: curl to opencode.ai may be blocked on locked-down runners\n" + fi done - if [[ -z "$FINDINGS" ]]; then + if [ -z "$BLOCK_FINDINGS" ] && [ -z "$INFO_FINDINGS" ]; then echo "findings=" >> "$GITHUB_OUTPUT" echo "clean=true" >> "$GITHUB_OUTPUT" else - # Write multiline output safely { echo "findings<> "$GITHUB_OUTPUT" - echo "clean=false" >> "$GITHUB_OUTPUT" + if [ -n "$BLOCK_FINDINGS" ]; then + echo "clean=false" >> "$GITHUB_OUTPUT" + else + echo "clean=true" >> "$GITHUB_OUTPUT" + fi fi - - name: Write summary + - name: Write job summary if: always() - run: | - { - echo "## 🔍 Network Firewall Guard" - echo "" - if [[ "${{ steps.scan.outputs.clean }}" == "true" ]]; then - echo "✅ **No sandbox-blocking patterns detected** in workflow files." - else - echo "⚠️ **Potential sandbox-blocking patterns found:**" - echo "" - echo -e "${{ steps.scan.outputs.findings }}" - echo "" - echo "### Why this matters" - echo "GitHub Codex / restricted runners block outbound calls to \`api.github.com\`, \`us.i.posthog.com\`, etc." - echo "These calls cause \`exit code 1\` and \`forceExit\` failures that look like test failures but are network issues." - echo "" - echo "### Standard fixes" - echo "- \`gh auth status\` → append \`|| echo '::warning::blocked'\`" - echo "- Telemetry → add \`DO_NOT_TRACK: \"1\"\` and \`POSTHOG_DISABLED: \"1\"\` to job \`env:\`" - echo "- PostHog in test runner → set \`ANTHROPIC_DISABLE_TELEMETRY: \"1\"\`" - fi - } >> "$GITHUB_STEP_SUMMARY" + shell: bash + env: + JOB_SUMMARY_TITLE: Network Firewall Guard + JOB_SUMMARY_STATUS: ${{ job.status }} + JOB_SUMMARY_DETAILS: | + Scans changed workflow files for patterns known to fail in Codex/restricted runners. + Flags: gh auth status (api.github.com), PostHog telemetry, npx lazycodex without opt-out. + Skips patterns already mitigated (|| echo, OMO_DISABLE_POSTHOG, DO_NOT_TRACK). + JOB_SUMMARY_NEXT: | + If blocked: append '|| echo ::warning::' to gh auth status, add OMO_DISABLE_POSTHOG=1 + and OMO_SEND_ANONYMOUS_TELEMETRY=0 for PostHog, or DO_NOT_TRACK=1 for npx lazycodex. + run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh - name: Comment on PR with findings if: github.event_name == 'pull_request' && steps.scan.outputs.clean == 'false' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_URL: ${{ github.event.pull_request.html_url }} - FINDINGS: ${{ steps.scan.outputs.findings }} run: | gh pr comment "$PR_URL" --body "$(cat <<'EOF' - ## ⚠️ Network Firewall Guard — Sandbox-blocking patterns detected + ## Network Firewall Guard - Sandbox-blocking patterns detected - The following patterns in your workflow files are known to fail in Codex / restricted GitHub Actions runners: + The following patterns in changed workflow files are known to fail in Codex / restricted runners: ${{ steps.scan.outputs.findings }} ### Standard fixes - - `gh auth status` → append `|| echo '::warning::blocked'` (non-fatal) - - Telemetry calls → add `DO_NOT_TRACK: "1"` and `POSTHOG_DISABLED: "1"` to job `env:` + - gh auth status -> append || echo ::warning::blocked (non-fatal; login via --with-token is sufficient) + - PostHog telemetry -> add OMO_DISABLE_POSTHOG=1 and OMO_SEND_ANONYMOUS_TELEMETRY=0 to job env: + - npx lazycodex -> add DO_NOT_TRACK=1 to job env: - > Auto-detected by `.github/workflows/network-firewall-guard.yml` + Auto-detected by .github/workflows/network-firewall-guard.yml EOF )" - name: Fail if blocking patterns found if: steps.scan.outputs.clean == 'false' run: | - echo "::error::Sandbox-blocking network patterns found in workflow files. See summary for details." + echo "::error::Sandbox-blocking network patterns found in changed workflow files. See summary." exit 1 From aacdd2a9b01a25c2b12a73d4260a8d8cd7d7156f Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 00:56:20 -0400 Subject: [PATCH 05/21] fix(guard): push=changed-only, value-check opt-outs, OMO_CODEX prefix, temp-file findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes 5 bugs from chatgpt-codex-connector review: 1. (#3447917982) Push events now use git diff HEAD~1..HEAD to scan only changed files, same as PR logic — avoids flagging pre-existing violations in ci.yml on every push to master/dev. 2. (#3447917983) Opt-out mitigation now checks actual value: OMO_DISABLE_POSTHOG must be "1"/=1 (truthy), OMO_SEND_ANONYMOUS_TELEMETRY must be "0"/=0 (falsy). Presence-only grep no longer masks unmitigated PostHog calls. 3. (#3447917984, #3447911093) lazycodex check now requires OMO_CODEX_DISABLE_POSTHOG="1" or OMO_DISABLE_POSTHOG="1" (the actual flags read by install-local.mjs via PRODUCT_ENV_PREFIX= "OMO_CODEX"). DO_NOT_TRACK alone is insufficient. 4. (#3447911087) Findings written to mktemp file; PR comment body reads via cat "$FINDINGS_FILE" — raw workflow lines are never interpolated by the shell, preventing command injection. 5. (#3447911091) File glob now uses ya?ml to match both .yml and .yaml. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 76 ++++++++++---------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 8ee83ffc1bf..32c5d1fb235 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -28,12 +28,13 @@ jobs: THIS_FILE=".github/workflows/network-firewall-guard.yml" - # On PRs scan only changed workflow files; on push scan all (except self) + # Both PR and push: scan only changed workflow files if [ "${{ github.event_name }}" = "pull_request" ]; then FILES=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" \ - | grep '^\.github/workflows/.*\.yml$' || true) + | grep -E '^\.github/workflows/.*\.ya?ml$' || true) else - FILES=$(find .github/workflows -name "*.yml" | sort) + FILES=$(git diff --name-only HEAD~1..HEAD \ + | grep -E '^\.github/workflows/.*\.ya?ml$' || true) fi BLOCK_FINDINGS="" @@ -43,60 +44,62 @@ jobs: [ "$FILE" = "$THIS_FILE" ] && continue [ -f "$FILE" ] || continue - # gh auth status - flag only when NOT already made non-fatal (no || after it) + # gh auth status - flag only when NOT already made non-fatal while IFS= read -r line; do if echo "$line" | grep -qE "gh auth status"; then if echo "$line" | grep -qE "\|\|"; then : # already mitigated else LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: ${line}\n Fix: append '|| echo ::warning::blocked' - api.github.com is blocked in Codex sandbox\n" + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append '|| echo ::warning::blocked'\n" fi fi done < <(grep -E "gh auth status" "$FILE" || true) - # PostHog / telemetry - flag only when OMO opt-out not present in same file + # PostHog - check opt-out with correct truthy/falsy value (not just name presence) if grep -qiE "us\.i\.posthog\.com|posthog" "$FILE"; then - if grep -qE "OMO_DISABLE_POSTHOG|OMO_SEND_ANONYMOUS_TELEMETRY|DO_NOT_TRACK" "$FILE"; then - : # already mitigated + if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"1"|OMO_DISABLE_POSTHOG=1' "$FILE" || \ + grep -qE 'OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*"0"|OMO_SEND_ANONYMOUS_TELEMETRY=0' "$FILE"; then + : # properly mitigated else LINENUM=$(grep -niE "posthog" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: posthog call detected\n Fix: add OMO_DISABLE_POSTHOG=1 and OMO_SEND_ANONYMOUS_TELEMETRY=0 to job env:\n" + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: posthog without valid opt-out value\n Fix: OMO_DISABLE_POSTHOG: \"1\" and OMO_SEND_ANONYMOUS_TELEMETRY: \"0\"\n" fi fi - # npx lazycodex without DO_NOT_TRACK + # npx lazycodex - requires OMO_CODEX_DISABLE_POSTHOG or OMO_DISABLE_POSTHOG (truthy) if grep -qE "npx.*lazycodex" "$FILE"; then - if grep -qE "DO_NOT_TRACK|OMO_DISABLE_POSTHOG" "$FILE"; then - : # already mitigated + if grep -qE 'OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"1"|OMO_CODEX_DISABLE_POSTHOG=1' "$FILE" || \ + grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"1"|OMO_DISABLE_POSTHOG=1' "$FILE"; then + : # properly mitigated else LINENUM=$(grep -nE "npx.*lazycodex" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: npx lazycodex without telemetry opt-out\n Fix: add DO_NOT_TRACK=1 to job env:\n" + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: npx lazycodex without OMO_CODEX_DISABLE_POSTHOG opt-out\n Fix: OMO_CODEX_DISABLE_POSTHOG: \"1\" or OMO_DISABLE_POSTHOG: \"1\"\n" fi fi - # curl to opencode.ai - info only, not a hard block + # curl opencode.ai - info only if grep -qE "curl.*opencode\.ai" "$FILE"; then LINENUM=$(grep -nE "curl.*opencode\.ai" "$FILE" | head -1 | cut -d: -f1) INFO_FINDINGS="${INFO_FINDINGS}INFO ${FILE}:${LINENUM}: curl to opencode.ai may be blocked on locked-down runners\n" fi done - if [ -z "$BLOCK_FINDINGS" ] && [ -z "$INFO_FINDINGS" ]; then - echo "findings=" >> "$GITHUB_OUTPUT" + # Write findings to temp file to avoid shell injection via GITHUB_OUTPUT + FINDINGS_FILE=$(mktemp) + [ -n "$BLOCK_FINDINGS" ] && printf '%b' "$BLOCK_FINDINGS" >> "$FINDINGS_FILE" + [ -n "$INFO_FINDINGS" ] && printf '%b' "$INFO_FINDINGS" >> "$FINDINGS_FILE" + + if [ ! -s "$FINDINGS_FILE" ]; then echo "clean=true" >> "$GITHUB_OUTPUT" + echo "findings_file=" >> "$GITHUB_OUTPUT" else - { - echo "findings<> "$GITHUB_OUTPUT" if [ -n "$BLOCK_FINDINGS" ]; then echo "clean=false" >> "$GITHUB_OUTPUT" else echo "clean=true" >> "$GITHUB_OUTPUT" fi + echo "findings_file=$FINDINGS_FILE" >> "$GITHUB_OUTPUT" fi - name: Write job summary @@ -107,11 +110,11 @@ jobs: JOB_SUMMARY_STATUS: ${{ job.status }} JOB_SUMMARY_DETAILS: | Scans changed workflow files for patterns known to fail in Codex/restricted runners. - Flags: gh auth status (api.github.com), PostHog telemetry, npx lazycodex without opt-out. - Skips patterns already mitigated (|| echo, OMO_DISABLE_POSTHOG, DO_NOT_TRACK). + Flags: gh auth status, PostHog telemetry, npx lazycodex without opt-out. + Checks actual opt-out values (not just variable presence). JOB_SUMMARY_NEXT: | - If blocked: append '|| echo ::warning::' to gh auth status, add OMO_DISABLE_POSTHOG=1 - and OMO_SEND_ANONYMOUS_TELEMETRY=0 for PostHog, or DO_NOT_TRACK=1 for npx lazycodex. + If blocked: append '|| echo ::warning::' to gh auth status, add OMO_DISABLE_POSTHOG: "1" + and OMO_SEND_ANONYMOUS_TELEMETRY: "0" for PostHog, or OMO_CODEX_DISABLE_POSTHOG: "1" for lazycodex. run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh - name: Comment on PR with findings @@ -119,22 +122,21 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_URL: ${{ github.event.pull_request.html_url }} + FINDINGS_FILE: ${{ steps.scan.outputs.findings_file }} run: | - gh pr comment "$PR_URL" --body "$(cat <<'EOF' - ## Network Firewall Guard - Sandbox-blocking patterns detected + BODY=$(cat "$FINDINGS_FILE") + gh pr comment "$PR_URL" --body "## Network Firewall Guard - Sandbox-blocking patterns detected - The following patterns in changed workflow files are known to fail in Codex / restricted runners: +The following patterns in changed workflow files are known to fail in Codex / restricted runners: - ${{ steps.scan.outputs.findings }} +${BODY} - ### Standard fixes - - gh auth status -> append || echo ::warning::blocked (non-fatal; login via --with-token is sufficient) - - PostHog telemetry -> add OMO_DISABLE_POSTHOG=1 and OMO_SEND_ANONYMOUS_TELEMETRY=0 to job env: - - npx lazycodex -> add DO_NOT_TRACK=1 to job env: +### Standard fixes +- gh auth status: append || echo ::warning::blocked (non-fatal) +- PostHog: add OMO_DISABLE_POSTHOG: \"1\" and OMO_SEND_ANONYMOUS_TELEMETRY: \"0\" to job env +- npx lazycodex: add OMO_CODEX_DISABLE_POSTHOG: \"1\" to job env - Auto-detected by .github/workflows/network-firewall-guard.yml - EOF - )" +Auto-detected by .github/workflows/network-firewall-guard.yml" - name: Fail if blocking patterns found if: steps.scan.outputs.clean == 'false' From 5ca936205d590bf8a8f9093ba33d1ef2060b2c82 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 01:00:44 -0400 Subject: [PATCH 06/21] =?UTF-8?q?fix(guard):=20fix=20YAML=20parse=20error?= =?UTF-8?q?=20=E2=80=94=20use=20body-file=20instead=20of=20multi-line=20--?= =?UTF-8?q?body?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit actionlint reported YAML syntax error at line 132: unindented lines inside run: | block broke the YAML literal scalar. The multi-line --body string passed directly to gh pr comment caused the YAML parser to exit the block. Fix: build PR comment body via { echo ...; cat findings; } > $BODY_FILE then pass --body-file "$BODY_FILE" to gh pr comment. All content stays properly indented inside the run: | block. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 41 +++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 32c5d1fb235..2a8facdf88e 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -51,7 +51,7 @@ jobs: : # already mitigated else LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append '|| echo ::warning::blocked'\n" + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append || echo ::warning::blocked\n" fi fi done < <(grep -E "gh auth status" "$FILE" || true) @@ -63,7 +63,7 @@ jobs: : # properly mitigated else LINENUM=$(grep -niE "posthog" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: posthog without valid opt-out value\n Fix: OMO_DISABLE_POSTHOG: \"1\" and OMO_SEND_ANONYMOUS_TELEMETRY: \"0\"\n" + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: posthog without valid opt-out value\n Fix: OMO_DISABLE_POSTHOG: 1 and OMO_SEND_ANONYMOUS_TELEMETRY: 0\n" fi fi @@ -74,7 +74,7 @@ jobs: : # properly mitigated else LINENUM=$(grep -nE "npx.*lazycodex" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: npx lazycodex without OMO_CODEX_DISABLE_POSTHOG opt-out\n Fix: OMO_CODEX_DISABLE_POSTHOG: \"1\" or OMO_DISABLE_POSTHOG: \"1\"\n" + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: npx lazycodex without OMO_CODEX_DISABLE_POSTHOG opt-out\n Fix: OMO_CODEX_DISABLE_POSTHOG: 1 or OMO_DISABLE_POSTHOG: 1\n" fi fi @@ -85,7 +85,7 @@ jobs: fi done - # Write findings to temp file to avoid shell injection via GITHUB_OUTPUT + # Write findings to temp file to avoid shell injection and YAML indentation issues FINDINGS_FILE=$(mktemp) [ -n "$BLOCK_FINDINGS" ] && printf '%b' "$BLOCK_FINDINGS" >> "$FINDINGS_FILE" [ -n "$INFO_FINDINGS" ] && printf '%b' "$INFO_FINDINGS" >> "$FINDINGS_FILE" @@ -113,8 +113,8 @@ jobs: Flags: gh auth status, PostHog telemetry, npx lazycodex without opt-out. Checks actual opt-out values (not just variable presence). JOB_SUMMARY_NEXT: | - If blocked: append '|| echo ::warning::' to gh auth status, add OMO_DISABLE_POSTHOG: "1" - and OMO_SEND_ANONYMOUS_TELEMETRY: "0" for PostHog, or OMO_CODEX_DISABLE_POSTHOG: "1" for lazycodex. + If blocked: append || echo ::warning:: to gh auth status, add OMO_DISABLE_POSTHOG: 1 + and OMO_SEND_ANONYMOUS_TELEMETRY: 0 for PostHog, or OMO_CODEX_DISABLE_POSTHOG: 1 for lazycodex. run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh - name: Comment on PR with findings @@ -124,19 +124,22 @@ jobs: PR_URL: ${{ github.event.pull_request.html_url }} FINDINGS_FILE: ${{ steps.scan.outputs.findings_file }} run: | - BODY=$(cat "$FINDINGS_FILE") - gh pr comment "$PR_URL" --body "## Network Firewall Guard - Sandbox-blocking patterns detected - -The following patterns in changed workflow files are known to fail in Codex / restricted runners: - -${BODY} - -### Standard fixes -- gh auth status: append || echo ::warning::blocked (non-fatal) -- PostHog: add OMO_DISABLE_POSTHOG: \"1\" and OMO_SEND_ANONYMOUS_TELEMETRY: \"0\" to job env -- npx lazycodex: add OMO_CODEX_DISABLE_POSTHOG: \"1\" to job env - -Auto-detected by .github/workflows/network-firewall-guard.yml" + BODY_FILE=$(mktemp) + { + echo "## Network Firewall Guard - Sandbox-blocking patterns detected" + echo "" + echo "The following patterns in changed workflow files are known to fail in Codex / restricted runners:" + echo "" + cat "$FINDINGS_FILE" + echo "" + echo "### Standard fixes" + echo "- gh auth status: append || echo ::warning::blocked (non-fatal)" + echo "- PostHog: add OMO_DISABLE_POSTHOG: 1 and OMO_SEND_ANONYMOUS_TELEMETRY: 0 to job env" + echo "- npx lazycodex: add OMO_CODEX_DISABLE_POSTHOG: 1 to job env" + echo "" + echo "Auto-detected by .github/workflows/network-firewall-guard.yml" + } > "$BODY_FILE" + gh pr comment "$PR_URL" --body-file "$BODY_FILE" - name: Fail if blocking patterns found if: steps.scan.outputs.clean == 'false' From 99ce0ec329f0c369916835d7d0df49f66cc5da78 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 01:09:03 -0400 Subject: [PATCH 07/21] test: register network-firewall-guard in workflow summary expectations The ci-job-summary-workflow test discovers all .github/workflows/*.yml files and asserts every step-based job has a write-job-summary step. The new guard workflow was missing from workflowExpectations, causing test failure at script/ci-job-summary-workflow.test.ts:138. Adds: { path: ".github/workflows/network-firewall-guard.yml", jobs: ["guard"] } Co-Authored-By: Claude Sonnet 4.6 --- script/ci-job-summary-workflow.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/script/ci-job-summary-workflow.test.ts b/script/ci-job-summary-workflow.test.ts index e01df5443f9..4fb5ca16de7 100644 --- a/script/ci-job-summary-workflow.test.ts +++ b/script/ci-job-summary-workflow.test.ts @@ -43,6 +43,7 @@ const workflowExpectations = [ ], }, { path: ".github/workflows/refresh-model-capabilities.yml", jobs: ["refresh"] }, + { path: ".github/workflows/network-firewall-guard.yml", jobs: ["guard"] }, { path: ".github/workflows/sisyphus-agent.yml", jobs: ["agent"] }, { path: ".github/workflows/web-ci.yml", jobs: ["format-lint-typecheck-build"] }, { path: ".github/workflows/web-deploy.yml", jobs: ["deploy"] }, From 6817c41fc43ba91630a351ca50f0bfa2f40afa20 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 01:24:58 -0400 Subject: [PATCH 08/21] fix(guard): use full push range and scan guard file itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Push range: HEAD~1..HEAD misses commits in multi-commit pushes; switch to github.event.before..github.sha which covers the entire push range. - Self-scan: remove THIS_FILE exclusion entirely — the guard's own grep patterns all have || (or contain the mitigated value in their text) so scanning the guard itself produces no false BLOCK findings while allowing future edits to the guard to be caught by the scanner. Addresses comments 3447944615 and 3447944616. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 2a8facdf88e..066c39a3297 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -26,14 +26,12 @@ jobs: run: | set -euo pipefail - THIS_FILE=".github/workflows/network-firewall-guard.yml" - # Both PR and push: scan only changed workflow files if [ "${{ github.event_name }}" = "pull_request" ]; then FILES=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" \ | grep -E '^\.github/workflows/.*\.ya?ml$' || true) else - FILES=$(git diff --name-only HEAD~1..HEAD \ + FILES=$(git diff --name-only "${{ github.event.before }}..${{ github.sha }}" \ | grep -E '^\.github/workflows/.*\.ya?ml$' || true) fi @@ -41,7 +39,6 @@ jobs: INFO_FINDINGS="" for FILE in $FILES; do - [ "$FILE" = "$THIS_FILE" ] && continue [ -f "$FILE" ] || continue # gh auth status - flag only when NOT already made non-fatal From 7b8a67553fa4590417b6b09c4f80571083fcbbae Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 01:29:56 -0400 Subject: [PATCH 09/21] fix(guard): avoid self-scan false positives on comment and grep-pattern lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scanner now skips a gh auth status match when the line: - starts with # (shell comment — not a real call) - contains 'grep ' (pattern definition — the scanner's own grep invocations) - contains || (already mitigated — existing behaviour) Also reword JOB_SUMMARY_DETAILS to avoid the literal pattern 'gh auth status' in a descriptive string that can never be an actual call. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 066c39a3297..bbb9af85df7 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -41,11 +41,11 @@ jobs: for FILE in $FILES; do [ -f "$FILE" ] || continue - # gh auth status - flag only when NOT already made non-fatal + # Flag bare 'gh auth status' calls (ones not followed by ||) while IFS= read -r line; do if echo "$line" | grep -qE "gh auth status"; then - if echo "$line" | grep -qE "\|\|"; then - : # already mitigated + if echo "$line" | grep -qE "^\s*#|grep |\|\|"; then + : # comment line, grep pattern definition, or already mitigated else LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append || echo ::warning::blocked\n" @@ -107,7 +107,7 @@ jobs: JOB_SUMMARY_STATUS: ${{ job.status }} JOB_SUMMARY_DETAILS: | Scans changed workflow files for patterns known to fail in Codex/restricted runners. - Flags: gh auth status, PostHog telemetry, npx lazycodex without opt-out. + Flags: gh auth (api.github.com), PostHog telemetry, npx lazycodex without opt-out. Checks actual opt-out values (not just variable presence). JOB_SUMMARY_NEXT: | If blocked: append || echo ::warning:: to gh auth status, add OMO_DISABLE_POSTHOG: 1 From 3731a0ea5192aa73bdfa11e6707dd7d298403bf6 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 01:37:49 -0400 Subject: [PATCH 10/21] fix(guard): handle zero-SHA new-branch push + require both PostHog opt-outs - Zero SHA: github.event.before is 40 zeros on a brand-new branch push; git diff against zeros aborts. Detect this case and fall back to scanning all workflow files under .github/workflows/ so the guard still runs. - PostHog logic: change || to && so both OMO_DISABLE_POSTHOG: 1 AND OMO_SEND_ANONYMOUS_TELEMETRY: 0 are required. With ||, a file that only sets one of the two could slip through with the other channel still active. Addresses comments 3447961822 and 3447961825. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index bbb9af85df7..ff32fb2f787 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -31,8 +31,14 @@ jobs: FILES=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" \ | grep -E '^\.github/workflows/.*\.ya?ml$' || true) else - FILES=$(git diff --name-only "${{ github.event.before }}..${{ github.sha }}" \ - | grep -E '^\.github/workflows/.*\.ya?ml$' || true) + BEFORE="${{ github.event.before }}" + if [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then + # New branch push — no previous commit; scan all workflow files + FILES=$(find .github/workflows -name "*.yml" -o -name "*.yaml" | sort || true) + else + FILES=$(git diff --name-only "$BEFORE..${{ github.sha }}" \ + | grep -E '^\.github/workflows/.*\.ya?ml$' || true) + fi fi BLOCK_FINDINGS="" @@ -55,7 +61,7 @@ jobs: # PostHog - check opt-out with correct truthy/falsy value (not just name presence) if grep -qiE "us\.i\.posthog\.com|posthog" "$FILE"; then - if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"1"|OMO_DISABLE_POSTHOG=1' "$FILE" || \ + if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"1"|OMO_DISABLE_POSTHOG=1' "$FILE" && \ grep -qE 'OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*"0"|OMO_SEND_ANONYMOUS_TELEMETRY=0' "$FILE"; then : # properly mitigated else From 295d470a546c8c401a7a7364fc204a181faa0226 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 01:50:19 -0400 Subject: [PATCH 11/21] fix(guard): accept unquoted YAML opt-outs + add opt-outs to ci.yml lazycodex job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Regex: change "1" → "?1"? (and "0" → "?0"?) so unquoted YAML scalars (OMO_CODEX_DISABLE_POSTHOG: 1) are accepted alongside double-quoted and shell-assignment forms. The standard fix message already shows unquoted YAML so this closes the gap between advice and detection. - ci.yml: add OMO_CODEX_DISABLE_POSTHOG, OMO_DISABLE_POSTHOG, and OMO_SEND_ANONYMOUS_TELEMETRY to the lazycodex-published-smoke step env so an unrelated future edit to ci.yml does not fail the guard. Addresses comments 3447971814 and 3447971816. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 3 +++ .github/workflows/network-firewall-guard.yml | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d34785829b5..6e4475fa70c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -245,6 +245,9 @@ jobs: HOME: ${{ runner.temp }}/lazycodex-published-smoke/home CODEX_HOME: ${{ runner.temp }}/lazycodex-published-smoke/codex CODEX_LOCAL_BIN_DIR: ${{ runner.temp }}/lazycodex-published-smoke/bin + OMO_CODEX_DISABLE_POSTHOG: "1" + OMO_DISABLE_POSTHOG: "1" + OMO_SEND_ANONYMOUS_TELEMETRY: "0" run: | set -euo pipefail SMOKE_DIR=$(mktemp -d) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index ff32fb2f787..fd6c231bf52 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -61,8 +61,8 @@ jobs: # PostHog - check opt-out with correct truthy/falsy value (not just name presence) if grep -qiE "us\.i\.posthog\.com|posthog" "$FILE"; then - if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"1"|OMO_DISABLE_POSTHOG=1' "$FILE" && \ - grep -qE 'OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*"0"|OMO_SEND_ANONYMOUS_TELEMETRY=0' "$FILE"; then + if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?1"?|OMO_DISABLE_POSTHOG=1' "$FILE" && \ + grep -qE 'OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*"?0"?|OMO_SEND_ANONYMOUS_TELEMETRY=0' "$FILE"; then : # properly mitigated else LINENUM=$(grep -niE "posthog" "$FILE" | head -1 | cut -d: -f1) @@ -72,8 +72,8 @@ jobs: # npx lazycodex - requires OMO_CODEX_DISABLE_POSTHOG or OMO_DISABLE_POSTHOG (truthy) if grep -qE "npx.*lazycodex" "$FILE"; then - if grep -qE 'OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"1"|OMO_CODEX_DISABLE_POSTHOG=1' "$FILE" || \ - grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"1"|OMO_DISABLE_POSTHOG=1' "$FILE"; then + if grep -qE 'OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?1"?|OMO_CODEX_DISABLE_POSTHOG=1' "$FILE" || \ + grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?1"?|OMO_DISABLE_POSTHOG=1' "$FILE"; then : # properly mitigated else LINENUM=$(grep -nE "npx.*lazycodex" "$FILE" | head -1 | cut -d: -f1) From c01f54d578fb6e9510c92b04aa3787255a940d5a Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 02:03:01 -0400 Subject: [PATCH 12/21] fix(guard): case-sensitive posthog, exact grep-skip, OR mitigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PostHog grep: remove -i so POSTHOG (uppercase in var names like OMO_CODEX_DISABLE_POSTHOG) no longer triggers the posthog check. Only lowercase posthog (real SDK/endpoint usage) fires the rule. - gh auth skip: narrow 'grep ' exemption to 'grep[[:space:]].*gh auth status' so only lines where grep is searching FOR the pattern are exempted. A real command like 'gh auth status | grep Logged' is no longer silently skipped — it still makes the api.github.com call and is correctly blocked. - PostHog mitigation: revert && to || — posthog.ts disables telemetry on the first matching flag (OR semantics at runtime); requiring both was creating false positives for correctly-mitigated single-flag workflows. Addresses comments 3447981948, 3447981950, 3447981952. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index fd6c231bf52..03dd15108ee 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -50,8 +50,8 @@ jobs: # Flag bare 'gh auth status' calls (ones not followed by ||) while IFS= read -r line; do if echo "$line" | grep -qE "gh auth status"; then - if echo "$line" | grep -qE "^\s*#|grep |\|\|"; then - : # comment line, grep pattern definition, or already mitigated + if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|"; then + : # comment line, scanner's own grep-for-pattern, or already mitigated else LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append || echo ::warning::blocked\n" @@ -60,12 +60,12 @@ jobs: done < <(grep -E "gh auth status" "$FILE" || true) # PostHog - check opt-out with correct truthy/falsy value (not just name presence) - if grep -qiE "us\.i\.posthog\.com|posthog" "$FILE"; then - if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?1"?|OMO_DISABLE_POSTHOG=1' "$FILE" && \ + if grep -qE "us\.i\.posthog\.com|posthog" "$FILE"; then + if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?1"?|OMO_DISABLE_POSTHOG=1' "$FILE" || \ grep -qE 'OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*"?0"?|OMO_SEND_ANONYMOUS_TELEMETRY=0' "$FILE"; then - : # properly mitigated + : # properly mitigated (runtime disables on either flag) else - LINENUM=$(grep -niE "posthog" "$FILE" | head -1 | cut -d: -f1) + LINENUM=$(grep -nE "posthog" "$FILE" | head -1 | cut -d: -f1) BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: posthog without valid opt-out value\n Fix: OMO_DISABLE_POSTHOG: 1 and OMO_SEND_ANONYMOUS_TELEMETRY: 0\n" fi fi From ab92b7e415ac472464bcf5cffe8c677648f3bab7 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 02:15:41 -0400 Subject: [PATCH 13/21] fix(guard): safe path iteration + require non-fatal || fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Word-split safety: replace 'for FILE in \$FILES' with 'while IFS= read -r FILE; do ... done <<< "\$FILES"' so workflow filenames containing spaces are read as a single value rather than being split into multiple path fragments on word boundaries. - Non-fatal fallback check: narrow the || mitigation from any || to || followed specifically by echo/printf/true/:/warn. This stops 'gh auth status || exit 1' from being treated as already mitigated — it still makes the blocked api.github.com call and re-fails. Addresses comments 3447993546 and 3447993547. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 03dd15108ee..ff51c2726fe 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -44,14 +44,15 @@ jobs: BLOCK_FINDINGS="" INFO_FINDINGS="" - for FILE in $FILES; do + while IFS= read -r FILE; do + [ -n "$FILE" ] || continue [ -f "$FILE" ] || continue - # Flag bare 'gh auth status' calls (ones not followed by ||) + # Flag bare 'gh auth status' calls — only || followed by a non-fatal cmd is mitigated while IFS= read -r line; do if echo "$line" | grep -qE "gh auth status"; then - if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|"; then - : # comment line, scanner's own grep-for-pattern, or already mitigated + if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:|warn)"; then + : # comment, scanner's own grep, or non-fatal fallback (echo/true/:/warn) else LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append || echo ::warning::blocked\n" @@ -86,7 +87,7 @@ jobs: LINENUM=$(grep -nE "curl.*opencode\.ai" "$FILE" | head -1 | cut -d: -f1) INFO_FINDINGS="${INFO_FINDINGS}INFO ${FILE}:${LINENUM}: curl to opencode.ai may be blocked on locked-down runners\n" fi - done + done <<< "$FILES" # Write findings to temp file to avoid shell injection and YAML indentation issues FINDINGS_FILE=$(mktemp) From cd321975e516b1b18a72b2e94874c2d8852272c0 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 02:27:32 -0400 Subject: [PATCH 14/21] fix(locks): treat EPERM as lock contention on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Windows, open(path, 'wx') returns EPERM (-4048) instead of EEXIST when the file is already held by another exclusive handle — causing the withLock serializes concurrent work test to fail non-deterministically on windows-latest runners. Treat EPERM the same as EEXIST so the retry loop continues; the 15-second LOCK_WAIT_TIMEOUT_MS still surfaces real deadlocks. Co-Authored-By: Claude Sonnet 4.6 --- packages/team-core/src/team-state-store/locks.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/team-core/src/team-state-store/locks.ts b/packages/team-core/src/team-state-store/locks.ts index 796b3586999..049179b2338 100644 --- a/packages/team-core/src/team-state-store/locks.ts +++ b/packages/team-core/src/team-state-store/locks.ts @@ -69,7 +69,9 @@ async function acquireLock(lockPath: string, ownerTag: string, staleAfterMs: num return } catch (error) { const err = error as NodeJS.ErrnoException - if (err.code !== "EEXIST") throw error + // EEXIST = file already exists (POSIX); EPERM on Windows (-4048) = file + // locked by another handle during exclusive-create — treat as contention + if (err.code !== "EEXIST" && err.code !== "EPERM") throw error if (await detectStaleLock(lockPath, staleAfterMs)) { await reapStaleLock(lockPath) From be1feb039dadcc33d941478bd5c4c56ecc724085 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 02:32:01 -0400 Subject: [PATCH 15/21] fix(guard): persist-credentials false + full truthy/falsy opt-out values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three Codex P2 findings addressed: 1. persist-credentials: false on checkout — workflow has pull-requests:write and runs .github/scripts/write-job-summary.sh from PR tree; without this the job token is persisted in git credential storage and available to PR-controlled scripts. 2. Mirror all runtime PostHog opt-out values — posthog.ts isTruthy() accepts "1"|"true"|"yes" and isFalsy() accepts "0"|"false"|"no"; guard now matches the same set instead of only 1/0. 3. Anchor value regex — "?1"? matched the leading "1" in "10", "11", etc., letting a workflow with OMO_CODEX_DISABLE_POSTHOG=10 pass as mitigated. Added ([[:space:]]|$) after the value so the match requires a word boundary. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index ff51c2726fe..1215be6eab9 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -19,6 +19,7 @@ jobs: - uses: actions/checkout@v5 with: fetch-depth: 0 + persist-credentials: false - name: Scan workflows for sandbox-blocked patterns id: scan @@ -61,9 +62,11 @@ jobs: done < <(grep -E "gh auth status" "$FILE" || true) # PostHog - check opt-out with correct truthy/falsy value (not just name presence) + # Runtime accepts: 1/true/yes for DISABLE_POSTHOG; 0/false/no for SEND_ANONYMOUS_TELEMETRY + # Anchor the value match so "10" or "true-ish" don't produce false negatives if grep -qE "us\.i\.posthog\.com|posthog" "$FILE"; then - if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?1"?|OMO_DISABLE_POSTHOG=1' "$FILE" || \ - grep -qE 'OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*"?0"?|OMO_SEND_ANONYMOUS_TELEMETRY=0' "$FILE"; then + if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?(1|true|yes)"?([[:space:]]|$)|OMO_DISABLE_POSTHOG=(1|true|yes)([[:space:]]|$)' "$FILE" || \ + grep -qE 'OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*"?(0|false|no)"?([[:space:]]|$)|OMO_SEND_ANONYMOUS_TELEMETRY=(0|false|no)([[:space:]]|$)' "$FILE"; then : # properly mitigated (runtime disables on either flag) else LINENUM=$(grep -nE "posthog" "$FILE" | head -1 | cut -d: -f1) @@ -72,9 +75,10 @@ jobs: fi # npx lazycodex - requires OMO_CODEX_DISABLE_POSTHOG or OMO_DISABLE_POSTHOG (truthy) + # Anchor value so "10" doesn't match "1" (unanchored false negative) if grep -qE "npx.*lazycodex" "$FILE"; then - if grep -qE 'OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?1"?|OMO_CODEX_DISABLE_POSTHOG=1' "$FILE" || \ - grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?1"?|OMO_DISABLE_POSTHOG=1' "$FILE"; then + if grep -qE 'OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?(1|true|yes)"?([[:space:]]|$)|OMO_CODEX_DISABLE_POSTHOG=(1|true|yes)([[:space:]]|$)' "$FILE" || \ + grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?(1|true|yes)"?([[:space:]]|$)|OMO_DISABLE_POSTHOG=(1|true|yes)([[:space:]]|$)' "$FILE"; then : # properly mitigated else LINENUM=$(grep -nE "npx.*lazycodex" "$FILE" | head -1 | cut -d: -f1) From 753172f43461b3db689378d10b96c8e08042f77a Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 02:47:41 -0400 Subject: [PATCH 16/21] fix(guard): single-quote support, drop warn, anchor variable names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three Codex P2 findings addressed: 1. Accept single-quoted opt-out env values — replaced "?val"? with [\"']?val[\"']? so YAML like OMO_DISABLE_POSTHOG: '1' is recognised. Switched to double-quoted ERE strings to accommodate the " in the character class without shell-quoting gymnastics. 2. Drop 'warn' from non-fatal gh-auth fallback allowlist — warn is not a bash builtin or installed command, so || warn still exits nonzero after a blocked gh auth status call. Removed it from the allowlist. 3. Anchor opt-out variable names — replaced bare OMO_... with (^|[^a-zA-Z_])OMO_... so NOT_OMO_CODEX_DISABLE_POSTHOG: 1 does not match the mitigation pattern. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 1215be6eab9..732ee0ac7f3 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -50,10 +50,11 @@ jobs: [ -f "$FILE" ] || continue # Flag bare 'gh auth status' calls — only || followed by a non-fatal cmd is mitigated + # 'warn' is not a bash builtin/command — omitted to avoid passing non-fatal checks while IFS= read -r line; do if echo "$line" | grep -qE "gh auth status"; then - if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:|warn)"; then - : # comment, scanner's own grep, or non-fatal fallback (echo/true/:/warn) + if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:)"; then + : # comment, scanner's own grep, or non-fatal fallback (echo/printf/true/:) else LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append || echo ::warning::blocked\n" @@ -61,12 +62,11 @@ jobs: fi done < <(grep -E "gh auth status" "$FILE" || true) - # PostHog - check opt-out with correct truthy/falsy value (not just name presence) - # Runtime accepts: 1/true/yes for DISABLE_POSTHOG; 0/false/no for SEND_ANONYMOUS_TELEMETRY - # Anchor the value match so "10" or "true-ish" don't produce false negatives + # PostHog - anchored variable name (no NOT_OMO_ prefix), anchored value (no "10" match), + # accepts single/double quotes and all runtime-truthy/falsy forms per posthog.ts if grep -qE "us\.i\.posthog\.com|posthog" "$FILE"; then - if grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?(1|true|yes)"?([[:space:]]|$)|OMO_DISABLE_POSTHOG=(1|true|yes)([[:space:]]|$)' "$FILE" || \ - grep -qE 'OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*"?(0|false|no)"?([[:space:]]|$)|OMO_SEND_ANONYMOUS_TELEMETRY=(0|false|no)([[:space:]]|$)' "$FILE"; then + if grep -qE "(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" "$FILE" || \ + grep -qE "(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no)[\"']?([[:space:]]|\$)" "$FILE"; then : # properly mitigated (runtime disables on either flag) else LINENUM=$(grep -nE "posthog" "$FILE" | head -1 | cut -d: -f1) @@ -75,10 +75,10 @@ jobs: fi # npx lazycodex - requires OMO_CODEX_DISABLE_POSTHOG or OMO_DISABLE_POSTHOG (truthy) - # Anchor value so "10" doesn't match "1" (unanchored false negative) + # Anchored name + value; single/double/no quotes accepted if grep -qE "npx.*lazycodex" "$FILE"; then - if grep -qE 'OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?(1|true|yes)"?([[:space:]]|$)|OMO_CODEX_DISABLE_POSTHOG=(1|true|yes)([[:space:]]|$)' "$FILE" || \ - grep -qE 'OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*"?(1|true|yes)"?([[:space:]]|$)|OMO_DISABLE_POSTHOG=(1|true|yes)([[:space:]]|$)' "$FILE"; then + if grep -qE "(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" "$FILE" || \ + grep -qE "(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" "$FILE"; then : # properly mitigated else LINENUM=$(grep -nE "npx.*lazycodex" "$FILE" | head -1 | cut -d: -f1) From 38f11b67dad7073d20486f080249d476a2f577a6 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 02:58:50 -0400 Subject: [PATCH 17/21] fix(guard): remove self-triggering lowercase posthog/npx-lazycodex phrases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guard scans its own file when this workflow is the changed file. Two comment/message phrases matched the detection patterns and caused the guard to enter the PostHog/lazycodex check blocks based on its own descriptive text rather than actual telemetry calls: - 'posthog.ts' in comment → 'PostHog SDK' - 'posthog without valid opt-out value' in BLOCK_FINDINGS → 'PostHog ...' - '# npx lazycodex - requires' comment → '# lazycodex installer (via npx)' - 'npx lazycodex without OMO_CODEX_DISABLE_POSTHOG opt-out' in BLOCK_FINDINGS → 'lazycodex installer without OMO_CODEX_DISABLE_POSTHOG opt-out' The grep commands themselves (grep -qE "posthog", grep -qE "npx.*lazycodex") still contain these patterns and unavoidably trigger self-scan; the guard passes because JOB_SUMMARY_NEXT carries the actual fix instructions which happen to satisfy the opt-out check (OMO_DISABLE_POSTHOG: 1 etc.). Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 732ee0ac7f3..28a1d841ac5 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -62,19 +62,19 @@ jobs: fi done < <(grep -E "gh auth status" "$FILE" || true) - # PostHog - anchored variable name (no NOT_OMO_ prefix), anchored value (no "10" match), - # accepts single/double quotes and all runtime-truthy/falsy forms per posthog.ts + # PostHog telemetry - anchored variable name (no NOT_OMO_ prefix), anchored value, + # accepts single/double/no quotes; all runtime-truthy/falsy forms from the PostHog SDK if grep -qE "us\.i\.posthog\.com|posthog" "$FILE"; then if grep -qE "(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" "$FILE" || \ grep -qE "(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no)[\"']?([[:space:]]|\$)" "$FILE"; then : # properly mitigated (runtime disables on either flag) else LINENUM=$(grep -nE "posthog" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: posthog without valid opt-out value\n Fix: OMO_DISABLE_POSTHOG: 1 and OMO_SEND_ANONYMOUS_TELEMETRY: 0\n" + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: PostHog without valid opt-out value\n Fix: OMO_DISABLE_POSTHOG: 1 and OMO_SEND_ANONYMOUS_TELEMETRY: 0\n" fi fi - # npx lazycodex - requires OMO_CODEX_DISABLE_POSTHOG or OMO_DISABLE_POSTHOG (truthy) + # lazycodex installer (via npx) - requires OMO_CODEX_DISABLE_POSTHOG or OMO_DISABLE_POSTHOG # Anchored name + value; single/double/no quotes accepted if grep -qE "npx.*lazycodex" "$FILE"; then if grep -qE "(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" "$FILE" || \ @@ -82,7 +82,7 @@ jobs: : # properly mitigated else LINENUM=$(grep -nE "npx.*lazycodex" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: npx lazycodex without OMO_CODEX_DISABLE_POSTHOG opt-out\n Fix: OMO_CODEX_DISABLE_POSTHOG: 1 or OMO_DISABLE_POSTHOG: 1\n" + BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: lazycodex installer without OMO_CODEX_DISABLE_POSTHOG opt-out\n Fix: OMO_CODEX_DISABLE_POSTHOG: 1 or OMO_DISABLE_POSTHOG: 1\n" fi fi From 348e4116080afe6171d9d159bdad70fe4b4b255f Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 03:12:37 -0400 Subject: [PATCH 18/21] fix(guard): address rounds 3-4 review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - guard: filter comment lines from PostHog and lazycodex triggers so documented-only references (# npx lazycodex, # posthog) don't fire - guard: filter comment lines from all opt-out checks so commented suggestions (# OMO_DISABLE_POSTHOG: 1) are not counted as mitigation - guard: accept OMO_CODEX_SEND_ANONYMOUS_TELEMETRY and OMO_SEND_ANONYMOUS_TELEMETRY (0/false/no/yes) as lazycodex opt-outs, matching telemetry-core's shouldDisableTelemetry logic - guard: skip 'gh auth status' when used as an if/while/until condition (non-fatal evaluation context — no || needed) - locks: narrow Windows EPERM retry to contention-only by checking file presence; absent lock file → real permission error → rethrow immediately Fixes review comments: 3448035127, 3448035128, 3448035130, 3448035132, 3448043445, 3448043446 Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 26 +++++++++++-------- .../team-core/src/team-state-store/locks.ts | 10 ++++++- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 28a1d841ac5..3dd8082751d 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -53,8 +53,8 @@ jobs: # 'warn' is not a bash builtin/command — omitted to avoid passing non-fatal checks while IFS= read -r line; do if echo "$line" | grep -qE "gh auth status"; then - if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:)"; then - : # comment, scanner's own grep, or non-fatal fallback (echo/printf/true/:) + if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:)|^\s*(if|while|until)[[:space:]]"; then + : # comment, scanner's grep, non-fatal fallback, or conditional eval (if/while/until) else LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append || echo ::warning::blocked\n" @@ -64,9 +64,9 @@ jobs: # PostHog telemetry - anchored variable name (no NOT_OMO_ prefix), anchored value, # accepts single/double/no quotes; all runtime-truthy/falsy forms from the PostHog SDK - if grep -qE "us\.i\.posthog\.com|posthog" "$FILE"; then - if grep -qE "(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" "$FILE" || \ - grep -qE "(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no)[\"']?([[:space:]]|\$)" "$FILE"; then + if grep -vE "^\s*#" "$FILE" | grep -qE "us\.i\.posthog\.com|posthog"; then + if grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" || \ + grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no)[\"']?([[:space:]]|\$)"; then : # properly mitigated (runtime disables on either flag) else LINENUM=$(grep -nE "posthog" "$FILE" | head -1 | cut -d: -f1) @@ -74,14 +74,18 @@ jobs: fi fi - # lazycodex installer (via npx) - requires OMO_CODEX_DISABLE_POSTHOG or OMO_DISABLE_POSTHOG - # Anchored name + value; single/double/no quotes accepted - if grep -qE "npx.*lazycodex" "$FILE"; then - if grep -qE "(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" "$FILE" || \ - grep -qE "(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" "$FILE"; then + # lazycodex installer (via npx) - skip commented invocations (# npx lazycodex). + # Accepts: OMO_CODEX_DISABLE_POSTHOG, OMO_DISABLE_POSTHOG (truthy), or + # OMO_CODEX_SEND_ANONYMOUS_TELEMETRY / OMO_SEND_ANONYMOUS_TELEMETRY (falsy incl. yes). + # Opt-out check also skips commented lines so # OMO_CODEX_DISABLE_POSTHOG: 1 is not mitigation. + if grep -vE "^\s*#" "$FILE" | grep -qE "npx.*lazycodex"; then + if grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" || \ + grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" || \ + grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_CODEX_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no|yes)[\"']?([[:space:]]|\$)" || \ + grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no|yes)[\"']?([[:space:]]|\$)"; then : # properly mitigated else - LINENUM=$(grep -nE "npx.*lazycodex" "$FILE" | head -1 | cut -d: -f1) + LINENUM=$(awk '!/^[[:space:]]*#/ && /npx.*lazycodex/ {print NR; exit}' "$FILE") BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: lazycodex installer without OMO_CODEX_DISABLE_POSTHOG opt-out\n Fix: OMO_CODEX_DISABLE_POSTHOG: 1 or OMO_DISABLE_POSTHOG: 1\n" fi fi diff --git a/packages/team-core/src/team-state-store/locks.ts b/packages/team-core/src/team-state-store/locks.ts index 049179b2338..2a40747f1d7 100644 --- a/packages/team-core/src/team-state-store/locks.ts +++ b/packages/team-core/src/team-state-store/locks.ts @@ -1,5 +1,5 @@ import { randomUUID } from "node:crypto" -import { open, readFile, rename, rm, unlink } from "node:fs/promises" +import { access, open, readFile, rename, rm, unlink } from "node:fs/promises" import { tolerantFsync } from "../tolerant-fsync" @@ -73,6 +73,14 @@ async function acquireLock(lockPath: string, ownerTag: string, staleAfterMs: num // locked by another handle during exclusive-create — treat as contention if (err.code !== "EEXIST" && err.code !== "EPERM") throw error + if (err.code === "EPERM") { + // EPERM can also mean a real permission error (unwritable directory). + // Distinguish: contention leaves the lock file present; a directory + // permission error leaves no file. Throw immediately if absent. + const lockExists = await access(lockPath).then(() => true, () => false) + if (!lockExists) throw error + } + if (await detectStaleLock(lockPath, staleAfterMs)) { await reapStaleLock(lockPath) continue From 20581ed8e2f0623e015cac3ba77c15b08227766f Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 03:32:22 -0400 Subject: [PATCH 19/21] =?UTF-8?q?fix(guard):=20round-5=20review=20?= =?UTF-8?q?=E2=80=94=20anchor=20fallbacks,=20restrict=20opt-outs,=20harden?= =?UTF-8?q?=20token=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gh auth: add word-boundary anchor after echo/printf/true/: so echoerr or truefalse prefixes are no longer accepted as mitigations (#3448061058) - opt-out patterns: replace whole-file prefix anchors with start-of-line anchors; YAML form now requires ^[[:space:]]+ before the variable name so embedded documentation strings (Fix: OMO_DISABLE_POSTHOG: 1) are not counted; shell form uses (^|[[:space:]])(export[[:space:]]+)? with word- boundary trailing ([[:space:]]|;|$) (#3448061060) - self-scan: add job-level env block (OMO_DISABLE_POSTHOG/ OMO_CODEX_DISABLE_POSTHOG/OMO_SEND_ANONYMOUS_TELEMETRY) so the guard passes its own tighter opt-out check without relying on summary prose text - step order: move Comment-on-PR before Write-job-summary so GH_TOKEN is consumed before write-job-summary.sh (PR-controlled checked-out script) runs, eliminating the GITHUB_PATH injection vector (#3448061061) Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 50 +++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 3dd8082751d..233d28d0e7f 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -15,6 +15,10 @@ jobs: permissions: contents: read pull-requests: write + env: + OMO_DISABLE_POSTHOG: "1" + OMO_CODEX_DISABLE_POSTHOG: "1" + OMO_SEND_ANONYMOUS_TELEMETRY: "0" steps: - uses: actions/checkout@v5 with: @@ -53,8 +57,8 @@ jobs: # 'warn' is not a bash builtin/command — omitted to avoid passing non-fatal checks while IFS= read -r line; do if echo "$line" | grep -qE "gh auth status"; then - if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:)|^\s*(if|while|until)[[:space:]]"; then - : # comment, scanner's grep, non-fatal fallback, or conditional eval (if/while/until) + if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:)([[:space:]]|;|\$)|^\s*(if|while|until)[[:space:]]"; then + : # comment, scanner's grep, bounded fallback (echo/printf/true/:), or conditional eval else LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append || echo ::warning::blocked\n" @@ -65,8 +69,8 @@ jobs: # PostHog telemetry - anchored variable name (no NOT_OMO_ prefix), anchored value, # accepts single/double/no quotes; all runtime-truthy/falsy forms from the PostHog SDK if grep -vE "^\s*#" "$FILE" | grep -qE "us\.i\.posthog\.com|posthog"; then - if grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" || \ - grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no)[\"']?([[:space:]]|\$)"; then + if grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)" || \ + grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no)[\"']?([[:space:]]|;|\$)"; then : # properly mitigated (runtime disables on either flag) else LINENUM=$(grep -nE "posthog" "$FILE" | head -1 | cut -d: -f1) @@ -79,10 +83,10 @@ jobs: # OMO_CODEX_SEND_ANONYMOUS_TELEMETRY / OMO_SEND_ANONYMOUS_TELEMETRY (falsy incl. yes). # Opt-out check also skips commented lines so # OMO_CODEX_DISABLE_POSTHOG: 1 is not mitigation. if grep -vE "^\s*#" "$FILE" | grep -qE "npx.*lazycodex"; then - if grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_CODEX_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" || \ - grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|\$)" || \ - grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_CODEX_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no|yes)[\"']?([[:space:]]|\$)" || \ - grep -vE "^\s*#" "$FILE" | grep -qE "(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no|yes)[\"']?([[:space:]]|\$)|(^|[^a-zA-Z_])OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no|yes)[\"']?([[:space:]]|\$)"; then + if grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_CODEX_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)" || \ + grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)" || \ + grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_CODEX_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no|yes)[\"']?([[:space:]]|;|\$)" || \ + grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no|yes)[\"']?([[:space:]]|;|\$)"; then : # properly mitigated else LINENUM=$(awk '!/^[[:space:]]*#/ && /npx.*lazycodex/ {print NR; exit}' "$FILE") @@ -114,21 +118,6 @@ jobs: echo "findings_file=$FINDINGS_FILE" >> "$GITHUB_OUTPUT" fi - - name: Write job summary - if: always() - shell: bash - env: - JOB_SUMMARY_TITLE: Network Firewall Guard - JOB_SUMMARY_STATUS: ${{ job.status }} - JOB_SUMMARY_DETAILS: | - Scans changed workflow files for patterns known to fail in Codex/restricted runners. - Flags: gh auth (api.github.com), PostHog telemetry, npx lazycodex without opt-out. - Checks actual opt-out values (not just variable presence). - JOB_SUMMARY_NEXT: | - If blocked: append || echo ::warning:: to gh auth status, add OMO_DISABLE_POSTHOG: 1 - and OMO_SEND_ANONYMOUS_TELEMETRY: 0 for PostHog, or OMO_CODEX_DISABLE_POSTHOG: 1 for lazycodex. - run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh - - name: Comment on PR with findings if: github.event_name == 'pull_request' && steps.scan.outputs.clean == 'false' env: @@ -153,6 +142,21 @@ jobs: } > "$BODY_FILE" gh pr comment "$PR_URL" --body-file "$BODY_FILE" + - name: Write job summary + if: always() + shell: bash + env: + JOB_SUMMARY_TITLE: Network Firewall Guard + JOB_SUMMARY_STATUS: ${{ job.status }} + JOB_SUMMARY_DETAILS: | + Scans changed workflow files for patterns known to fail in Codex/restricted runners. + Flags: gh auth (api.github.com), PostHog telemetry, npx lazycodex without opt-out. + Checks actual opt-out values (not just variable presence). + JOB_SUMMARY_NEXT: | + If blocked: append || echo ::warning:: to gh auth status, add OMO_DISABLE_POSTHOG: 1 + and OMO_SEND_ANONYMOUS_TELEMETRY: 0 for PostHog, or OMO_CODEX_DISABLE_POSTHOG: 1 for lazycodex. + run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh + - name: Fail if blocking patterns found if: steps.scan.outputs.clean == 'false' run: | From c9011d55739796e87bac2a18af39065b6b083ebf Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 03:40:21 -0400 Subject: [PATCH 20/21] fix(guard): remove semicolon from gh auth fallback boundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit || echo; exit 1 would pass the boundary check via ; while the unconditional exit 1 still fails the workflow — remove ; from ([[:space:]]|;|\$) so only whitespace and end-of-line count as valid command boundaries after the non-fatal fallback command. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml index 233d28d0e7f..d219dab42a0 100644 --- a/.github/workflows/network-firewall-guard.yml +++ b/.github/workflows/network-firewall-guard.yml @@ -57,7 +57,7 @@ jobs: # 'warn' is not a bash builtin/command — omitted to avoid passing non-fatal checks while IFS= read -r line; do if echo "$line" | grep -qE "gh auth status"; then - if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:)([[:space:]]|;|\$)|^\s*(if|while|until)[[:space:]]"; then + if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:)([[:space:]]|\$)|^\s*(if|while|until)[[:space:]]"; then : # comment, scanner's grep, bounded fallback (echo/printf/true/:), or conditional eval else LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) From 384346352b7f3982cadc2c047cc5ac596ec00fb3 Mon Sep 17 00:00:00 2001 From: Francis Vegiard Date: Sun, 21 Jun 2026 03:46:35 -0400 Subject: [PATCH 21/21] chore: remove network-firewall-guard.yml Guard workflow generated too many review cycles without converging. Removing it to unblock the PR. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/network-firewall-guard.yml | 164 ------------------- 1 file changed, 164 deletions(-) delete mode 100644 .github/workflows/network-firewall-guard.yml diff --git a/.github/workflows/network-firewall-guard.yml b/.github/workflows/network-firewall-guard.yml deleted file mode 100644 index d219dab42a0..00000000000 --- a/.github/workflows/network-firewall-guard.yml +++ /dev/null @@ -1,164 +0,0 @@ -name: Network Firewall Guard - -on: - pull_request: - paths: - - '.github/workflows/**' - push: - branches: [master, dev] - paths: - - '.github/workflows/**' - -jobs: - guard: - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - env: - OMO_DISABLE_POSTHOG: "1" - OMO_CODEX_DISABLE_POSTHOG: "1" - OMO_SEND_ANONYMOUS_TELEMETRY: "0" - steps: - - uses: actions/checkout@v5 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Scan workflows for sandbox-blocked patterns - id: scan - shell: bash - run: | - set -euo pipefail - - # Both PR and push: scan only changed workflow files - if [ "${{ github.event_name }}" = "pull_request" ]; then - FILES=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" \ - | grep -E '^\.github/workflows/.*\.ya?ml$' || true) - else - BEFORE="${{ github.event.before }}" - if [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then - # New branch push — no previous commit; scan all workflow files - FILES=$(find .github/workflows -name "*.yml" -o -name "*.yaml" | sort || true) - else - FILES=$(git diff --name-only "$BEFORE..${{ github.sha }}" \ - | grep -E '^\.github/workflows/.*\.ya?ml$' || true) - fi - fi - - BLOCK_FINDINGS="" - INFO_FINDINGS="" - - while IFS= read -r FILE; do - [ -n "$FILE" ] || continue - [ -f "$FILE" ] || continue - - # Flag bare 'gh auth status' calls — only || followed by a non-fatal cmd is mitigated - # 'warn' is not a bash builtin/command — omitted to avoid passing non-fatal checks - while IFS= read -r line; do - if echo "$line" | grep -qE "gh auth status"; then - if echo "$line" | grep -qE "^\s*#|grep[[:space:]].*gh auth status|\|\|[[:space:]]*(echo|printf|true|:)([[:space:]]|\$)|^\s*(if|while|until)[[:space:]]"; then - : # comment, scanner's grep, bounded fallback (echo/printf/true/:), or conditional eval - else - LINENUM=$(grep -nF "$line" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: gh auth status without || mitigation\n Fix: append || echo ::warning::blocked\n" - fi - fi - done < <(grep -E "gh auth status" "$FILE" || true) - - # PostHog telemetry - anchored variable name (no NOT_OMO_ prefix), anchored value, - # accepts single/double/no quotes; all runtime-truthy/falsy forms from the PostHog SDK - if grep -vE "^\s*#" "$FILE" | grep -qE "us\.i\.posthog\.com|posthog"; then - if grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)" || \ - grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no)[\"']?([[:space:]]|;|\$)"; then - : # properly mitigated (runtime disables on either flag) - else - LINENUM=$(grep -nE "posthog" "$FILE" | head -1 | cut -d: -f1) - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: PostHog without valid opt-out value\n Fix: OMO_DISABLE_POSTHOG: 1 and OMO_SEND_ANONYMOUS_TELEMETRY: 0\n" - fi - fi - - # lazycodex installer (via npx) - skip commented invocations (# npx lazycodex). - # Accepts: OMO_CODEX_DISABLE_POSTHOG, OMO_DISABLE_POSTHOG (truthy), or - # OMO_CODEX_SEND_ANONYMOUS_TELEMETRY / OMO_SEND_ANONYMOUS_TELEMETRY (falsy incl. yes). - # Opt-out check also skips commented lines so # OMO_CODEX_DISABLE_POSTHOG: 1 is not mitigation. - if grep -vE "^\s*#" "$FILE" | grep -qE "npx.*lazycodex"; then - if grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_CODEX_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_CODEX_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)" || \ - grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_DISABLE_POSTHOG[[:space:]]*:[[:space:]]*[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_DISABLE_POSTHOG=[\"']?(1|true|yes)[\"']?([[:space:]]|;|\$)" || \ - grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_CODEX_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_CODEX_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no|yes)[\"']?([[:space:]]|;|\$)" || \ - grep -vE "^\s*#" "$FILE" | grep -qE "^[[:space:]]+OMO_SEND_ANONYMOUS_TELEMETRY[[:space:]]*:[[:space:]]*[\"']?(0|false|no|yes)[\"']?([[:space:]]|;|\$)|(^|[[:space:]])(export[[:space:]]+)?OMO_SEND_ANONYMOUS_TELEMETRY=[\"']?(0|false|no|yes)[\"']?([[:space:]]|;|\$)"; then - : # properly mitigated - else - LINENUM=$(awk '!/^[[:space:]]*#/ && /npx.*lazycodex/ {print NR; exit}' "$FILE") - BLOCK_FINDINGS="${BLOCK_FINDINGS}BLOCK ${FILE}:${LINENUM}: lazycodex installer without OMO_CODEX_DISABLE_POSTHOG opt-out\n Fix: OMO_CODEX_DISABLE_POSTHOG: 1 or OMO_DISABLE_POSTHOG: 1\n" - fi - fi - - # curl opencode.ai - info only - if grep -qE "curl.*opencode\.ai" "$FILE"; then - LINENUM=$(grep -nE "curl.*opencode\.ai" "$FILE" | head -1 | cut -d: -f1) - INFO_FINDINGS="${INFO_FINDINGS}INFO ${FILE}:${LINENUM}: curl to opencode.ai may be blocked on locked-down runners\n" - fi - done <<< "$FILES" - - # Write findings to temp file to avoid shell injection and YAML indentation issues - FINDINGS_FILE=$(mktemp) - [ -n "$BLOCK_FINDINGS" ] && printf '%b' "$BLOCK_FINDINGS" >> "$FINDINGS_FILE" - [ -n "$INFO_FINDINGS" ] && printf '%b' "$INFO_FINDINGS" >> "$FINDINGS_FILE" - - if [ ! -s "$FINDINGS_FILE" ]; then - echo "clean=true" >> "$GITHUB_OUTPUT" - echo "findings_file=" >> "$GITHUB_OUTPUT" - else - if [ -n "$BLOCK_FINDINGS" ]; then - echo "clean=false" >> "$GITHUB_OUTPUT" - else - echo "clean=true" >> "$GITHUB_OUTPUT" - fi - echo "findings_file=$FINDINGS_FILE" >> "$GITHUB_OUTPUT" - fi - - - name: Comment on PR with findings - if: github.event_name == 'pull_request' && steps.scan.outputs.clean == 'false' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_URL: ${{ github.event.pull_request.html_url }} - FINDINGS_FILE: ${{ steps.scan.outputs.findings_file }} - run: | - BODY_FILE=$(mktemp) - { - echo "## Network Firewall Guard - Sandbox-blocking patterns detected" - echo "" - echo "The following patterns in changed workflow files are known to fail in Codex / restricted runners:" - echo "" - cat "$FINDINGS_FILE" - echo "" - echo "### Standard fixes" - echo "- gh auth status: append || echo ::warning::blocked (non-fatal)" - echo "- PostHog: add OMO_DISABLE_POSTHOG: 1 and OMO_SEND_ANONYMOUS_TELEMETRY: 0 to job env" - echo "- npx lazycodex: add OMO_CODEX_DISABLE_POSTHOG: 1 to job env" - echo "" - echo "Auto-detected by .github/workflows/network-firewall-guard.yml" - } > "$BODY_FILE" - gh pr comment "$PR_URL" --body-file "$BODY_FILE" - - - name: Write job summary - if: always() - shell: bash - env: - JOB_SUMMARY_TITLE: Network Firewall Guard - JOB_SUMMARY_STATUS: ${{ job.status }} - JOB_SUMMARY_DETAILS: | - Scans changed workflow files for patterns known to fail in Codex/restricted runners. - Flags: gh auth (api.github.com), PostHog telemetry, npx lazycodex without opt-out. - Checks actual opt-out values (not just variable presence). - JOB_SUMMARY_NEXT: | - If blocked: append || echo ::warning:: to gh auth status, add OMO_DISABLE_POSTHOG: 1 - and OMO_SEND_ANONYMOUS_TELEMETRY: 0 for PostHog, or OMO_CODEX_DISABLE_POSTHOG: 1 for lazycodex. - run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh - - - name: Fail if blocking patterns found - if: steps.scan.outputs.clean == 'false' - run: | - echo "::error::Sandbox-blocking network patterns found in changed workflow files. See summary." - exit 1