diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 24c2359584..d9f5f1e19e 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -322,7 +322,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -352,7 +352,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1894,7 +1894,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1933,7 +1933,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Agentic Workflow Audit Agent", experimental: true, supports_tools_allowlist: true, @@ -1950,7 +1950,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1997,7 +1997,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + ''; @@ -5975,9 +5975,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6943,7 +6943,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index 6f1834ae51..85e3ecac1d 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -255,7 +255,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -285,7 +285,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 docker_pull_with_retry mcr.microsoft.com/playwright/mcp - name: Write Safe Outputs Config run: | @@ -1795,7 +1795,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1850,7 +1850,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Blog Auditor", experimental: true, supports_tools_allowlist: true, @@ -1867,7 +1867,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","githubnext.com","www.githubnext.com"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1914,7 +1914,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5472,9 +5472,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6426,7 +6426,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/blog-auditor.md b/.github/workflows/blog-auditor.md index 0ab9c5d4d4..332ce5ca3c 100644 --- a/.github/workflows/blog-auditor.md +++ b/.github/workflows/blog-auditor.md @@ -9,7 +9,7 @@ permissions: pull-requests: read tracker-id: blog-auditor-weekly engine: claude -strict: false +strict: true network: allowed: - defaults diff --git a/.github/workflows/campaign-generator.lock.yml b/.github/workflows/campaign-generator.lock.yml index d8a8899dd6..d72be49b26 100644 --- a/.github/workflows/campaign-generator.lock.yml +++ b/.github/workflows/campaign-generator.lock.yml @@ -287,7 +287,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -331,7 +331,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1887,7 +1887,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "tools": ["*"], "env": { @@ -1936,7 +1936,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", - agent_version: "0.0.369", + agent_version: "0.0.371", workflow_name: "Campaign Generator", experimental: false, supports_tools_allowlist: true, @@ -1953,7 +1953,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2000,7 +2000,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5574,9 +5574,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6561,7 +6561,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -7522,7 +7522,7 @@ jobs: }; EOF_4d21ccbd - cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_006d32d7' + cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_60283df2' // @ts-check /// @@ -7607,11 +7607,12 @@ jobs: * @param {boolean} params.canUpdateStatus - Whether status updates are allowed * @param {boolean} params.canUpdateTitle - Whether title updates are allowed * @param {boolean} params.canUpdateBody - Whether body updates are allowed + * @param {boolean} [params.canUpdateLabels] - Whether label updates are allowed * @param {boolean} params.supportsStatus - Whether this type supports status * @returns {{hasUpdates: boolean, updateData: any, logMessages: string[]}} */ function buildUpdateData(params) { - const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, supportsStatus } = params; + const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, canUpdateLabels, supportsStatus } = params; /** @type {any} */ const updateData = {}; @@ -7661,6 +7662,17 @@ jobs: } } + // Handle labels update + if (canUpdateLabels && item.labels !== undefined) { + if (Array.isArray(item.labels)) { + updateData.labels = item.labels; + hasUpdates = true; + logMessages.push(`Will update labels to: ${item.labels.join(", ")}`); + } else { + logMessages.push("Invalid labels value: must be an array"); + } + } + return { hasUpdates, updateData, logMessages }; } @@ -7705,12 +7717,13 @@ jobs: const canUpdateStatus = process.env.GH_AW_UPDATE_STATUS === "true"; const canUpdateTitle = process.env.GH_AW_UPDATE_TITLE === "true"; const canUpdateBody = process.env.GH_AW_UPDATE_BODY === "true"; + const canUpdateLabels = process.env.GH_AW_UPDATE_LABELS === "true"; core.info(`Update target configuration: ${updateTarget}`); if (supportsStatus) { - core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } else { - core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } // Check context validity @@ -7754,6 +7767,7 @@ jobs: canUpdateStatus, canUpdateTitle, canUpdateBody, + canUpdateLabels, supportsStatus, }); @@ -7875,7 +7889,7 @@ jobs: createGetSummaryLine, }; - EOF_006d32d7 + EOF_60283df2 - name: Assign To Agent id: assign_to_agent if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'assign_to_agent')) diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index 9028f71a96..0993dbd152 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -988,7 +988,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Install awf binary run: | echo "Installing awf from release: v0.7.0" @@ -1026,7 +1026,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -2600,7 +2600,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ] env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] @@ -2623,7 +2623,7 @@ jobs: engine_name: "Codex", model: "gpt-5-mini", version: "", - agent_version: "0.73.0", + agent_version: "0.75.0", workflow_name: "Changeset Generator", experimental: true, supports_tools_allowlist: true, @@ -2640,7 +2640,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","node"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2687,7 +2687,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6244,9 +6244,168 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; + summary += "
\n"; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `${validAllowedRequests} allowed | `; + summary += `${validDeniedRequests} blocked | `; + summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; + if (uniqueDomainCount > 0) { + summary += "| Domain | Allowed | Denied |\n"; + summary += "|--------|---------|--------|\n"; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + summary += `| ${domain} | ${stats.allowed} | ${stats.denied} |\n`; + } + } else { + summary += "No firewall activity detected.\n"; + } + summary += "\n
\n\n"; + return summary; + } + const isDirectExecution = typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); + if (isDirectExecution) { + main(); + } + - name: Upload Firewall Logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: firewall-logs-changeset-generator + path: /tmp/gh-aw/sandbox/firewall/logs/ + if-no-files-found: ignore + - name: Parse firewall logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); + } + function main() { + const fs = require("fs"); + const path = require("path"); + try { + const squidLogsDir = `/tmp/gh-aw/sandbox/firewall/logs/`; + if (!fs.existsSync(squidLogsDir)) { + core.info(`No firewall logs directory found at: ${squidLogsDir}`); + return; + } + const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); + if (files.length === 0) { + core.info(`No firewall log files found in: ${squidLogsDir}`); + return; + } + core.info(`Found ${files.length} firewall log file(s)`); + let totalRequests = 0; + let allowedRequests = 0; + let deniedRequests = 0; + const allowedDomains = new Set(); + const deniedDomains = new Set(); + const requestsByDomain = new Map(); + for (const file of files) { + const filePath = path.join(squidLogsDir, file); + core.info(`Parsing firewall log: ${file}`); + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n").filter(line => line.trim()); + for (const line of lines) { + const entry = parseFirewallLogLine(line); + if (!entry) { + continue; + } + totalRequests++; + const isAllowed = isRequestAllowed(entry.decision, entry.status); + if (isAllowed) { + allowedRequests++; + allowedDomains.add(entry.domain); + } else { + deniedRequests++; + deniedDomains.add(entry.domain); + } + if (!requestsByDomain.has(entry.domain)) { + requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); + } + const domainStats = requestsByDomain.get(entry.domain); + if (isAllowed) { + domainStats.allowed++; + } else { + domainStats.denied++; + } + } + } + const summary = generateFirewallSummary({ + totalRequests, + allowedRequests, + deniedRequests, + allowedDomains: Array.from(allowedDomains).sort(), + deniedDomains: Array.from(deniedDomains).sort(), + requestsByDomain, + }); + core.summary.addRaw(summary).write(); + core.info("Firewall log summary generated successfully"); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } + function parseFirewallLogLine(line) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) { + return null; + } + const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!fields || fields.length < 10) { + return null; + } + const timestamp = fields[0]; + if (!/^\d+(\.\d+)?$/.test(timestamp)) { + return null; + } + return { + timestamp, + clientIpPort: fields[1], + domain: fields[2], + destIpPort: fields[3], + proto: fields[4], + method: fields[5], + status: fields[6], + decision: fields[7], + url: fields[8], + userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", + }; + } + function isRequestAllowed(decision, status) { + const statusCode = parseInt(status, 10); + if (statusCode === 200 || statusCode === 206 || statusCode === 304) { + return true; + } + if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { + return true; + } + if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { + return false; + } + return false; + } + function generateFirewallSummary(analysis) { + const { totalRequests, requestsByDomain } = analysis; + const validDomains = Array.from(requestsByDomain.keys()) + .filter(domain => domain !== "-") + .sort(); + const uniqueDomainCount = validDomains.length; + let validAllowedRequests = 0; + let validDeniedRequests = 0; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + validAllowedRequests += stats.allowed; + validDeniedRequests += stats.denied; + } + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -8269,7 +8428,7 @@ jobs: }; EOF_d0693c3b - cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_006d32d7' + cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_60283df2' // @ts-check /// @@ -8354,11 +8513,12 @@ jobs: * @param {boolean} params.canUpdateStatus - Whether status updates are allowed * @param {boolean} params.canUpdateTitle - Whether title updates are allowed * @param {boolean} params.canUpdateBody - Whether body updates are allowed + * @param {boolean} [params.canUpdateLabels] - Whether label updates are allowed * @param {boolean} params.supportsStatus - Whether this type supports status * @returns {{hasUpdates: boolean, updateData: any, logMessages: string[]}} */ function buildUpdateData(params) { - const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, supportsStatus } = params; + const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, canUpdateLabels, supportsStatus } = params; /** @type {any} */ const updateData = {}; @@ -8408,6 +8568,17 @@ jobs: } } + // Handle labels update + if (canUpdateLabels && item.labels !== undefined) { + if (Array.isArray(item.labels)) { + updateData.labels = item.labels; + hasUpdates = true; + logMessages.push(`Will update labels to: ${item.labels.join(", ")}`); + } else { + logMessages.push("Invalid labels value: must be an array"); + } + } + return { hasUpdates, updateData, logMessages }; } @@ -8452,12 +8623,13 @@ jobs: const canUpdateStatus = process.env.GH_AW_UPDATE_STATUS === "true"; const canUpdateTitle = process.env.GH_AW_UPDATE_TITLE === "true"; const canUpdateBody = process.env.GH_AW_UPDATE_BODY === "true"; + const canUpdateLabels = process.env.GH_AW_UPDATE_LABELS === "true"; core.info(`Update target configuration: ${updateTarget}`); if (supportsStatus) { - core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } else { - core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } // Check context validity @@ -8501,6 +8673,7 @@ jobs: canUpdateStatus, canUpdateTitle, canUpdateBody, + canUpdateLabels, supportsStatus, }); @@ -8622,7 +8795,7 @@ jobs: createGetSummaryLine, }; - EOF_006d32d7 + EOF_60283df2 - name: Update Pull Request id: update_pull_request if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'update_pull_request')) diff --git a/.github/workflows/changeset.md b/.github/workflows/changeset.md index 5eb6afd6dd..45cd2ee578 100644 --- a/.github/workflows/changeset.md +++ b/.github/workflows/changeset.md @@ -15,7 +15,7 @@ permissions: engine: id: codex model: gpt-5-mini -strict: false # Required: codex engine doesn't support network firewall +strict: true safe-outputs: push-to-pull-request-branch: commit-title-suffix: " [skip-ci]" diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index edf443f7e6..97567db202 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -273,7 +273,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -303,7 +303,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1833,7 +1833,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1872,7 +1872,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "CLI Version Checker", experimental: true, supports_tools_allowlist: true, @@ -1889,7 +1889,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","node","api.github.com","ghcr.io"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1936,7 +1936,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5474,9 +5474,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6432,7 +6432,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/cli-version-checker.md b/.github/workflows/cli-version-checker.md index 12902e3ded..425b6c2058 100644 --- a/.github/workflows/cli-version-checker.md +++ b/.github/workflows/cli-version-checker.md @@ -7,7 +7,7 @@ permissions: contents: read pull-requests: read issues: read -strict: false +strict: true engine: claude network: allowed: [defaults, node, "api.github.com", "ghcr.io"] diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index 01ffb8598a..3c1e892a22 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1064,7 +1064,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -1094,7 +1094,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 docker_pull_with_retry mcr.microsoft.com/playwright/mcp - name: Write Safe Outputs Config run: | @@ -2654,7 +2654,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -2722,7 +2722,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "/cloclo", experimental: true, supports_tools_allowlist: true, @@ -2739,7 +2739,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2786,7 +2786,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6342,9 +6342,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -7306,7 +7306,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/close-old-discussions.lock.yml b/.github/workflows/close-old-discussions.lock.yml index 36428eb38f..aab970964a 100644 --- a/.github/workflows/close-old-discussions.lock.yml +++ b/.github/workflows/close-old-discussions.lock.yml @@ -279,7 +279,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Install awf binary run: | echo "Installing awf from release: v0.7.0" @@ -317,7 +317,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1839,7 +1839,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests,discussions", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ] env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] @@ -1862,7 +1862,7 @@ jobs: engine_name: "Codex", model: process.env.GH_AW_MODEL_AGENT_CODEX || "", version: "", - agent_version: "0.73.0", + agent_version: "0.75.0", workflow_name: "Close Outdated Discussions", experimental: true, supports_tools_allowlist: true, @@ -1879,7 +1879,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1926,7 +1926,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5426,9 +5426,168 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; + summary += "
\n"; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `${validAllowedRequests} allowed | `; + summary += `${validDeniedRequests} blocked | `; + summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; + if (uniqueDomainCount > 0) { + summary += "| Domain | Allowed | Denied |\n"; + summary += "|--------|---------|--------|\n"; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + summary += `| ${domain} | ${stats.allowed} | ${stats.denied} |\n`; + } + } else { + summary += "No firewall activity detected.\n"; + } + summary += "\n
\n\n"; + return summary; + } + const isDirectExecution = typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); + if (isDirectExecution) { + main(); + } + - name: Upload Firewall Logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: firewall-logs-close-outdated-discussions + path: /tmp/gh-aw/sandbox/firewall/logs/ + if-no-files-found: ignore + - name: Parse firewall logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); + } + function main() { + const fs = require("fs"); + const path = require("path"); + try { + const squidLogsDir = `/tmp/gh-aw/sandbox/firewall/logs/`; + if (!fs.existsSync(squidLogsDir)) { + core.info(`No firewall logs directory found at: ${squidLogsDir}`); + return; + } + const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); + if (files.length === 0) { + core.info(`No firewall log files found in: ${squidLogsDir}`); + return; + } + core.info(`Found ${files.length} firewall log file(s)`); + let totalRequests = 0; + let allowedRequests = 0; + let deniedRequests = 0; + const allowedDomains = new Set(); + const deniedDomains = new Set(); + const requestsByDomain = new Map(); + for (const file of files) { + const filePath = path.join(squidLogsDir, file); + core.info(`Parsing firewall log: ${file}`); + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n").filter(line => line.trim()); + for (const line of lines) { + const entry = parseFirewallLogLine(line); + if (!entry) { + continue; + } + totalRequests++; + const isAllowed = isRequestAllowed(entry.decision, entry.status); + if (isAllowed) { + allowedRequests++; + allowedDomains.add(entry.domain); + } else { + deniedRequests++; + deniedDomains.add(entry.domain); + } + if (!requestsByDomain.has(entry.domain)) { + requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); + } + const domainStats = requestsByDomain.get(entry.domain); + if (isAllowed) { + domainStats.allowed++; + } else { + domainStats.denied++; + } + } + } + const summary = generateFirewallSummary({ + totalRequests, + allowedRequests, + deniedRequests, + allowedDomains: Array.from(allowedDomains).sort(), + deniedDomains: Array.from(deniedDomains).sort(), + requestsByDomain, + }); + core.summary.addRaw(summary).write(); + core.info("Firewall log summary generated successfully"); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } + function parseFirewallLogLine(line) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) { + return null; + } + const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!fields || fields.length < 10) { + return null; + } + const timestamp = fields[0]; + if (!/^\d+(\.\d+)?$/.test(timestamp)) { + return null; + } + return { + timestamp, + clientIpPort: fields[1], + domain: fields[2], + destIpPort: fields[3], + proto: fields[4], + method: fields[5], + status: fields[6], + decision: fields[7], + url: fields[8], + userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", + }; + } + function isRequestAllowed(decision, status) { + const statusCode = parseInt(status, 10); + if (statusCode === 200 || statusCode === 206 || statusCode === 304) { + return true; + } + if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { + return true; + } + if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { + return false; + } + return false; + } + function generateFirewallSummary(analysis) { + const { totalRequests, requestsByDomain } = analysis; + const validDomains = Array.from(requestsByDomain.keys()) + .filter(domain => domain !== "-") + .sort(); + const uniqueDomainCount = validDomains.length; + let validAllowedRequests = 0; + let validDeniedRequests = 0; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + validAllowedRequests += stats.allowed; + validDeniedRequests += stats.denied; + } + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6384,7 +6543,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Run Codex run: | set -o pipefail diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml index f1da96f780..3370366425 100644 --- a/.github/workflows/commit-changes-analyzer.lock.yml +++ b/.github/workflows/commit-changes-analyzer.lock.yml @@ -257,7 +257,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -287,7 +287,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1796,7 +1796,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1835,7 +1835,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Commit Changes Analyzer", experimental: true, supports_tools_allowlist: true, @@ -1852,7 +1852,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1899,7 +1899,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5393,9 +5393,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6344,7 +6344,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 21ed5f0de9..6f25ece959 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -282,7 +282,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -312,7 +312,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1821,7 +1821,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1860,7 +1860,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Copilot Agent PR Analysis", experimental: true, supports_tools_allowlist: true, @@ -1877,7 +1877,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1924,7 +1924,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5784,9 +5784,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6742,7 +6742,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/copilot-agent-analysis.md b/.github/workflows/copilot-agent-analysis.md index 043469677c..f92793dad6 100644 --- a/.github/workflows/copilot-agent-analysis.md +++ b/.github/workflows/copilot-agent-analysis.md @@ -14,7 +14,7 @@ permissions: actions: read engine: claude -strict: false +strict: true network: allowed: diff --git a/.github/workflows/copilot-pr-merged-report.md b/.github/workflows/copilot-pr-merged-report.md index 47e91c309a..60c7b3b4f8 100644 --- a/.github/workflows/copilot-pr-merged-report.md +++ b/.github/workflows/copilot-pr-merged-report.md @@ -14,7 +14,7 @@ permissions: actions: read engine: copilot -strict: false +strict: true tools: github: false diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index 6dd6ea140f..395b9236e7 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -308,7 +308,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -338,7 +338,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1876,7 +1876,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1915,7 +1915,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Copilot Session Insights", experimental: true, supports_tools_allowlist: true, @@ -1932,7 +1932,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github","python"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1979,7 +1979,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6516,9 +6516,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -7481,7 +7481,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/copilot-session-insights.md b/.github/workflows/copilot-session-insights.md index 899e639e76..098a900667 100644 --- a/.github/workflows/copilot-session-insights.md +++ b/.github/workflows/copilot-session-insights.md @@ -14,7 +14,7 @@ permissions: pull-requests: read engine: claude -strict: false +strict: true network: allowed: diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index 3d2cbd817a..ef1f8f397c 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -295,7 +295,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -325,7 +325,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1834,7 +1834,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1873,7 +1873,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Daily Code Metrics and Trend Tracking Agent", experimental: true, supports_tools_allowlist: true, @@ -1890,7 +1890,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1937,7 +1937,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6018,9 +6018,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6979,7 +6979,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index c65b8cb9ac..dc022e7a49 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -266,7 +266,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -296,7 +296,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1815,7 +1815,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1854,7 +1854,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Daily Documentation Updater", experimental: true, supports_tools_allowlist: true, @@ -1871,7 +1871,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1918,7 +1918,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5311,9 +5311,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6279,7 +6279,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/daily-doc-updater.md b/.github/workflows/daily-doc-updater.md index a5893530e5..fb7b281fee 100644 --- a/.github/workflows/daily-doc-updater.md +++ b/.github/workflows/daily-doc-updater.md @@ -14,7 +14,7 @@ permissions: tracker-id: daily-doc-updater engine: claude -strict: false +strict: true network: allowed: diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml index 227bca5d40..0621a54545 100644 --- a/.github/workflows/daily-fact.lock.yml +++ b/.github/workflows/daily-fact.lock.yml @@ -244,7 +244,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Install awf binary run: | echo "Installing awf from release: v0.7.0" @@ -282,7 +282,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1783,7 +1783,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests,discussions", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ] env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] @@ -1806,7 +1806,7 @@ jobs: engine_name: "Codex", model: "gpt-5-mini", version: "", - agent_version: "0.73.0", + agent_version: "0.75.0", workflow_name: "Daily Fact About gh-aw", experimental: true, supports_tools_allowlist: true, @@ -1823,7 +1823,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1870,7 +1870,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5248,9 +5248,168 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; + summary += "
\n"; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `${validAllowedRequests} allowed | `; + summary += `${validDeniedRequests} blocked | `; + summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; + if (uniqueDomainCount > 0) { + summary += "| Domain | Allowed | Denied |\n"; + summary += "|--------|---------|--------|\n"; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + summary += `| ${domain} | ${stats.allowed} | ${stats.denied} |\n`; + } + } else { + summary += "No firewall activity detected.\n"; + } + summary += "\n
\n\n"; + return summary; + } + const isDirectExecution = typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); + if (isDirectExecution) { + main(); + } + - name: Upload Firewall Logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: firewall-logs-daily-fact-about-gh-aw + path: /tmp/gh-aw/sandbox/firewall/logs/ + if-no-files-found: ignore + - name: Parse firewall logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); + } + function main() { + const fs = require("fs"); + const path = require("path"); + try { + const squidLogsDir = `/tmp/gh-aw/sandbox/firewall/logs/`; + if (!fs.existsSync(squidLogsDir)) { + core.info(`No firewall logs directory found at: ${squidLogsDir}`); + return; + } + const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); + if (files.length === 0) { + core.info(`No firewall log files found in: ${squidLogsDir}`); + return; + } + core.info(`Found ${files.length} firewall log file(s)`); + let totalRequests = 0; + let allowedRequests = 0; + let deniedRequests = 0; + const allowedDomains = new Set(); + const deniedDomains = new Set(); + const requestsByDomain = new Map(); + for (const file of files) { + const filePath = path.join(squidLogsDir, file); + core.info(`Parsing firewall log: ${file}`); + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n").filter(line => line.trim()); + for (const line of lines) { + const entry = parseFirewallLogLine(line); + if (!entry) { + continue; + } + totalRequests++; + const isAllowed = isRequestAllowed(entry.decision, entry.status); + if (isAllowed) { + allowedRequests++; + allowedDomains.add(entry.domain); + } else { + deniedRequests++; + deniedDomains.add(entry.domain); + } + if (!requestsByDomain.has(entry.domain)) { + requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); + } + const domainStats = requestsByDomain.get(entry.domain); + if (isAllowed) { + domainStats.allowed++; + } else { + domainStats.denied++; + } + } + } + const summary = generateFirewallSummary({ + totalRequests, + allowedRequests, + deniedRequests, + allowedDomains: Array.from(allowedDomains).sort(), + deniedDomains: Array.from(deniedDomains).sort(), + requestsByDomain, + }); + core.summary.addRaw(summary).write(); + core.info("Firewall log summary generated successfully"); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } + function parseFirewallLogLine(line) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) { + return null; + } + const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!fields || fields.length < 10) { + return null; + } + const timestamp = fields[0]; + if (!/^\d+(\.\d+)?$/.test(timestamp)) { + return null; + } + return { + timestamp, + clientIpPort: fields[1], + domain: fields[2], + destIpPort: fields[3], + proto: fields[4], + method: fields[5], + status: fields[6], + decision: fields[7], + url: fields[8], + userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", + }; + } + function isRequestAllowed(decision, status) { + const statusCode = parseInt(status, 10); + if (statusCode === 200 || statusCode === 206 || statusCode === 304) { + return true; + } + if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { + return true; + } + if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { + return false; + } + return false; + } + function generateFirewallSummary(analysis) { + const { totalRequests, requestsByDomain } = analysis; + const validDomains = Array.from(requestsByDomain.keys()) + .filter(domain => domain !== "-") + .sort(); + const uniqueDomainCount = validDomains.length; + let validAllowedRequests = 0; + let validDeniedRequests = 0; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + validAllowedRequests += stats.allowed; + validDeniedRequests += stats.denied; + } + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6203,7 +6362,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Run Codex run: | set -o pipefail diff --git a/.github/workflows/daily-fact.md b/.github/workflows/daily-fact.md index b99956d354..20539becc4 100644 --- a/.github/workflows/daily-fact.md +++ b/.github/workflows/daily-fact.md @@ -14,7 +14,7 @@ tracker-id: daily-fact-thread engine: id: codex model: gpt-5-mini -strict: false # Required: codex engine doesn't support network firewall +strict: true timeout-minutes: 15 network: diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index a76841daf5..8b36305819 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -301,7 +301,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Install awf binary run: | echo "Installing awf from release: v0.7.0" @@ -339,7 +339,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1942,7 +1942,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests,discussions", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ] env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] @@ -1965,7 +1965,7 @@ jobs: engine_name: "Codex", model: process.env.GH_AW_MODEL_AGENT_CODEX || "", version: "", - agent_version: "0.73.0", + agent_version: "0.75.0", workflow_name: "Daily Issues Report Generator", experimental: true, supports_tools_allowlist: true, @@ -1982,7 +1982,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","python"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2029,7 +2029,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6413,9 +6413,168 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; + summary += "
\n"; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `${validAllowedRequests} allowed | `; + summary += `${validDeniedRequests} blocked | `; + summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; + if (uniqueDomainCount > 0) { + summary += "| Domain | Allowed | Denied |\n"; + summary += "|--------|---------|--------|\n"; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + summary += `| ${domain} | ${stats.allowed} | ${stats.denied} |\n`; + } + } else { + summary += "No firewall activity detected.\n"; + } + summary += "\n
\n\n"; + return summary; + } + const isDirectExecution = typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); + if (isDirectExecution) { + main(); + } + - name: Upload Firewall Logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: firewall-logs-daily-issues-report-generator + path: /tmp/gh-aw/sandbox/firewall/logs/ + if-no-files-found: ignore + - name: Parse firewall logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); + } + function main() { + const fs = require("fs"); + const path = require("path"); + try { + const squidLogsDir = `/tmp/gh-aw/sandbox/firewall/logs/`; + if (!fs.existsSync(squidLogsDir)) { + core.info(`No firewall logs directory found at: ${squidLogsDir}`); + return; + } + const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); + if (files.length === 0) { + core.info(`No firewall log files found in: ${squidLogsDir}`); + return; + } + core.info(`Found ${files.length} firewall log file(s)`); + let totalRequests = 0; + let allowedRequests = 0; + let deniedRequests = 0; + const allowedDomains = new Set(); + const deniedDomains = new Set(); + const requestsByDomain = new Map(); + for (const file of files) { + const filePath = path.join(squidLogsDir, file); + core.info(`Parsing firewall log: ${file}`); + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n").filter(line => line.trim()); + for (const line of lines) { + const entry = parseFirewallLogLine(line); + if (!entry) { + continue; + } + totalRequests++; + const isAllowed = isRequestAllowed(entry.decision, entry.status); + if (isAllowed) { + allowedRequests++; + allowedDomains.add(entry.domain); + } else { + deniedRequests++; + deniedDomains.add(entry.domain); + } + if (!requestsByDomain.has(entry.domain)) { + requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); + } + const domainStats = requestsByDomain.get(entry.domain); + if (isAllowed) { + domainStats.allowed++; + } else { + domainStats.denied++; + } + } + } + const summary = generateFirewallSummary({ + totalRequests, + allowedRequests, + deniedRequests, + allowedDomains: Array.from(allowedDomains).sort(), + deniedDomains: Array.from(deniedDomains).sort(), + requestsByDomain, + }); + core.summary.addRaw(summary).write(); + core.info("Firewall log summary generated successfully"); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } + function parseFirewallLogLine(line) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) { + return null; + } + const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!fields || fields.length < 10) { + return null; + } + const timestamp = fields[0]; + if (!/^\d+(\.\d+)?$/.test(timestamp)) { + return null; + } + return { + timestamp, + clientIpPort: fields[1], + domain: fields[2], + destIpPort: fields[3], + proto: fields[4], + method: fields[5], + status: fields[6], + decision: fields[7], + url: fields[8], + userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", + }; + } + function isRequestAllowed(decision, status) { + const statusCode = parseInt(status, 10); + if (statusCode === 200 || statusCode === 206 || statusCode === 304) { + return true; + } + if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { + return true; + } + if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { + return false; + } + return false; + } + function generateFirewallSummary(analysis) { + const { totalRequests, requestsByDomain } = analysis; + const validDomains = Array.from(requestsByDomain.keys()) + .filter(domain => domain !== "-") + .sort(); + const uniqueDomainCount = validDomains.length; + let validAllowedRequests = 0; + let validDeniedRequests = 0; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + validAllowedRequests += stats.allowed; + validDeniedRequests += stats.denied; + } + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -7381,7 +7540,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Run Codex run: | set -o pipefail diff --git a/.github/workflows/daily-issues-report.md b/.github/workflows/daily-issues-report.md index 2ae5fb4388..204fd04b77 100644 --- a/.github/workflows/daily-issues-report.md +++ b/.github/workflows/daily-issues-report.md @@ -10,7 +10,7 @@ permissions: pull-requests: read discussions: write engine: codex -strict: false +strict: true tracker-id: daily-issues-report tools: github: diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index 1b019ef786..83fb769d91 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -140,7 +140,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - issues: write + issues: read pull-requests: read concurrency: group: "gh-aw-claude-${{ github.workflow }}" @@ -259,7 +259,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -289,7 +289,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 docker_pull_with_retry mcr.microsoft.com/playwright/mcp - name: Write Safe Outputs Config run: | @@ -1849,7 +1849,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1904,7 +1904,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Multi-Device Docs Tester", experimental: true, supports_tools_allowlist: true, @@ -1921,7 +1921,7 @@ jobs: network_mode: "defaults", allowed_domains: ["node"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1968,7 +1968,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5293,9 +5293,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6254,7 +6254,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/daily-multi-device-docs-tester.md b/.github/workflows/daily-multi-device-docs-tester.md index 79cb1cddd9..2c9623f7ff 100644 --- a/.github/workflows/daily-multi-device-docs-tester.md +++ b/.github/workflows/daily-multi-device-docs-tester.md @@ -11,13 +11,13 @@ on: default: 'mobile,tablet,desktop' permissions: contents: read - issues: write + issues: read pull-requests: read tracker-id: daily-multi-device-docs-tester engine: id: claude max-turns: 30 # Prevent runaway token usage -strict: false +strict: true timeout-minutes: 30 tools: playwright: diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index eeb10be1ed..5d09c3ea7b 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -293,7 +293,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Install awf binary run: | echo "Installing awf from release: v0.7.0" @@ -331,7 +331,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -3617,7 +3617,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests,discussions", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ] env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] @@ -3646,7 +3646,7 @@ jobs: engine_name: "Codex", model: process.env.GH_AW_MODEL_AGENT_CODEX || "", version: "", - agent_version: "0.73.0", + agent_version: "0.75.0", workflow_name: "Daily Project Performance Summary Generator (Using Safe Inputs)", experimental: true, supports_tools_allowlist: true, @@ -3663,7 +3663,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -3710,7 +3710,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -7876,9 +7876,168 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; + summary += "
\n"; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `${validAllowedRequests} allowed | `; + summary += `${validDeniedRequests} blocked | `; + summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; + if (uniqueDomainCount > 0) { + summary += "| Domain | Allowed | Denied |\n"; + summary += "|--------|---------|--------|\n"; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + summary += `| ${domain} | ${stats.allowed} | ${stats.denied} |\n`; + } + } else { + summary += "No firewall activity detected.\n"; + } + summary += "\n
\n\n"; + return summary; + } + const isDirectExecution = typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); + if (isDirectExecution) { + main(); + } + - name: Upload Firewall Logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: firewall-logs-daily-project-performance-summary-generator-using-safe-inputs- + path: /tmp/gh-aw/sandbox/firewall/logs/ + if-no-files-found: ignore + - name: Parse firewall logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); + } + function main() { + const fs = require("fs"); + const path = require("path"); + try { + const squidLogsDir = `/tmp/gh-aw/sandbox/firewall/logs/`; + if (!fs.existsSync(squidLogsDir)) { + core.info(`No firewall logs directory found at: ${squidLogsDir}`); + return; + } + const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); + if (files.length === 0) { + core.info(`No firewall log files found in: ${squidLogsDir}`); + return; + } + core.info(`Found ${files.length} firewall log file(s)`); + let totalRequests = 0; + let allowedRequests = 0; + let deniedRequests = 0; + const allowedDomains = new Set(); + const deniedDomains = new Set(); + const requestsByDomain = new Map(); + for (const file of files) { + const filePath = path.join(squidLogsDir, file); + core.info(`Parsing firewall log: ${file}`); + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n").filter(line => line.trim()); + for (const line of lines) { + const entry = parseFirewallLogLine(line); + if (!entry) { + continue; + } + totalRequests++; + const isAllowed = isRequestAllowed(entry.decision, entry.status); + if (isAllowed) { + allowedRequests++; + allowedDomains.add(entry.domain); + } else { + deniedRequests++; + deniedDomains.add(entry.domain); + } + if (!requestsByDomain.has(entry.domain)) { + requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); + } + const domainStats = requestsByDomain.get(entry.domain); + if (isAllowed) { + domainStats.allowed++; + } else { + domainStats.denied++; + } + } + } + const summary = generateFirewallSummary({ + totalRequests, + allowedRequests, + deniedRequests, + allowedDomains: Array.from(allowedDomains).sort(), + deniedDomains: Array.from(deniedDomains).sort(), + requestsByDomain, + }); + core.summary.addRaw(summary).write(); + core.info("Firewall log summary generated successfully"); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } + function parseFirewallLogLine(line) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) { + return null; + } + const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!fields || fields.length < 10) { + return null; + } + const timestamp = fields[0]; + if (!/^\d+(\.\d+)?$/.test(timestamp)) { + return null; + } + return { + timestamp, + clientIpPort: fields[1], + domain: fields[2], + destIpPort: fields[3], + proto: fields[4], + method: fields[5], + status: fields[6], + decision: fields[7], + url: fields[8], + userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", + }; + } + function isRequestAllowed(decision, status) { + const statusCode = parseInt(status, 10); + if (statusCode === 200 || statusCode === 206 || statusCode === 304) { + return true; + } + if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { + return true; + } + if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { + return false; + } + return false; + } + function generateFirewallSummary(analysis) { + const { totalRequests, requestsByDomain } = analysis; + const validDomains = Array.from(requestsByDomain.keys()) + .filter(domain => domain !== "-") + .sort(); + const uniqueDomainCount = validDomains.length; + let validAllowedRequests = 0; + let validDeniedRequests = 0; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + validAllowedRequests += stats.allowed; + validDeniedRequests += stats.denied; + } + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -8844,7 +9003,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Run Codex run: | set -o pipefail diff --git a/.github/workflows/daily-performance-summary.md b/.github/workflows/daily-performance-summary.md index 376aa5f5d8..36728bfa6d 100644 --- a/.github/workflows/daily-performance-summary.md +++ b/.github/workflows/daily-performance-summary.md @@ -10,7 +10,7 @@ permissions: pull-requests: read discussions: write engine: codex -strict: false +strict: true tracker-id: daily-performance-summary tools: github: diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index 9facc42777..cf1867aae9 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -324,7 +324,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Install awf binary run: | echo "Installing awf from release: v0.7.0" @@ -362,7 +362,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1911,7 +1911,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=all", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ] env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] @@ -1934,7 +1934,7 @@ jobs: engine_name: "Codex", model: process.env.GH_AW_MODEL_AGENT_CODEX || "", version: "", - agent_version: "0.73.0", + agent_version: "0.75.0", workflow_name: "DeepReport - Intelligence Gathering Agent", experimental: true, supports_tools_allowlist: true, @@ -1951,7 +1951,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","python","node"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1998,7 +1998,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5894,9 +5894,168 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; + summary += "
\n"; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `${validAllowedRequests} allowed | `; + summary += `${validDeniedRequests} blocked | `; + summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; + if (uniqueDomainCount > 0) { + summary += "| Domain | Allowed | Denied |\n"; + summary += "|--------|---------|--------|\n"; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + summary += `| ${domain} | ${stats.allowed} | ${stats.denied} |\n`; + } + } else { + summary += "No firewall activity detected.\n"; + } + summary += "\n
\n\n"; + return summary; + } + const isDirectExecution = typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); + if (isDirectExecution) { + main(); + } + - name: Upload Firewall Logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: firewall-logs-deepreport-intelligence-gathering-agent + path: /tmp/gh-aw/sandbox/firewall/logs/ + if-no-files-found: ignore + - name: Parse firewall logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); + } + function main() { + const fs = require("fs"); + const path = require("path"); + try { + const squidLogsDir = `/tmp/gh-aw/sandbox/firewall/logs/`; + if (!fs.existsSync(squidLogsDir)) { + core.info(`No firewall logs directory found at: ${squidLogsDir}`); + return; + } + const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); + if (files.length === 0) { + core.info(`No firewall log files found in: ${squidLogsDir}`); + return; + } + core.info(`Found ${files.length} firewall log file(s)`); + let totalRequests = 0; + let allowedRequests = 0; + let deniedRequests = 0; + const allowedDomains = new Set(); + const deniedDomains = new Set(); + const requestsByDomain = new Map(); + for (const file of files) { + const filePath = path.join(squidLogsDir, file); + core.info(`Parsing firewall log: ${file}`); + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n").filter(line => line.trim()); + for (const line of lines) { + const entry = parseFirewallLogLine(line); + if (!entry) { + continue; + } + totalRequests++; + const isAllowed = isRequestAllowed(entry.decision, entry.status); + if (isAllowed) { + allowedRequests++; + allowedDomains.add(entry.domain); + } else { + deniedRequests++; + deniedDomains.add(entry.domain); + } + if (!requestsByDomain.has(entry.domain)) { + requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); + } + const domainStats = requestsByDomain.get(entry.domain); + if (isAllowed) { + domainStats.allowed++; + } else { + domainStats.denied++; + } + } + } + const summary = generateFirewallSummary({ + totalRequests, + allowedRequests, + deniedRequests, + allowedDomains: Array.from(allowedDomains).sort(), + deniedDomains: Array.from(deniedDomains).sort(), + requestsByDomain, + }); + core.summary.addRaw(summary).write(); + core.info("Firewall log summary generated successfully"); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } + function parseFirewallLogLine(line) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) { + return null; + } + const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!fields || fields.length < 10) { + return null; + } + const timestamp = fields[0]; + if (!/^\d+(\.\d+)?$/.test(timestamp)) { + return null; + } + return { + timestamp, + clientIpPort: fields[1], + domain: fields[2], + destIpPort: fields[3], + proto: fields[4], + method: fields[5], + status: fields[6], + decision: fields[7], + url: fields[8], + userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", + }; + } + function isRequestAllowed(decision, status) { + const statusCode = parseInt(status, 10); + if (statusCode === 200 || statusCode === 206 || statusCode === 304) { + return true; + } + if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { + return true; + } + if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { + return false; + } + return false; + } + function generateFirewallSummary(analysis) { + const { totalRequests, requestsByDomain } = analysis; + const validDomains = Array.from(requestsByDomain.keys()) + .filter(domain => domain !== "-") + .sort(); + const uniqueDomainCount = validDomains.length; + let validAllowedRequests = 0; + let validDeniedRequests = 0; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + validAllowedRequests += stats.allowed; + validDeniedRequests += stats.denied; + } + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6872,7 +7031,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Run Codex run: | set -o pipefail diff --git a/.github/workflows/deep-report.md b/.github/workflows/deep-report.md index 577963f4de..4284281c03 100644 --- a/.github/workflows/deep-report.md +++ b/.github/workflows/deep-report.md @@ -18,7 +18,7 @@ permissions: tracker-id: deep-report-intel-agent timeout-minutes: 45 engine: codex -strict: false +strict: true network: allowed: diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index fb168fe885..00a614e66a 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -240,7 +240,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -284,7 +284,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -3157,7 +3157,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=discussions", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "tools": ["*"], "env": { @@ -3220,7 +3220,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", - agent_version: "0.0.369", + agent_version: "0.0.371", workflow_name: "Dev", experimental: false, supports_tools_allowlist: true, @@ -3237,7 +3237,7 @@ jobs: network_mode: "defaults", allowed_domains: ["api.github.com"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -3284,7 +3284,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6797,9 +6797,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -7746,7 +7746,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -8404,7 +8404,7 @@ jobs: }; EOF_4d21ccbd - cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_006d32d7' + cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_60283df2' // @ts-check /// @@ -8489,11 +8489,12 @@ jobs: * @param {boolean} params.canUpdateStatus - Whether status updates are allowed * @param {boolean} params.canUpdateTitle - Whether title updates are allowed * @param {boolean} params.canUpdateBody - Whether body updates are allowed + * @param {boolean} [params.canUpdateLabels] - Whether label updates are allowed * @param {boolean} params.supportsStatus - Whether this type supports status * @returns {{hasUpdates: boolean, updateData: any, logMessages: string[]}} */ function buildUpdateData(params) { - const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, supportsStatus } = params; + const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, canUpdateLabels, supportsStatus } = params; /** @type {any} */ const updateData = {}; @@ -8543,6 +8544,17 @@ jobs: } } + // Handle labels update + if (canUpdateLabels && item.labels !== undefined) { + if (Array.isArray(item.labels)) { + updateData.labels = item.labels; + hasUpdates = true; + logMessages.push(`Will update labels to: ${item.labels.join(", ")}`); + } else { + logMessages.push("Invalid labels value: must be an array"); + } + } + return { hasUpdates, updateData, logMessages }; } @@ -8587,12 +8599,13 @@ jobs: const canUpdateStatus = process.env.GH_AW_UPDATE_STATUS === "true"; const canUpdateTitle = process.env.GH_AW_UPDATE_TITLE === "true"; const canUpdateBody = process.env.GH_AW_UPDATE_BODY === "true"; + const canUpdateLabels = process.env.GH_AW_UPDATE_LABELS === "true"; core.info(`Update target configuration: ${updateTarget}`); if (supportsStatus) { - core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } else { - core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } // Check context validity @@ -8636,6 +8649,7 @@ jobs: canUpdateStatus, canUpdateTitle, canUpdateBody, + canUpdateLabels, supportsStatus, }); @@ -8757,7 +8771,7 @@ jobs: createGetSummaryLine, }; - EOF_006d32d7 + EOF_60283df2 - name: Update Discussion id: update_discussion if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'update_discussion')) @@ -8788,8 +8802,28 @@ jobs: includeOperation: false, }); async function executeDiscussionUpdate(github, context, discussionNumber, updateData) { - const { _operation, _rawBody, ...fieldsToUpdate } = updateData; - const getDiscussionQuery = ` + const { _operation, _rawBody, labels, ...fieldsToUpdate } = updateData; + const shouldUpdateLabels = process.env.GH_AW_UPDATE_LABELS === "true" && labels !== undefined; + const getDiscussionQuery = shouldUpdateLabels + ? ` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + discussion(number: $number) { + id + title + body + url + labels(first: 100) { + nodes { + id + name + } + } + } + } + } + ` + : ` query($owner: String!, $repo: String!, $number: Int!) { repository(owner: $owner, name: $repo) { discussion(number: $number) { @@ -8809,9 +8843,11 @@ jobs: if (!queryResult?.repository?.discussion) { throw new Error(`Discussion #${discussionNumber} not found`); } - const discussionId = queryResult.repository.discussion.id; - if (fieldsToUpdate.title === undefined && fieldsToUpdate.body === undefined) { - throw new Error("At least one field (title or body) must be provided for update"); + const discussion = queryResult.repository.discussion; + const discussionId = discussion.id; + const currentLabels = shouldUpdateLabels ? discussion.labels?.nodes || [] : []; + if (fieldsToUpdate.title === undefined && fieldsToUpdate.body === undefined && !shouldUpdateLabels) { + throw new Error("At least one field (title, body, or labels) must be provided for update"); } if (fieldsToUpdate.body !== undefined) { const workflowName = process.env.GH_AW_WORKFLOW_NAME || "Workflow"; @@ -8826,22 +8862,130 @@ jobs: const footer = generateFooterWithMessages(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber); fieldsToUpdate.body = fieldsToUpdate.body + footer; } - const mutationFields = []; - if (fieldsToUpdate.title !== undefined) { - mutationFields.push("title: $title"); + if (fieldsToUpdate.title !== undefined || fieldsToUpdate.body !== undefined) { + const mutationFields = []; + if (fieldsToUpdate.title !== undefined) { + mutationFields.push("title: $title"); + } + if (fieldsToUpdate.body !== undefined) { + mutationFields.push("body: $body"); + } + const updateDiscussionMutation = ` + mutation($discussionId: ID!${fieldsToUpdate.title !== undefined ? ", $title: String!" : ""}${fieldsToUpdate.body !== undefined ? ", $body: String!" : ""}) { + updateDiscussion(input: { + discussionId: $discussionId + ${mutationFields.join("\n ")} + }) { + discussion { + id + number + title + body + url + } + } + } + `; + const variables = { + discussionId: discussionId, + }; + if (fieldsToUpdate.title !== undefined) { + variables.title = fieldsToUpdate.title; + } + if (fieldsToUpdate.body !== undefined) { + variables.body = fieldsToUpdate.body; + } + const mutationResult = await github.graphql(updateDiscussionMutation, variables); + if (!mutationResult?.updateDiscussion?.discussion) { + throw new Error("Failed to update discussion"); + } } - if (fieldsToUpdate.body !== undefined) { - mutationFields.push("body: $body"); - } - const updateDiscussionMutation = ` - mutation($discussionId: ID!${fieldsToUpdate.title !== undefined ? ", $title: String!" : ""}${fieldsToUpdate.body !== undefined ? ", $body: String!" : ""}) { - updateDiscussion(input: { - discussionId: $discussionId - ${mutationFields.join("\n ")} - }) { - discussion { + if (shouldUpdateLabels && Array.isArray(labels)) { + const repoQuery = ` + query($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + id + labels(first: 100) { + nodes { + id + name + } + } + } + } + `; + const repoResult = await github.graphql(repoQuery, { + owner: context.repo.owner, + repo: context.repo.repo, + }); + if (!repoResult?.repository) { + throw new Error(`Repository ${context.repo.owner}/${context.repo.repo} not found`); + } + const repoLabels = repoResult.repository.labels?.nodes || []; + const labelIds = labels.map(labelName => { + const label = repoLabels.find(l => l.name === labelName); + if (!label) { + throw new Error(`Label "${labelName}" not found in repository`); + } + return label.id; + }); + if (currentLabels.length > 0) { + const removeLabelsMutation = ` + mutation($labelableId: ID!, $labelIds: [ID!]!) { + removeLabelsFromLabelable(input: { + labelableId: $labelableId + labelIds: $labelIds + }) { + clientMutationId + } + } + `; + await github.graphql(removeLabelsMutation, { + labelableId: discussionId, + labelIds: currentLabels.map(l => l.id), + }); + } + if (labelIds.length > 0) { + const addLabelsMutation = ` + mutation($labelableId: ID!, $labelIds: [ID!]!) { + addLabelsToLabelable(input: { + labelableId: $labelableId + labelIds: $labelIds + }) { + clientMutationId + } + } + `; + await github.graphql(addLabelsMutation, { + labelableId: discussionId, + labelIds: labelIds, + }); + } + } + const finalQuery = shouldUpdateLabels + ? ` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + discussion(number: $number) { + id + title + body + url + labels(first: 100) { + nodes { + id + name + } + } + } + } + } + ` + : ` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + discussion(number: $number) { id - number title body url @@ -8849,23 +8993,15 @@ jobs: } } `; - const variables = { - discussionId: discussionId, - }; - if (fieldsToUpdate.title !== undefined) { - variables.title = fieldsToUpdate.title; - } - if (fieldsToUpdate.body !== undefined) { - variables.body = fieldsToUpdate.body; - } - const mutationResult = await github.graphql(updateDiscussionMutation, variables); - if (!mutationResult?.updateDiscussion?.discussion) { - throw new Error("Failed to update discussion"); - } - const discussion = mutationResult.updateDiscussion.discussion; + const finalQueryResult = await github.graphql(finalQuery, { + owner: context.repo.owner, + repo: context.repo.repo, + number: discussionNumber, + }); + const updatedDiscussion = finalQueryResult.repository.discussion; return { - ...discussion, - html_url: discussion.url, + ...updatedDiscussion, + html_url: updatedDiscussion.url, }; } const getSummaryLine = createGetSummaryLine({ diff --git a/.github/workflows/dev.md b/.github/workflows/dev.md index 3301b90108..4df0e5c1da 100644 --- a/.github/workflows/dev.md +++ b/.github/workflows/dev.md @@ -4,7 +4,7 @@ on: name: Dev description: Add a poem to the latest discussion timeout-minutes: 5 -strict: false +strict: true engine: copilot permissions: diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index 63628e9c8d..96741703c5 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -284,7 +284,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -314,7 +314,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1885,7 +1885,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1937,7 +1937,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Developer Documentation Consolidator", experimental: true, supports_tools_allowlist: true, @@ -1954,7 +1954,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2001,7 +2001,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5930,9 +5930,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6895,7 +6895,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/developer-docs-consolidator.md b/.github/workflows/developer-docs-consolidator.md index 4c288cef12..06f224d111 100644 --- a/.github/workflows/developer-docs-consolidator.md +++ b/.github/workflows/developer-docs-consolidator.md @@ -14,7 +14,7 @@ permissions: pull-requests: read engine: claude -strict: false +strict: true network: allowed: diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 0d17c82494..46aece0fda 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -255,7 +255,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Install awf binary run: | echo "Installing awf from release: v0.7.0" @@ -293,7 +293,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1831,7 +1831,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ] env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] @@ -1867,7 +1867,7 @@ jobs: engine_name: "Codex", model: process.env.GH_AW_MODEL_AGENT_CODEX || "", version: "", - agent_version: "0.73.0", + agent_version: "0.75.0", workflow_name: "Duplicate Code Detector", experimental: true, supports_tools_allowlist: true, @@ -1884,7 +1884,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1931,7 +1931,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5481,9 +5481,168 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; + summary += "
\n"; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `${validAllowedRequests} allowed | `; + summary += `${validDeniedRequests} blocked | `; + summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; + if (uniqueDomainCount > 0) { + summary += "| Domain | Allowed | Denied |\n"; + summary += "|--------|---------|--------|\n"; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + summary += `| ${domain} | ${stats.allowed} | ${stats.denied} |\n`; + } + } else { + summary += "No firewall activity detected.\n"; + } + summary += "\n
\n\n"; + return summary; + } + const isDirectExecution = typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); + if (isDirectExecution) { + main(); + } + - name: Upload Firewall Logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: firewall-logs-duplicate-code-detector + path: /tmp/gh-aw/sandbox/firewall/logs/ + if-no-files-found: ignore + - name: Parse firewall logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); + } + function main() { + const fs = require("fs"); + const path = require("path"); + try { + const squidLogsDir = `/tmp/gh-aw/sandbox/firewall/logs/`; + if (!fs.existsSync(squidLogsDir)) { + core.info(`No firewall logs directory found at: ${squidLogsDir}`); + return; + } + const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); + if (files.length === 0) { + core.info(`No firewall log files found in: ${squidLogsDir}`); + return; + } + core.info(`Found ${files.length} firewall log file(s)`); + let totalRequests = 0; + let allowedRequests = 0; + let deniedRequests = 0; + const allowedDomains = new Set(); + const deniedDomains = new Set(); + const requestsByDomain = new Map(); + for (const file of files) { + const filePath = path.join(squidLogsDir, file); + core.info(`Parsing firewall log: ${file}`); + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n").filter(line => line.trim()); + for (const line of lines) { + const entry = parseFirewallLogLine(line); + if (!entry) { + continue; + } + totalRequests++; + const isAllowed = isRequestAllowed(entry.decision, entry.status); + if (isAllowed) { + allowedRequests++; + allowedDomains.add(entry.domain); + } else { + deniedRequests++; + deniedDomains.add(entry.domain); + } + if (!requestsByDomain.has(entry.domain)) { + requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); + } + const domainStats = requestsByDomain.get(entry.domain); + if (isAllowed) { + domainStats.allowed++; + } else { + domainStats.denied++; + } + } + } + const summary = generateFirewallSummary({ + totalRequests, + allowedRequests, + deniedRequests, + allowedDomains: Array.from(allowedDomains).sort(), + deniedDomains: Array.from(deniedDomains).sort(), + requestsByDomain, + }); + core.summary.addRaw(summary).write(); + core.info("Firewall log summary generated successfully"); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } + function parseFirewallLogLine(line) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) { + return null; + } + const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!fields || fields.length < 10) { + return null; + } + const timestamp = fields[0]; + if (!/^\d+(\.\d+)?$/.test(timestamp)) { + return null; + } + return { + timestamp, + clientIpPort: fields[1], + domain: fields[2], + destIpPort: fields[3], + proto: fields[4], + method: fields[5], + status: fields[6], + decision: fields[7], + url: fields[8], + userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", + }; + } + function isRequestAllowed(decision, status) { + const statusCode = parseInt(status, 10); + if (statusCode === 200 || statusCode === 206 || statusCode === 304) { + return true; + } + if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { + return true; + } + if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { + return false; + } + return false; + } + function generateFirewallSummary(analysis) { + const { totalRequests, requestsByDomain } = analysis; + const validDomains = Array.from(requestsByDomain.keys()) + .filter(domain => domain !== "-") + .sort(); + const uniqueDomainCount = validDomains.length; + let validAllowedRequests = 0; + let validDeniedRequests = 0; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + validAllowedRequests += stats.allowed; + validDeniedRequests += stats.denied; + } + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6432,7 +6591,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Run Codex run: | set -o pipefail diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index 5840890694..2b2d3d8735 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -256,7 +256,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -286,7 +286,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Install gh-aw extension env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} @@ -1816,7 +1816,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests,actions", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1855,7 +1855,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Weekly Workflow Analysis", experimental: true, supports_tools_allowlist: true, @@ -1872,7 +1872,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1919,7 +1919,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5142,9 +5142,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6093,7 +6093,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/firewall-escape.md b/.github/workflows/firewall-escape.md index 371aaa527e..f8a9a4b528 100644 --- a/.github/workflows/firewall-escape.md +++ b/.github/workflows/firewall-escape.md @@ -15,7 +15,7 @@ permissions: issues: read pull-requests: read -strict: false +strict: true engine: copilot diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml index 3b51245ff6..8f9edd03f2 100644 --- a/.github/workflows/github-mcp-structural-analysis.lock.yml +++ b/.github/workflows/github-mcp-structural-analysis.lock.yml @@ -300,7 +300,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -330,7 +330,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1868,7 +1868,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=all", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1907,7 +1907,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "GitHub MCP Structural Analysis", experimental: true, supports_tools_allowlist: true, @@ -1924,7 +1924,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","python"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1971,7 +1971,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5870,9 +5870,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6835,7 +6835,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/github-mcp-structural-analysis.md b/.github/workflows/github-mcp-structural-analysis.md index e2b49448be..a857b1ad25 100644 --- a/.github/workflows/github-mcp-structural-analysis.md +++ b/.github/workflows/github-mcp-structural-analysis.md @@ -14,7 +14,7 @@ permissions: repository-projects: read security-events: read engine: claude -strict: false # Required: imports python-dataviz.md which needs network access, and claude doesn't support firewall +strict: true tools: github: mode: local diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index 26ca19b57c..7825fda3a3 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -274,7 +274,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1875,7 +1875,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "GitHub MCP Remote Server Tools Report Generator", experimental: true, supports_tools_allowlist: true, @@ -1892,7 +1892,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1939,7 +1939,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5742,9 +5742,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6707,7 +6707,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index 276db272b2..b8580010ac 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -282,7 +282,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -312,7 +312,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1821,7 +1821,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1873,7 +1873,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Go Fan", experimental: true, supports_tools_allowlist: true, @@ -1890,7 +1890,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github","go"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1937,7 +1937,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5528,9 +5528,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6489,7 +6489,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/go-fan.md b/.github/workflows/go-fan.md index 601fa32786..c18be92af0 100644 --- a/.github/workflows/go-fan.md +++ b/.github/workflows/go-fan.md @@ -48,7 +48,7 @@ tools: - "cat specs/mods/*" timeout-minutes: 30 -strict: false +strict: true --- # Go Fan 🐹 - Daily Go Module Reviewer diff --git a/.github/workflows/go-file-size-reduction-project64.campaign.g.lock.yml b/.github/workflows/go-file-size-reduction-project64.campaign.g.lock.yml index 80819b97ab..1b57eae222 100644 --- a/.github/workflows/go-file-size-reduction-project64.campaign.g.lock.yml +++ b/.github/workflows/go-file-size-reduction-project64.campaign.g.lock.yml @@ -239,7 +239,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -283,7 +283,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1859,7 +1859,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "tools": ["*"], "env": { @@ -1908,7 +1908,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", - agent_version: "0.0.369", + agent_version: "0.0.371", workflow_name: "Go File Size Reduction Campaign (Project 64)", experimental: false, supports_tools_allowlist: true, @@ -1925,7 +1925,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1972,7 +1972,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5631,9 +5631,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6579,7 +6579,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -7721,29 +7721,29 @@ jobs: globalThis.io = io; const { loadAgentOutput } = require('/tmp/gh-aw/scripts/load_agent_output.cjs'); function logGraphQLError(error, operation) { - (core.error(`GraphQL Error during: ${operation}`), core.error(`Message: ${error.message}`)); + (core.info(`GraphQL Error during: ${operation}`), core.info(`Message: ${error.message}`)); const errorList = Array.isArray(error.errors) ? error.errors : [], hasInsufficientScopes = errorList.some(e => e && "INSUFFICIENT_SCOPES" === e.type), hasNotFound = errorList.some(e => e && "NOT_FOUND" === e.type); (hasInsufficientScopes - ? core.error( + ? core.info( "This looks like a token permission problem for Projects v2. The GraphQL fields used by update_project require a token with Projects access (classic PAT: scope 'project'; fine-grained PAT: Organization permission 'Projects' and access to the org). Fix: set safe-outputs.update-project.github-token to a secret PAT that can access the target org project." ) : hasNotFound && /projectV2\b/.test(error.message) && - core.error( + core.info( "GitHub returned NOT_FOUND for ProjectV2. This can mean either: (1) the project number is wrong for Projects v2, (2) the project is a classic Projects board (not Projects v2), or (3) the token does not have access to that org/user project." ), error.errors && - (core.error(`Errors array (${error.errors.length} error(s)):`), + (core.info(`Errors array (${error.errors.length} error(s)):`), error.errors.forEach((err, idx) => { - (core.error(` [${idx + 1}] ${err.message}`), - err.type && core.error(` Type: ${err.type}`), - err.path && core.error(` Path: ${JSON.stringify(err.path)}`), - err.locations && core.error(` Locations: ${JSON.stringify(err.locations)}`)); + (core.info(` [${idx + 1}] ${err.message}`), + err.type && core.info(` Type: ${err.type}`), + err.path && core.info(` Path: ${JSON.stringify(err.path)}`), + err.locations && core.info(` Locations: ${JSON.stringify(err.locations)}`)); })), - error.request && core.error(`Request: ${JSON.stringify(error.request, null, 2)}`), - error.data && core.error(`Response data: ${JSON.stringify(error.data, null, 2)}`)); + error.request && core.info(`Request: ${JSON.stringify(error.request, null, 2)}`), + error.data && core.info(`Response data: ${JSON.stringify(error.data, null, 2)}`)); } function parseProjectInput(projectUrl) { if (!projectUrl || "string" != typeof projectUrl) throw new Error(`Invalid project input: expected string, got ${typeof projectUrl}. The "project" field is required and must be a full GitHub project URL.`); @@ -7917,10 +7917,13 @@ jobs: const contentType = "pull_request" === output.content_type ? "PullRequest" : "issue" === output.content_type || output.issue ? "Issue" : "PullRequest", contentQuery = "Issue" === contentType - ? "query($owner: String!, $repo: String!, $number: Int!) {\n repository(owner: $owner, name: $repo) {\n issue(number: $number) {\n id\n }\n }\n }" - : "query($owner: String!, $repo: String!, $number: Int!) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n id\n }\n }\n }", + ? "query($owner: String!, $repo: String!, $number: Int!) {\n repository(owner: $owner, name: $repo) {\n issue(number: $number) {\n id\n createdAt\n closedAt\n }\n }\n }" + : "query($owner: String!, $repo: String!, $number: Int!) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n id\n createdAt\n closedAt\n }\n }\n }", contentResult = await github.graphql(contentQuery, { owner, repo, number: contentNumber }), - contentId = "Issue" === contentType ? contentResult.repository.issue.id : contentResult.repository.pullRequest.id, + contentData = "Issue" === contentType ? contentResult.repository.issue : contentResult.repository.pullRequest, + contentId = contentData.id, + createdAt = contentData.createdAt, + closedAt = contentData.closedAt, existingItem = await (async function (projectId, contentId) { let hasNextPage = !0, endCursor = null; @@ -7950,14 +7953,29 @@ jobs: core.warning(`Failed to add campaign label: ${labelError.message}`); } } - if (output.fields && Object.keys(output.fields).length > 0) { + const fieldsToUpdate = output.fields ? { ...output.fields } : {}; + if (createdAt) { + const startDate = new Date(createdAt).toISOString().split("T")[0]; + if (!fieldsToUpdate.start_date && !fieldsToUpdate["Start Date"] && !fieldsToUpdate.StartDate) { + fieldsToUpdate.start_date = startDate; + core.info(`Auto-populating Start Date from createdAt: ${startDate}`); + } + } + if (closedAt) { + const endDate = new Date(closedAt).toISOString().split("T")[0]; + if (!fieldsToUpdate.end_date && !fieldsToUpdate["End Date"] && !fieldsToUpdate.EndDate) { + fieldsToUpdate.end_date = endDate; + core.info(`Auto-populating End Date from closedAt: ${endDate}`); + } + } + if (Object.keys(fieldsToUpdate).length > 0) { const projectFields = ( await github.graphql( - "query($projectId: ID!) {\n node(id: $projectId) {\n ... on ProjectV2 {\n fields(first: 20) {\n nodes {\n ... on ProjectV2Field {\n id\n name\n }\n ... on ProjectV2SingleSelectField {\n id\n name\n options {\n id\n name\n color\n }\n }\n }\n }\n }\n }\n }", + "query($projectId: ID!) {\n node(id: $projectId) {\n ... on ProjectV2 {\n fields(first: 20) {\n nodes {\n ... on ProjectV2Field {\n id\n name\n dataType\n }\n ... on ProjectV2SingleSelectField {\n id\n name\n dataType\n options {\n id\n name\n color\n }\n }\n }\n }\n }\n }\n }", { projectId } ) ).node.fields.nodes; - for (const [fieldName, fieldValue] of Object.entries(output.fields)) { + for (const [fieldName, fieldValue] of Object.entries(fieldsToUpdate)) { const normalizedFieldName = fieldName .split(/[\s_-]+/) .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) @@ -7989,7 +8007,9 @@ jobs: core.warning(`Failed to create field "${fieldName}": ${createError.message}`); continue; } - if (field.options) { + if (field.dataType === "DATE") { + valueToSet = { date: String(fieldValue) }; + } else if (field.options) { let option = field.options.find(o => o.name === fieldValue); if (!option) try { diff --git a/.github/workflows/go-file-size-reduction.campaign.g.lock.yml b/.github/workflows/go-file-size-reduction.campaign.g.lock.yml index 85b2277c0a..fa3379507f 100644 --- a/.github/workflows/go-file-size-reduction.campaign.g.lock.yml +++ b/.github/workflows/go-file-size-reduction.campaign.g.lock.yml @@ -239,7 +239,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -283,7 +283,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1859,7 +1859,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "tools": ["*"], "env": { @@ -1908,7 +1908,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", - agent_version: "0.0.369", + agent_version: "0.0.371", workflow_name: "Go File Size Reduction Campaign", experimental: false, supports_tools_allowlist: true, @@ -1925,7 +1925,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1972,7 +1972,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5631,9 +5631,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6579,7 +6579,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -7721,29 +7721,29 @@ jobs: globalThis.io = io; const { loadAgentOutput } = require('/tmp/gh-aw/scripts/load_agent_output.cjs'); function logGraphQLError(error, operation) { - (core.error(`GraphQL Error during: ${operation}`), core.error(`Message: ${error.message}`)); + (core.info(`GraphQL Error during: ${operation}`), core.info(`Message: ${error.message}`)); const errorList = Array.isArray(error.errors) ? error.errors : [], hasInsufficientScopes = errorList.some(e => e && "INSUFFICIENT_SCOPES" === e.type), hasNotFound = errorList.some(e => e && "NOT_FOUND" === e.type); (hasInsufficientScopes - ? core.error( + ? core.info( "This looks like a token permission problem for Projects v2. The GraphQL fields used by update_project require a token with Projects access (classic PAT: scope 'project'; fine-grained PAT: Organization permission 'Projects' and access to the org). Fix: set safe-outputs.update-project.github-token to a secret PAT that can access the target org project." ) : hasNotFound && /projectV2\b/.test(error.message) && - core.error( + core.info( "GitHub returned NOT_FOUND for ProjectV2. This can mean either: (1) the project number is wrong for Projects v2, (2) the project is a classic Projects board (not Projects v2), or (3) the token does not have access to that org/user project." ), error.errors && - (core.error(`Errors array (${error.errors.length} error(s)):`), + (core.info(`Errors array (${error.errors.length} error(s)):`), error.errors.forEach((err, idx) => { - (core.error(` [${idx + 1}] ${err.message}`), - err.type && core.error(` Type: ${err.type}`), - err.path && core.error(` Path: ${JSON.stringify(err.path)}`), - err.locations && core.error(` Locations: ${JSON.stringify(err.locations)}`)); + (core.info(` [${idx + 1}] ${err.message}`), + err.type && core.info(` Type: ${err.type}`), + err.path && core.info(` Path: ${JSON.stringify(err.path)}`), + err.locations && core.info(` Locations: ${JSON.stringify(err.locations)}`)); })), - error.request && core.error(`Request: ${JSON.stringify(error.request, null, 2)}`), - error.data && core.error(`Response data: ${JSON.stringify(error.data, null, 2)}`)); + error.request && core.info(`Request: ${JSON.stringify(error.request, null, 2)}`), + error.data && core.info(`Response data: ${JSON.stringify(error.data, null, 2)}`)); } function parseProjectInput(projectUrl) { if (!projectUrl || "string" != typeof projectUrl) throw new Error(`Invalid project input: expected string, got ${typeof projectUrl}. The "project" field is required and must be a full GitHub project URL.`); @@ -7917,10 +7917,13 @@ jobs: const contentType = "pull_request" === output.content_type ? "PullRequest" : "issue" === output.content_type || output.issue ? "Issue" : "PullRequest", contentQuery = "Issue" === contentType - ? "query($owner: String!, $repo: String!, $number: Int!) {\n repository(owner: $owner, name: $repo) {\n issue(number: $number) {\n id\n }\n }\n }" - : "query($owner: String!, $repo: String!, $number: Int!) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n id\n }\n }\n }", + ? "query($owner: String!, $repo: String!, $number: Int!) {\n repository(owner: $owner, name: $repo) {\n issue(number: $number) {\n id\n createdAt\n closedAt\n }\n }\n }" + : "query($owner: String!, $repo: String!, $number: Int!) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n id\n createdAt\n closedAt\n }\n }\n }", contentResult = await github.graphql(contentQuery, { owner, repo, number: contentNumber }), - contentId = "Issue" === contentType ? contentResult.repository.issue.id : contentResult.repository.pullRequest.id, + contentData = "Issue" === contentType ? contentResult.repository.issue : contentResult.repository.pullRequest, + contentId = contentData.id, + createdAt = contentData.createdAt, + closedAt = contentData.closedAt, existingItem = await (async function (projectId, contentId) { let hasNextPage = !0, endCursor = null; @@ -7950,14 +7953,29 @@ jobs: core.warning(`Failed to add campaign label: ${labelError.message}`); } } - if (output.fields && Object.keys(output.fields).length > 0) { + const fieldsToUpdate = output.fields ? { ...output.fields } : {}; + if (createdAt) { + const startDate = new Date(createdAt).toISOString().split("T")[0]; + if (!fieldsToUpdate.start_date && !fieldsToUpdate["Start Date"] && !fieldsToUpdate.StartDate) { + fieldsToUpdate.start_date = startDate; + core.info(`Auto-populating Start Date from createdAt: ${startDate}`); + } + } + if (closedAt) { + const endDate = new Date(closedAt).toISOString().split("T")[0]; + if (!fieldsToUpdate.end_date && !fieldsToUpdate["End Date"] && !fieldsToUpdate.EndDate) { + fieldsToUpdate.end_date = endDate; + core.info(`Auto-populating End Date from closedAt: ${endDate}`); + } + } + if (Object.keys(fieldsToUpdate).length > 0) { const projectFields = ( await github.graphql( - "query($projectId: ID!) {\n node(id: $projectId) {\n ... on ProjectV2 {\n fields(first: 20) {\n nodes {\n ... on ProjectV2Field {\n id\n name\n }\n ... on ProjectV2SingleSelectField {\n id\n name\n options {\n id\n name\n color\n }\n }\n }\n }\n }\n }\n }", + "query($projectId: ID!) {\n node(id: $projectId) {\n ... on ProjectV2 {\n fields(first: 20) {\n nodes {\n ... on ProjectV2Field {\n id\n name\n dataType\n }\n ... on ProjectV2SingleSelectField {\n id\n name\n dataType\n options {\n id\n name\n color\n }\n }\n }\n }\n }\n }\n }", { projectId } ) ).node.fields.nodes; - for (const [fieldName, fieldValue] of Object.entries(output.fields)) { + for (const [fieldName, fieldValue] of Object.entries(fieldsToUpdate)) { const normalizedFieldName = fieldName .split(/[\s_-]+/) .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) @@ -7989,7 +8007,9 @@ jobs: core.warning(`Failed to create field "${fieldName}": ${createError.message}`); continue; } - if (field.options) { + if (field.dataType === "DATE") { + valueToSet = { date: String(fieldValue) }; + } else if (field.options) { let option = field.options.find(o => o.name === fieldValue); if (!option) try { diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index f6b6b3329d..b5b7f21f4a 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -282,7 +282,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -312,7 +312,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1831,7 +1831,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1870,7 +1870,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Go Logger Enhancement", experimental: true, supports_tools_allowlist: true, @@ -1887,7 +1887,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1934,7 +1934,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5389,9 +5389,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6354,7 +6354,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 3eaf35c0d1..d1298f0c25 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -257,7 +257,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -287,7 +287,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 docker_pull_with_retry mcp/ast-grep:latest - name: Write Safe Outputs Config run: | @@ -1828,7 +1828,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1867,7 +1867,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Go Pattern Detector", experimental: true, supports_tools_allowlist: true, @@ -1884,7 +1884,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1931,7 +1931,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5235,9 +5235,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6220,7 +6220,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index 075bdb6418..c593b90958 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -266,7 +266,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -296,7 +296,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1815,7 +1815,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1854,7 +1854,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Instructions Janitor", experimental: true, supports_tools_allowlist: true, @@ -1871,7 +1871,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1918,7 +1918,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5269,9 +5269,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6234,7 +6234,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/instructions-janitor.md b/.github/workflows/instructions-janitor.md index 68b7fc4590..bee95127b5 100644 --- a/.github/workflows/instructions-janitor.md +++ b/.github/workflows/instructions-janitor.md @@ -11,7 +11,7 @@ permissions: pull-requests: read engine: claude -strict: false +strict: true network: allowed: diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index 1f2e15240d..fe76909399 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -254,7 +254,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Install awf binary run: | echo "Installing awf from release: v0.7.0" @@ -292,7 +292,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1924,7 +1924,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=issues", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ] env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] @@ -1947,7 +1947,7 @@ jobs: engine_name: "Codex", model: process.env.GH_AW_MODEL_AGENT_CODEX || "", version: "", - agent_version: "0.73.0", + agent_version: "0.75.0", workflow_name: "Issue Arborist", experimental: true, supports_tools_allowlist: true, @@ -1964,7 +1964,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2011,7 +2011,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5551,9 +5551,168 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; + summary += "
\n"; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `${validAllowedRequests} allowed | `; + summary += `${validDeniedRequests} blocked | `; + summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; + if (uniqueDomainCount > 0) { + summary += "| Domain | Allowed | Denied |\n"; + summary += "|--------|---------|--------|\n"; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + summary += `| ${domain} | ${stats.allowed} | ${stats.denied} |\n`; + } + } else { + summary += "No firewall activity detected.\n"; + } + summary += "\n
\n\n"; + return summary; + } + const isDirectExecution = typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); + if (isDirectExecution) { + main(); + } + - name: Upload Firewall Logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: firewall-logs-issue-arborist + path: /tmp/gh-aw/sandbox/firewall/logs/ + if-no-files-found: ignore + - name: Parse firewall logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); + } + function main() { + const fs = require("fs"); + const path = require("path"); + try { + const squidLogsDir = `/tmp/gh-aw/sandbox/firewall/logs/`; + if (!fs.existsSync(squidLogsDir)) { + core.info(`No firewall logs directory found at: ${squidLogsDir}`); + return; + } + const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); + if (files.length === 0) { + core.info(`No firewall log files found in: ${squidLogsDir}`); + return; + } + core.info(`Found ${files.length} firewall log file(s)`); + let totalRequests = 0; + let allowedRequests = 0; + let deniedRequests = 0; + const allowedDomains = new Set(); + const deniedDomains = new Set(); + const requestsByDomain = new Map(); + for (const file of files) { + const filePath = path.join(squidLogsDir, file); + core.info(`Parsing firewall log: ${file}`); + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n").filter(line => line.trim()); + for (const line of lines) { + const entry = parseFirewallLogLine(line); + if (!entry) { + continue; + } + totalRequests++; + const isAllowed = isRequestAllowed(entry.decision, entry.status); + if (isAllowed) { + allowedRequests++; + allowedDomains.add(entry.domain); + } else { + deniedRequests++; + deniedDomains.add(entry.domain); + } + if (!requestsByDomain.has(entry.domain)) { + requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); + } + const domainStats = requestsByDomain.get(entry.domain); + if (isAllowed) { + domainStats.allowed++; + } else { + domainStats.denied++; + } + } + } + const summary = generateFirewallSummary({ + totalRequests, + allowedRequests, + deniedRequests, + allowedDomains: Array.from(allowedDomains).sort(), + deniedDomains: Array.from(deniedDomains).sort(), + requestsByDomain, + }); + core.summary.addRaw(summary).write(); + core.info("Firewall log summary generated successfully"); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } + function parseFirewallLogLine(line) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) { + return null; + } + const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!fields || fields.length < 10) { + return null; + } + const timestamp = fields[0]; + if (!/^\d+(\.\d+)?$/.test(timestamp)) { + return null; + } + return { + timestamp, + clientIpPort: fields[1], + domain: fields[2], + destIpPort: fields[3], + proto: fields[4], + method: fields[5], + status: fields[6], + decision: fields[7], + url: fields[8], + userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", + }; + } + function isRequestAllowed(decision, status) { + const statusCode = parseInt(status, 10); + if (statusCode === 200 || statusCode === 206 || statusCode === 304) { + return true; + } + if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { + return true; + } + if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { + return false; + } + return false; + } + function generateFirewallSummary(analysis) { + const { totalRequests, requestsByDomain } = analysis; + const validDomains = Array.from(requestsByDomain.keys()) + .filter(domain => domain !== "-") + .sort(); + const uniqueDomainCount = validDomains.length; + let validAllowedRequests = 0; + let validDeniedRequests = 0; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + validAllowedRequests += stats.allowed; + validDeniedRequests += stats.denied; + } + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6502,7 +6661,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Run Codex run: | set -o pipefail diff --git a/.github/workflows/issue-arborist.md b/.github/workflows/issue-arborist.md index 432be784c8..b4be948a09 100644 --- a/.github/workflows/issue-arborist.md +++ b/.github/workflows/issue-arborist.md @@ -8,7 +8,7 @@ permissions: contents: read issues: read engine: codex -strict: false +strict: true network: allowed: - defaults diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 4106eeeecb..58804996dd 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -270,7 +270,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -300,7 +300,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1809,7 +1809,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1848,7 +1848,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Lockfile Statistics Analysis Agent", experimental: true, supports_tools_allowlist: true, @@ -1865,7 +1865,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1912,7 +1912,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5522,9 +5522,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6480,7 +6480,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index f2aa070bbb..dc89da5530 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -998,7 +998,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -1042,7 +1042,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -3068,7 +3068,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "tools": ["*"], "env": { @@ -3117,7 +3117,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: "gpt-5", version: "", - agent_version: "0.0.369", + agent_version: "0.0.371", workflow_name: "Poem Bot - A Creative Agentic Workflow", experimental: false, supports_tools_allowlist: true, @@ -3134,7 +3134,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -3181,7 +3181,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6834,9 +6834,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -7803,7 +7803,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -10633,7 +10633,7 @@ jobs: }; EOF_4d21ccbd - cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_006d32d7' + cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_60283df2' // @ts-check /// @@ -10718,11 +10718,12 @@ jobs: * @param {boolean} params.canUpdateStatus - Whether status updates are allowed * @param {boolean} params.canUpdateTitle - Whether title updates are allowed * @param {boolean} params.canUpdateBody - Whether body updates are allowed + * @param {boolean} [params.canUpdateLabels] - Whether label updates are allowed * @param {boolean} params.supportsStatus - Whether this type supports status * @returns {{hasUpdates: boolean, updateData: any, logMessages: string[]}} */ function buildUpdateData(params) { - const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, supportsStatus } = params; + const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, canUpdateLabels, supportsStatus } = params; /** @type {any} */ const updateData = {}; @@ -10772,6 +10773,17 @@ jobs: } } + // Handle labels update + if (canUpdateLabels && item.labels !== undefined) { + if (Array.isArray(item.labels)) { + updateData.labels = item.labels; + hasUpdates = true; + logMessages.push(`Will update labels to: ${item.labels.join(", ")}`); + } else { + logMessages.push("Invalid labels value: must be an array"); + } + } + return { hasUpdates, updateData, logMessages }; } @@ -10816,12 +10828,13 @@ jobs: const canUpdateStatus = process.env.GH_AW_UPDATE_STATUS === "true"; const canUpdateTitle = process.env.GH_AW_UPDATE_TITLE === "true"; const canUpdateBody = process.env.GH_AW_UPDATE_BODY === "true"; + const canUpdateLabels = process.env.GH_AW_UPDATE_LABELS === "true"; core.info(`Update target configuration: ${updateTarget}`); if (supportsStatus) { - core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } else { - core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } // Check context validity @@ -10865,6 +10878,7 @@ jobs: canUpdateStatus, canUpdateTitle, canUpdateBody, + canUpdateLabels, supportsStatus, }); @@ -10986,7 +11000,7 @@ jobs: createGetSummaryLine, }; - EOF_006d32d7 + EOF_60283df2 - name: Create Issue id: create_issue if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index 6961e6e7b8..f3b1398ee6 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -343,7 +343,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -373,7 +373,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1886,7 +1886,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=repos,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1925,7 +1925,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Copilot Agent Prompt Clustering Analysis", experimental: true, supports_tools_allowlist: true, @@ -1942,7 +1942,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github","python"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1989,7 +1989,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6157,9 +6157,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -7115,7 +7115,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/prompt-clustering-analysis.md b/.github/workflows/prompt-clustering-analysis.md index 1f7f503774..ab6d62164c 100644 --- a/.github/workflows/prompt-clustering-analysis.md +++ b/.github/workflows/prompt-clustering-analysis.md @@ -11,7 +11,7 @@ permissions: issues: read actions: read engine: claude -strict: false +strict: true network: allowed: diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 03e69ac517..0274cd8d19 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1045,7 +1045,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -1089,7 +1089,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -2653,7 +2653,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests,actions,discussions", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "tools": ["*"], "env": { @@ -2721,7 +2721,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", - agent_version: "0.0.369", + agent_version: "0.0.371", workflow_name: "Q", experimental: false, supports_tools_allowlist: true, @@ -2738,7 +2738,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2785,7 +2785,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6757,9 +6757,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -7718,7 +7718,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index fb5b54e285..3f39e4b75d 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -295,7 +295,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -325,7 +325,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1838,7 +1838,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1877,7 +1877,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Safe Output Health Monitor", experimental: true, supports_tools_allowlist: true, @@ -1894,7 +1894,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1941,7 +1941,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5691,9 +5691,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6649,7 +6649,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index 29bc0a385c..111f714b2e 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -273,7 +273,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1812,7 +1812,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Schema Consistency Checker", experimental: true, supports_tools_allowlist: true, @@ -1829,7 +1829,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1876,7 +1876,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5458,9 +5458,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6416,7 +6416,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 94f08449de..6d9cbef6b3 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1055,7 +1055,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -1085,7 +1085,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 docker_pull_with_retry mcp/arxiv-mcp-server docker_pull_with_retry mcp/context7 - name: Write Safe Outputs Config @@ -2609,7 +2609,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -2663,7 +2663,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Scout", experimental: true, supports_tools_allowlist: true, @@ -2680,7 +2680,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2727,7 +2727,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6317,9 +6317,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -7274,7 +7274,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index 502ead04f2..2dde2170f9 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -274,7 +274,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -304,7 +304,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1823,7 +1823,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,code_security,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1862,7 +1862,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Security Fix PR", experimental: true, supports_tools_allowlist: true, @@ -1879,7 +1879,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1926,7 +1926,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5279,9 +5279,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6244,7 +6244,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index d2bad32fcb..eae41e8b38 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -255,7 +255,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -285,7 +285,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1853,7 +1853,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1892,7 +1892,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Semantic Function Refactoring", experimental: true, supports_tools_allowlist: true, @@ -1909,7 +1909,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1956,7 +1956,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5712,9 +5712,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6663,7 +6663,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 56fb86feda..b752acbc73 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -694,7 +694,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -724,7 +724,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 docker_pull_with_retry mcr.microsoft.com/playwright/mcp - name: Write Safe Outputs Config run: | @@ -2330,7 +2330,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=repos,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -2398,7 +2398,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Smoke Claude", experimental: true, supports_tools_allowlist: true, @@ -2415,7 +2415,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github","playwright"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2462,7 +2462,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5839,9 +5839,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6796,7 +6796,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/smoke-codex-firewall.md b/.github/workflows/smoke-codex-firewall.md index cca5656da5..048b420444 100644 --- a/.github/workflows/smoke-codex-firewall.md +++ b/.github/workflows/smoke-codex-firewall.md @@ -14,7 +14,7 @@ permissions: pull-requests: read name: Smoke Codex Firewall engine: codex -strict: false +strict: true network: allowed: - defaults diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 7dc3b12082..1b4b483a6a 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -682,7 +682,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Install awf binary run: | echo "Installing awf from release: v0.7.0" @@ -720,7 +720,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 docker_pull_with_retry mcr.microsoft.com/playwright/mcp - name: Write Safe Outputs Config run: | @@ -2362,7 +2362,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ] env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] @@ -2414,7 +2414,7 @@ jobs: engine_name: "Codex", model: process.env.GH_AW_MODEL_AGENT_CODEX || "", version: "", - agent_version: "0.73.0", + agent_version: "0.75.0", workflow_name: "Smoke Codex", experimental: true, supports_tools_allowlist: true, @@ -2431,7 +2431,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github","playwright"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2478,7 +2478,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5872,9 +5872,168 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; + summary += "
\n"; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `${validAllowedRequests} allowed | `; + summary += `${validDeniedRequests} blocked | `; + summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; + if (uniqueDomainCount > 0) { + summary += "| Domain | Allowed | Denied |\n"; + summary += "|--------|---------|--------|\n"; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + summary += `| ${domain} | ${stats.allowed} | ${stats.denied} |\n`; + } + } else { + summary += "No firewall activity detected.\n"; + } + summary += "\n
\n\n"; + return summary; + } + const isDirectExecution = typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); + if (isDirectExecution) { + main(); + } + - name: Upload Firewall Logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: firewall-logs-smoke-codex + path: /tmp/gh-aw/sandbox/firewall/logs/ + if-no-files-found: ignore + - name: Parse firewall logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); + } + function main() { + const fs = require("fs"); + const path = require("path"); + try { + const squidLogsDir = `/tmp/gh-aw/sandbox/firewall/logs/`; + if (!fs.existsSync(squidLogsDir)) { + core.info(`No firewall logs directory found at: ${squidLogsDir}`); + return; + } + const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); + if (files.length === 0) { + core.info(`No firewall log files found in: ${squidLogsDir}`); + return; + } + core.info(`Found ${files.length} firewall log file(s)`); + let totalRequests = 0; + let allowedRequests = 0; + let deniedRequests = 0; + const allowedDomains = new Set(); + const deniedDomains = new Set(); + const requestsByDomain = new Map(); + for (const file of files) { + const filePath = path.join(squidLogsDir, file); + core.info(`Parsing firewall log: ${file}`); + const content = fs.readFileSync(filePath, "utf8"); + const lines = content.split("\n").filter(line => line.trim()); + for (const line of lines) { + const entry = parseFirewallLogLine(line); + if (!entry) { + continue; + } + totalRequests++; + const isAllowed = isRequestAllowed(entry.decision, entry.status); + if (isAllowed) { + allowedRequests++; + allowedDomains.add(entry.domain); + } else { + deniedRequests++; + deniedDomains.add(entry.domain); + } + if (!requestsByDomain.has(entry.domain)) { + requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); + } + const domainStats = requestsByDomain.get(entry.domain); + if (isAllowed) { + domainStats.allowed++; + } else { + domainStats.denied++; + } + } + } + const summary = generateFirewallSummary({ + totalRequests, + allowedRequests, + deniedRequests, + allowedDomains: Array.from(allowedDomains).sort(), + deniedDomains: Array.from(deniedDomains).sort(), + requestsByDomain, + }); + core.summary.addRaw(summary).write(); + core.info("Firewall log summary generated successfully"); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } + function parseFirewallLogLine(line) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) { + return null; + } + const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); + if (!fields || fields.length < 10) { + return null; + } + const timestamp = fields[0]; + if (!/^\d+(\.\d+)?$/.test(timestamp)) { + return null; + } + return { + timestamp, + clientIpPort: fields[1], + domain: fields[2], + destIpPort: fields[3], + proto: fields[4], + method: fields[5], + status: fields[6], + decision: fields[7], + url: fields[8], + userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", + }; + } + function isRequestAllowed(decision, status) { + const statusCode = parseInt(status, 10); + if (statusCode === 200 || statusCode === 206 || statusCode === 304) { + return true; + } + if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { + return true; + } + if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { + return false; + } + return false; + } + function generateFirewallSummary(analysis) { + const { totalRequests, requestsByDomain } = analysis; + const validDomains = Array.from(requestsByDomain.keys()) + .filter(domain => domain !== "-") + .sort(); + const uniqueDomainCount = validDomains.length; + let validAllowedRequests = 0; + let validDeniedRequests = 0; + for (const domain of validDomains) { + const stats = requestsByDomain.get(domain); + validAllowedRequests += stats.allowed; + validDeniedRequests += stats.denied; + } + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6829,7 +6988,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex - run: npm install -g @openai/codex@0.73.0 + run: npm install -g @openai/codex@0.75.0 - name: Run Codex run: | set -o pipefail diff --git a/.github/workflows/smoke-codex.md b/.github/workflows/smoke-codex.md index 50c0ced02e..d90060fee8 100644 --- a/.github/workflows/smoke-codex.md +++ b/.github/workflows/smoke-codex.md @@ -14,7 +14,7 @@ permissions: pull-requests: read name: Smoke Codex engine: codex -strict: false +strict: true network: allowed: - defaults diff --git a/.github/workflows/smoke-copilot-playwright.md b/.github/workflows/smoke-copilot-playwright.md index 7b0a1855e5..5fe80c96e3 100644 --- a/.github/workflows/smoke-copilot-playwright.md +++ b/.github/workflows/smoke-copilot-playwright.md @@ -56,7 +56,7 @@ safe-outputs: run-success: "📰 VERDICT: [{workflow_name}]({run_url}) has concluded. All systems operational. This is a developing story. 🎤" run-failure: "📰 DEVELOPING STORY: [{workflow_name}]({run_url}) reports {status}. Our correspondents are investigating the incident..." timeout-minutes: 5 -strict: false +strict: true steps: # Pre-flight Docker container test for Playwright MCP - name: Pre-flight Playwright MCP Test diff --git a/.github/workflows/smoke-copilot.md b/.github/workflows/smoke-copilot.md index a4bca74b7b..e04e4d56b1 100644 --- a/.github/workflows/smoke-copilot.md +++ b/.github/workflows/smoke-copilot.md @@ -40,7 +40,7 @@ safe-outputs: run-success: "📰 VERDICT: [{workflow_name}]({run_url}) has concluded. All systems operational. This is a developing story. 🎤" run-failure: "📰 DEVELOPING STORY: [{workflow_name}]({run_url}) reports {status}. Our correspondents are investigating the incident..." timeout-minutes: 5 -strict: false +strict: true --- # Smoke Test: Copilot Engine Validation diff --git a/.github/workflows/smoke-detector.lock.yml b/.github/workflows/smoke-detector.lock.yml index 3b730a0607..2884b16e34 100644 --- a/.github/workflows/smoke-detector.lock.yml +++ b/.github/workflows/smoke-detector.lock.yml @@ -717,7 +717,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -747,7 +747,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -2317,7 +2317,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests,actions", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -2356,7 +2356,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Smoke Detector - Smoke Test Failure Investigator", experimental: true, supports_tools_allowlist: true, @@ -2373,7 +2373,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2420,7 +2420,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5954,9 +5954,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6913,7 +6913,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/smoke-detector.md b/.github/workflows/smoke-detector.md index e2fa7a658f..205c55bd5a 100644 --- a/.github/workflows/smoke-detector.md +++ b/.github/workflows/smoke-detector.md @@ -56,7 +56,7 @@ tools: cache-memory: true github: toolsets: [default, actions] -strict: false +strict: true --- # Smoke Detector - Smoke Test Failure Investigator diff --git a/.github/workflows/spec-kit-execute.md b/.github/workflows/spec-kit-execute.md index b03a7200b8..be3433f6f5 100644 --- a/.github/workflows/spec-kit-execute.md +++ b/.github/workflows/spec-kit-execute.md @@ -13,7 +13,7 @@ permissions: tracker-id: spec-kit-execute engine: copilot -strict: false +strict: true safe-outputs: create-pull-request: diff --git a/.github/workflows/spec-kit-executor.md b/.github/workflows/spec-kit-executor.md index 2197726f1c..fdf82758b2 100644 --- a/.github/workflows/spec-kit-executor.md +++ b/.github/workflows/spec-kit-executor.md @@ -14,7 +14,7 @@ permissions: tracker-id: spec-kit-executor engine: copilot -strict: false +strict: true network: allowed: diff --git a/.github/workflows/speckit-dispatcher.md b/.github/workflows/speckit-dispatcher.md index d0f74880f4..adb251c301 100644 --- a/.github/workflows/speckit-dispatcher.md +++ b/.github/workflows/speckit-dispatcher.md @@ -13,7 +13,7 @@ permissions: pull-requests: read engine: copilot -strict: false +strict: true imports: - ../agents/speckit-dispatcher.agent.md diff --git a/.github/workflows/stale-repo-identifier.md b/.github/workflows/stale-repo-identifier.md index 790c908ab4..aa73bb9546 100644 --- a/.github/workflows/stale-repo-identifier.md +++ b/.github/workflows/stale-repo-identifier.md @@ -18,7 +18,7 @@ permissions: actions: read engine: copilot -strict: false +strict: true timeout-minutes: 45 imports: diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index d52f9fa2a6..3df98376ed 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -288,7 +288,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -318,7 +318,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1831,7 +1831,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests,actions", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1870,7 +1870,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Static Analysis Report", experimental: true, supports_tools_allowlist: true, @@ -1887,7 +1887,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1934,7 +1934,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5542,9 +5542,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6500,7 +6500,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/sub-issue-closer.lock.yml b/.github/workflows/sub-issue-closer.lock.yml index 4246aed312..62aa303e5f 100644 --- a/.github/workflows/sub-issue-closer.lock.yml +++ b/.github/workflows/sub-issue-closer.lock.yml @@ -239,7 +239,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -283,7 +283,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1837,7 +1837,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=issues", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "tools": ["*"], "env": { @@ -1886,7 +1886,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", - agent_version: "0.0.369", + agent_version: "0.0.371", workflow_name: "Sub-Issue Closer", experimental: false, supports_tools_allowlist: true, @@ -1903,7 +1903,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1950,7 +1950,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5581,9 +5581,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6529,7 +6529,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -7428,7 +7428,7 @@ jobs: }; EOF_4d21ccbd - cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_006d32d7' + cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_60283df2' // @ts-check /// @@ -7513,11 +7513,12 @@ jobs: * @param {boolean} params.canUpdateStatus - Whether status updates are allowed * @param {boolean} params.canUpdateTitle - Whether title updates are allowed * @param {boolean} params.canUpdateBody - Whether body updates are allowed + * @param {boolean} [params.canUpdateLabels] - Whether label updates are allowed * @param {boolean} params.supportsStatus - Whether this type supports status * @returns {{hasUpdates: boolean, updateData: any, logMessages: string[]}} */ function buildUpdateData(params) { - const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, supportsStatus } = params; + const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, canUpdateLabels, supportsStatus } = params; /** @type {any} */ const updateData = {}; @@ -7567,6 +7568,17 @@ jobs: } } + // Handle labels update + if (canUpdateLabels && item.labels !== undefined) { + if (Array.isArray(item.labels)) { + updateData.labels = item.labels; + hasUpdates = true; + logMessages.push(`Will update labels to: ${item.labels.join(", ")}`); + } else { + logMessages.push("Invalid labels value: must be an array"); + } + } + return { hasUpdates, updateData, logMessages }; } @@ -7611,12 +7623,13 @@ jobs: const canUpdateStatus = process.env.GH_AW_UPDATE_STATUS === "true"; const canUpdateTitle = process.env.GH_AW_UPDATE_TITLE === "true"; const canUpdateBody = process.env.GH_AW_UPDATE_BODY === "true"; + const canUpdateLabels = process.env.GH_AW_UPDATE_LABELS === "true"; core.info(`Update target configuration: ${updateTarget}`); if (supportsStatus) { - core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } else { - core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } // Check context validity @@ -7660,6 +7673,7 @@ jobs: canUpdateStatus, canUpdateTitle, canUpdateBody, + canUpdateLabels, supportsStatus, }); @@ -7781,7 +7795,7 @@ jobs: createGetSummaryLine, }; - EOF_006d32d7 + EOF_60283df2 - name: Add Comment id: add_comment if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index 4b39555f3d..94af3201ba 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -266,7 +266,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -296,7 +296,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1805,7 +1805,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -1857,7 +1857,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Typist - Go Type Analysis", experimental: true, supports_tools_allowlist: true, @@ -1874,7 +1874,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -1921,7 +1921,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -5710,9 +5710,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6661,7 +6661,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index def8af6062..aea8b04294 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -701,7 +701,7 @@ jobs: which awf awf --version - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Downloading container images run: | set -e @@ -731,7 +731,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 docker_pull_with_retry mcr.microsoft.com/playwright/mcp - name: Write Safe Outputs Config run: | @@ -2316,7 +2316,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN" @@ -2373,7 +2373,7 @@ jobs: engine_name: "Claude Code", model: process.env.GH_AW_MODEL_AGENT_CLAUDE || "", version: "", - agent_version: "2.0.71", + agent_version: "2.0.73", workflow_name: "Documentation Unbloat", experimental: true, supports_tools_allowlist: true, @@ -2390,7 +2390,7 @@ jobs: network_mode: "defaults", allowed_domains: ["defaults","github"], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2437,7 +2437,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -6036,9 +6036,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -7007,7 +7007,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@2.0.71 + run: npm install -g @anthropic-ai/claude-code@2.0.73 - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/.github/workflows/unbloat-docs.md b/.github/workflows/unbloat-docs.md index 58b87721a5..c7060604b8 100644 --- a/.github/workflows/unbloat-docs.md +++ b/.github/workflows/unbloat-docs.md @@ -19,7 +19,7 @@ permissions: pull-requests: read issues: read -strict: false +strict: true # AI engine configuration engine: diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index 46400e300e..730f37980c 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -287,7 +287,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -331,7 +331,7 @@ jobs: done } - docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.25.0 + docker_pull_with_retry ghcr.io/github/github-mcp-server:v0.26.3 - name: Write Safe Outputs Config run: | mkdir -p /tmp/gh-aw/safeoutputs @@ -1887,7 +1887,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=context,repos,issues,pull_requests", - "ghcr.io/github/github-mcp-server:v0.25.0" + "ghcr.io/github/github-mcp-server:v0.26.3" ], "tools": ["*"], "env": { @@ -1936,7 +1936,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", - agent_version: "0.0.369", + agent_version: "0.0.371", workflow_name: "Workflow Generator", experimental: false, supports_tools_allowlist: true, @@ -1953,7 +1953,7 @@ jobs: network_mode: "defaults", allowed_domains: [], firewall_enabled: true, - firewall_version: "", + awf_version: "v0.7.0", steps: { firewall: "squid" }, @@ -2000,7 +2000,7 @@ jobs: '|----------|-------|\n' + `| Mode | ${awInfo.network_mode || 'defaults'} |\n` + `| Firewall | ${awInfo.firewall_enabled ? '✅ Enabled' : '❌ Disabled'} |\n` + - `| Firewall Version | ${awInfo.firewall_version || '(latest)'} |\n` + + `| Firewall Version | ${awInfo.awf_version || '(latest)'} |\n` + '\n' + (networkDetails ? `##### Allowed Domains\n${networkDetails}\n` : '') + '
'; @@ -2047,26 +2047,39 @@ jobs: This issue has been assigned to an AI agent for workflow design. The agent will: - 1. **Parse the workflow requirements** from the information provided above - 2. **Generate a NEW workflow specification file** (`.md`) with appropriate triggers, tools, and safe outputs - 3. **Create a pull request** with the new workflow file at `.github/workflows/.md` + 1. **Parse the workflow requirements** from the issue form fields above: + - Workflow Name + - Workflow Description + - Additional Context (if provided) - **IMPORTANT**: The agent will create a NEW workflow file following best practices for: - - Security (minimal permissions, safe outputs for write operations) - - Appropriate triggers (issues, pull requests, schedule, workflow_dispatch, etc.) - - Necessary tools and MCP servers - - Network restrictions when needed - - Proper safe output configuration for GitHub operations + 2. **Generate a NEW workflow specification file** (`.md`) with: + - Kebab-case workflow ID derived from the name + - Complete YAML frontmatter (triggers, permissions, engine, tools, safe-outputs) + - Clear prompt body with instructions for the AI agent + - Security best practices applied - The workflow specification will include: - - Frontmatter with triggers, permissions, engine, and tools - - Clear prompt instructions for the AI agent - - Safe output configuration for any write operations - - Security best practices (network restrictions, minimal permissions) + 3. **Compile the workflow** using `gh aw compile ` to generate the `.lock.yml` file + + 4. **Create a pull request** with BOTH files: + - `.github/workflows/.md` (source) + - `.github/workflows/.lock.yml` (compiled) + + **IMPORTANT - Issue Form Mode**: The agent operates in non-interactive mode and will: + - Parse the issue form data directly + - Make intelligent decisions about triggers, tools, and permissions based on the description + - Create a complete, working workflow without back-and-forth conversation + - Follow the same pattern as the campaign generator + + **Best Practices Applied:** + - Security: minimal permissions, safe outputs for write operations + - Triggers: inferred from description (issues, pull_requests, schedule, workflow_dispatch) + - Tools: only include what's needed (github, web-fetch, playwright, etc.) + - Network: restricted to required domains/ecosystems + - Safe Outputs: for all GitHub write operations **Next Steps:** - - The AI agent will analyze your requirements and create a comprehensive workflow - - The workflow will be compiled automatically to ensure validity + - The AI agent will parse your requirements and generate a complete workflow + - Both `.md` and `.lock.yml` files will be included in the PR - Review the generated PR when it's ready - Merge the PR to activate your workflow ``` @@ -5579,9 +5592,9 @@ jobs: validAllowedRequests += stats.allowed; validDeniedRequests += stats.denied; } - let summary = "### 🔥 Firewall Activity\n\n"; + let summary = ""; summary += "
\n"; - summary += `📊 ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; + summary += `sandbox agent: ${totalRequests} request${totalRequests !== 1 ? "s" : ""} | `; summary += `${validAllowedRequests} allowed | `; summary += `${validDeniedRequests} blocked | `; summary += `${uniqueDomainCount} unique domain${uniqueDomainCount !== 1 ? "s" : ""}\n\n`; @@ -6566,7 +6579,7 @@ jobs: curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh # Execute the installer with the specified version - export VERSION=0.0.369 && sudo bash /tmp/copilot-install.sh + export VERSION=0.0.371 && sudo bash /tmp/copilot-install.sh # Cleanup rm -f /tmp/copilot-install.sh @@ -7434,7 +7447,7 @@ jobs: module.exports = { generateStagedPreview }; EOF_8386ee20 - cat > /tmp/gh-aw/scripts/update_context_helpers.cjs << 'EOF_95d23c7d' + cat > /tmp/gh-aw/scripts/update_context_helpers.cjs << 'EOF_4d21ccbd' // @ts-check /// @@ -7498,15 +7511,36 @@ jobs: return undefined; } + /** + * Check if the current context is a valid discussion context + * @param {string} eventName - GitHub event name + * @param {any} _payload - GitHub event payload (unused but kept for interface consistency) + * @returns {boolean} Whether context is valid for discussion updates + */ + function isDiscussionContext(eventName, _payload) { + return eventName === "discussion" || eventName === "discussion_comment"; + } + + /** + * Get discussion number from the context payload + * @param {any} payload - GitHub event payload + * @returns {number|undefined} Discussion number or undefined + */ + function getDiscussionNumber(payload) { + return payload?.discussion?.number; + } + module.exports = { isIssueContext, getIssueNumber, isPRContext, getPRNumber, + isDiscussionContext, + getDiscussionNumber, }; - EOF_95d23c7d - cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_006d32d7' + EOF_4d21ccbd + cat > /tmp/gh-aw/scripts/update_runner.cjs << 'EOF_60283df2' // @ts-check /// @@ -7591,11 +7625,12 @@ jobs: * @param {boolean} params.canUpdateStatus - Whether status updates are allowed * @param {boolean} params.canUpdateTitle - Whether title updates are allowed * @param {boolean} params.canUpdateBody - Whether body updates are allowed + * @param {boolean} [params.canUpdateLabels] - Whether label updates are allowed * @param {boolean} params.supportsStatus - Whether this type supports status * @returns {{hasUpdates: boolean, updateData: any, logMessages: string[]}} */ function buildUpdateData(params) { - const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, supportsStatus } = params; + const { item, canUpdateStatus, canUpdateTitle, canUpdateBody, canUpdateLabels, supportsStatus } = params; /** @type {any} */ const updateData = {}; @@ -7645,6 +7680,17 @@ jobs: } } + // Handle labels update + if (canUpdateLabels && item.labels !== undefined) { + if (Array.isArray(item.labels)) { + updateData.labels = item.labels; + hasUpdates = true; + logMessages.push(`Will update labels to: ${item.labels.join(", ")}`); + } else { + logMessages.push("Invalid labels value: must be an array"); + } + } + return { hasUpdates, updateData, logMessages }; } @@ -7689,12 +7735,13 @@ jobs: const canUpdateStatus = process.env.GH_AW_UPDATE_STATUS === "true"; const canUpdateTitle = process.env.GH_AW_UPDATE_TITLE === "true"; const canUpdateBody = process.env.GH_AW_UPDATE_BODY === "true"; + const canUpdateLabels = process.env.GH_AW_UPDATE_LABELS === "true"; core.info(`Update target configuration: ${updateTarget}`); if (supportsStatus) { - core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update status: ${canUpdateStatus}, title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } else { - core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}`); + core.info(`Can update title: ${canUpdateTitle}, body: ${canUpdateBody}, labels: ${canUpdateLabels}`); } // Check context validity @@ -7738,6 +7785,7 @@ jobs: canUpdateStatus, canUpdateTitle, canUpdateBody, + canUpdateLabels, supportsStatus, }); @@ -7859,7 +7907,7 @@ jobs: createGetSummaryLine, }; - EOF_006d32d7 + EOF_60283df2 - name: Assign To Agent id: assign_to_agent if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'assign_to_agent')) diff --git a/pkg/workflow/aw_info_steps_test.go b/pkg/workflow/aw_info_steps_test.go index dddf1c516b..056070f085 100644 --- a/pkg/workflow/aw_info_steps_test.go +++ b/pkg/workflow/aw_info_steps_test.go @@ -72,8 +72,8 @@ engine: claude This workflow tests that Claude has firewall enabled by default when network is not configured. `, - expectFirewall: "", - description: "Should have empty firewall type when no network is configured (firewall only applies with network restrictions)", + expectFirewall: "squid", + description: "Should have firewall type squid when no network is configured (firewall enabled by default)", }, } diff --git a/pkg/workflow/compiler_permissions_test.go b/pkg/workflow/compiler_permissions_test.go index 25505be587..484f8763cf 100644 --- a/pkg/workflow/compiler_permissions_test.go +++ b/pkg/workflow/compiler_permissions_test.go @@ -136,14 +136,14 @@ This is a test workflow without network permissions. t.Fatalf("Failed to read lock file: %v", err) } - // When no network is specified, firewall is NOT enabled (defaults to full access) - // AWF is only enabled when network restrictions are configured - if strings.Contains(string(lockContent), "sudo -E awf") { - t.Error("Should NOT contain AWF wrapper when no network field specified (defaults to full access)") + // AWF is enabled by default for all engines (copilot, claude, codex) even without explicit network config + // This ensures sandbox.agent: awf is the default behavior + if !strings.Contains(string(lockContent), "sudo -E awf") { + t.Error("Should contain AWF wrapper by default for Claude engine") } }) - t.Run("network: defaults should not enable AWF for Claude without firewall config", func(t *testing.T) { + t.Run("network: defaults enables AWF by default for Claude", func(t *testing.T) { testContent := `--- on: push engine: claude @@ -173,14 +173,13 @@ This is a test workflow with explicit defaults network permissions. t.Fatalf("Failed to read lock file: %v", err) } - // network: defaults without explicit firewall config does NOT enable AWF - // (firewall must be explicitly enabled or network.allowed must be specified) - if strings.Contains(string(lockContent), "sudo -E awf") { - t.Error("Should NOT contain AWF wrapper for network: defaults without firewall config") + // AWF is enabled by default for Claude engine with network: defaults + if !strings.Contains(string(lockContent), "sudo -E awf") { + t.Error("Should contain AWF wrapper for Claude engine with network: defaults") } }) - t.Run("network: {} should not enable AWF without firewall config", func(t *testing.T) { + t.Run("network: {} enables AWF by default for Claude", func(t *testing.T) { testContent := `--- on: push engine: claude @@ -210,9 +209,9 @@ This is a test workflow with empty network permissions (deny all). t.Fatalf("Failed to read lock file: %v", err) } - // Empty network config without explicit firewall config does NOT enable AWF - if strings.Contains(string(lockContent), "sudo -E awf") { - t.Error("Should NOT contain AWF wrapper for network: {} without firewall config") + // AWF is enabled by default for Claude engine with network: {} + if !strings.Contains(string(lockContent), "sudo -E awf") { + t.Error("Should contain AWF wrapper for Claude engine with network: {}") } })