From 767fad50920718597fb3832340512778a4f06bac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:05:07 +0000 Subject: [PATCH 1/2] Initial plan From 19a66ff642f25527bf0d6c0bae4a8718c944c90b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:19:43 +0000 Subject: [PATCH 2/2] fix: pre-download firewall-enabled runs to avoid MCP tool timeout in daily-security-observability Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../daily-security-observability.lock.yml | 8 +++-- .../workflows/daily-security-observability.md | 36 +++++++++++++------ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/.github/workflows/daily-security-observability.lock.yml b/.github/workflows/daily-security-observability.lock.yml index 0a71e2c28bd..75df1f2dbb9 100644 --- a/.github/workflows/daily-security-observability.lock.yml +++ b/.github/workflows/daily-security-observability.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"18e9cebd0e9fadc489c9e65dbb10a27d41365ac9fd83eb6f325511b8be1bf328","body_hash":"6fa8d8ae08a53f45e82633e53d1cd54f67b397663a82bffa10fd27341d00af5d","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.75","copilot-sdk":"1.0.8"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b945a87e6ed3eabaad180c8a3935c5d9264cffd8334b55040726bbca6fee26c9","body_hash":"54a32044c76a0b8e6ab9dc17b949dd123b57dee2a74ca9c4d4117478d3513c46","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.75","copilot-sdk":"1.0.8"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/setup-python","sha":"5fda3b95a4ea91299a34e894583c3862153e4b97","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"docker/build-push-action","sha":"53b7df96c91f9c12dcc8a07bcb9ccacbed38856a","version":"v7.3.0"},{"repo":"docker/setup-buildx-action","sha":"bb05f3f5519dd87d3ba754cc423b652a5edd6d2c","version":"v4.2.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.42","digest":"sha256:26a8af4e5566485b02f52af59ee03803ae798271a9619d4767e94d07806deb9b","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.42@sha256:26a8af4e5566485b02f52af59ee03803ae798271a9619d4767e94d07806deb9b"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.42","digest":"sha256:944f2686c9ab9bec338fd14b662461662f77cd12cd0ea8a3e7cb8c0987cd1607","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.42@sha256:944f2686c9ab9bec338fd14b662461662f77cd12cd0ea8a3e7cb8c0987cd1607"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.42","digest":"sha256:da006bf96d2d246dd269d57b233c1798d2ad63d6cd64ca02f7bf71045028781f","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.42@sha256:da006bf96d2d246dd269d57b233c1798d2ad63d6cd64ca02f7bf71045028781f"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.42","digest":"sha256:42dfeb649c680a8558cd5423dbc530b653a69413e35ffbe5e71da5d48c94bdf0","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.42@sha256:42dfeb649c680a8558cd5423dbc530b653a69413e35ffbe5e71da5d48c94bdf0"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.6","digest":"sha256:fecabec51bbc41f2ad61076d6bcd9a36ef23b142e672a444e054d37fc29de93c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.6@sha256:fecabec51bbc41f2ad61076d6bcd9a36ef23b142e672a444e054d37fc29de93c"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:a8082161d7dceda14b68f32eb39d0eaa96b825d07f5895b096afab9d9e0c7748","pinned_image":"ghcr.io/github/gh-aw-node@sha256:a8082161d7dceda14b68f32eb39d0eaa96b825d07f5895b096afab9d9e0c7748"},{"image":"ghcr.io/github/github-mcp-server:v1.7.0","digest":"sha256:c491ffdf6f4c85cb5397021bc655edb8ab825c6f5f568e7597d77a1bd7c4d308","pinned_image":"ghcr.io/github/github-mcp-server:v1.7.0@sha256:c491ffdf6f4c85cb5397021bc655edb8ab825c6f5f568e7597d77a1bd7c4d308"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -607,7 +607,11 @@ jobs: - env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} name: Download integrity-filtered logs - run: "mkdir -p /tmp/gh-aw/agent/integrity\nmkdir -p /tmp/gh-aw/cache-memory/security-observability\n\nCACHE_FILE=/tmp/gh-aw/cache-memory/security-observability/filtered-logs.snapshot.json\nRUN_FILE=/tmp/gh-aw/agent/integrity/filtered-logs.json\nFRESH_LOGS=/tmp/gh-aw/agent/integrity/filtered-logs.fresh.json\nEMPTY_DATA='{\"runs\":[],\"summary\":{\"total_runs\":0}}'\nNOW_EPOCH=$(date +%s)\nMAX_CACHE_AGE_SECONDS=$((7 * 24 * 60 * 60))\n\n# Warm start from cached 7-day snapshot when available and fresh.\nif [ -f \"$CACHE_FILE\" ] && jq -e '.runs and .updated_at' \"$CACHE_FILE\" > /dev/null 2>&1; then\n cache_updated_at=$(jq -r '.updated_at' \"$CACHE_FILE\")\n cache_updated_epoch=$(\n date -d \"$cache_updated_at\" +%s 2>/dev/null \\\n || date -j -f \"%Y-%m-%dT%H:%M:%SZ\" \"$cache_updated_at\" +%s 2>/dev/null \\\n || echo 0\n )\n cache_age_seconds=$((NOW_EPOCH - cache_updated_epoch))\n if [ \"$cache_updated_epoch\" -gt 0 ] && [ \"$cache_age_seconds\" -le \"$MAX_CACHE_AGE_SECONDS\" ]; then\n jq '{runs: (.runs // []), summary: (.summary // {\"total_runs\": 0})}' \"$CACHE_FILE\" > \"$RUN_FILE\"\n echo \"✅ Warm cache restored (${cache_age_seconds}s old)\"\n else\n echo \"⚠️ Cache snapshot is stale (${cache_age_seconds}s old); starting fresh\"\n fi\nfi\n\n# Download logs filtered to only runs with DIFC integrity-filtered events.\n# --artifacts mcp: only download the MCP gateway log artifact (sufficient for DIFC checking).\n# --timeout 8: cap execution at 8 minutes to prevent runaway downloads.\ngh aw logs --filtered-integrity --start-date -7d --json -c 200 \\\n --artifacts mcp --timeout 8 \\\n > \"$FRESH_LOGS\" || true\n\n# Validate JSON output and fall back to an empty dataset on failure\nif ! jq -e '.runs' \"$FRESH_LOGS\" > /dev/null 2>&1; then\n echo \"⚠️ No valid logs produced; continuing with empty dataset\"\n echo \"$EMPTY_DATA\" > \"$FRESH_LOGS\"\nfi\n\n# Merge warm-start and fresh runs; fresh entries override warm-cache entries with the same run_id.\nif [ -f \"$RUN_FILE\" ] && jq -e '.runs' \"$RUN_FILE\" > /dev/null 2>&1; then\n jq -s '\n {\n runs: (\n ((.[0].runs // []) + (.[1].runs // []))\n | sort_by(.run_id)\n | group_by(.run_id)\n | map(.[-1])\n ),\n summary: {\n total_runs: (\n ((.[0].runs // []) + (.[1].runs // []) | sort_by(.run_id) | group_by(.run_id) | length)\n )\n }\n }\n ' \"$RUN_FILE\" \"$FRESH_LOGS\" > \"$RUN_FILE.merged\"\n mv \"$RUN_FILE.merged\" \"$RUN_FILE\"\nelse\n mv \"$FRESH_LOGS\" \"$RUN_FILE\"\nfi\n\ncount=$(jq '.runs | length' \"$RUN_FILE\" 2>/dev/null || echo 0)\necho \"✅ Downloaded $count runs with integrity-filtered events\"\n\n# Persist updated 7-day snapshot back to cache-memory every run.\njq -n \\\n --arg updated_at \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \\\n --slurpfile payload \"$RUN_FILE\" \\\n '{\n updated_at: $updated_at,\n runs: ($payload[0].runs // []),\n summary: {\n total_runs: (($payload[0].runs // []) | length)\n }\n }' > \"$CACHE_FILE\"" + run: "mkdir -p /tmp/gh-aw/agent/integrity\nmkdir -p /tmp/gh-aw/cache-memory/security-observability\n\nCACHE_FILE=/tmp/gh-aw/cache-memory/security-observability/filtered-logs.snapshot.json\nRUN_FILE=/tmp/gh-aw/agent/integrity/filtered-logs.json\nFRESH_LOGS=/tmp/gh-aw/agent/integrity/filtered-logs.fresh.json\nEMPTY_DATA='{\"runs\":[],\"summary\":{\"total_runs\":0}}'\nNOW_EPOCH=$(date +%s)\nMAX_CACHE_AGE_SECONDS=$((7 * 24 * 60 * 60))\n\n# Warm start from cached 7-day snapshot when available and fresh.\nif [ -f \"$CACHE_FILE\" ] && jq -e '.runs and .updated_at' \"$CACHE_FILE\" > /dev/null 2>&1; then\n cache_updated_at=$(jq -r '.updated_at' \"$CACHE_FILE\")\n cache_updated_epoch=$(\n date -d \"$cache_updated_at\" +%s 2>/dev/null \\\n || date -j -f \"%Y-%m-%dT%H:%M:%SZ\" \"$cache_updated_at\" +%s 2>/dev/null \\\n || echo 0\n )\n cache_age_seconds=$((NOW_EPOCH - cache_updated_epoch))\n if [ \"$cache_updated_epoch\" -gt 0 ] && [ \"$cache_age_seconds\" -le \"$MAX_CACHE_AGE_SECONDS\" ]; then\n jq '{runs: (.runs // []), summary: (.summary // {\"total_runs\": 0})}' \"$CACHE_FILE\" > \"$RUN_FILE\"\n echo \"✅ Warm cache restored (${cache_age_seconds}s old)\"\n else\n echo \"⚠️ Cache snapshot is stale (${cache_age_seconds}s old); starting fresh\"\n fi\nfi\n\n# Download logs filtered to only runs with DIFC integrity-filtered events.\n# --artifacts mcp: only download the MCP gateway log artifact (sufficient for DIFC checking).\n# --timeout 8: cap execution at 8 minutes to prevent runaway downloads.\ngh aw logs --filtered-integrity --start-date -7d --json -c 200 \\\n --artifacts mcp --timeout 8 \\\n > \"$FRESH_LOGS\" || true\n\n# Validate JSON output and fall back to an empty dataset on failure\nif ! jq -e '.runs' \"$FRESH_LOGS\" > /dev/null 2>&1; then\n echo \"⚠️ No valid logs produced; continuing with empty dataset\"\n echo \"$EMPTY_DATA\" > \"$FRESH_LOGS\"\nfi\n\n# Merge warm-start and fresh runs; fresh entries override warm-cache entries with the same run_id.\nif [ -f \"$RUN_FILE\" ] && jq -e '.runs' \"$RUN_FILE\" > /dev/null 2>&1; then\n jq -s '\n {\n runs: (\n ((.[0].runs // []) + (.[1].runs // []))\n | sort_by(.run_id)\n | group_by(.run_id)\n | map(.[-1])\n ),\n summary: {\n total_runs: (\n ((.[0].runs // []) + (.[1].runs // []) | sort_by(.run_id) | group_by(.run_id) | length)\n )\n }\n }\n ' \"$RUN_FILE\" \"$FRESH_LOGS\" > \"$RUN_FILE.merged\"\n mv \"$RUN_FILE.merged\" \"$RUN_FILE\"\nelse\n mv \"$FRESH_LOGS\" \"$RUN_FILE\"\nfi\n\ncount=$(jq '.runs | length' \"$RUN_FILE\" 2>/dev/null || echo 0)\necho \"✅ Downloaded $count runs with integrity-filtered events\"\n\n# Persist updated 7-day snapshot back to cache-memory every run.\njq -n \\\n --arg updated_at \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \\\n --slurpfile payload \"$RUN_FILE\" \\\n '{\n updated_at: $updated_at,\n runs: ($payload[0].runs // []),\n summary: {\n total_runs: (($payload[0].runs // []) | length)\n }\n }' > \"$CACHE_FILE\"\n" + - env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + name: Download firewall-enabled workflow runs + run: "mkdir -p /tmp/gh-aw/agent/firewall\nFIREWALL_RUNS=/tmp/gh-aw/agent/firewall/firewall-enabled-runs.json\nEMPTY_DATA='{\"runs\":[],\"summary\":{\"total_runs\":0}}'\n\n# Download logs filtered to only runs with the firewall feature enabled.\n# --artifacts activation: only download the activation artifact (aw_info.json)\n# needed for firewall detection, avoiding large agent artifact downloads.\n# --timeout 8: cap execution at 8 minutes to prevent runaway downloads.\ngh aw logs --firewall --start-date -7d --json -c 100 \\\n --artifacts activation --timeout 8 \\\n > \"$FIREWALL_RUNS\" || true\n\n# Validate JSON output and fall back to an empty dataset on failure\nif ! jq -e '.runs' \"$FIREWALL_RUNS\" > /dev/null 2>&1; then\n echo \"No valid firewall logs produced; continuing with empty dataset\"\n echo \"$EMPTY_DATA\" > \"$FIREWALL_RUNS\"\nfi\n\ncount=$(jq '.runs | length' \"$FIREWALL_RUNS\" 2>/dev/null || echo 0)\necho \"Downloaded $count firewall-enabled workflow runs\"" - name: Configure Git credentials env: diff --git a/.github/workflows/daily-security-observability.md b/.github/workflows/daily-security-observability.md index 40e8eb845b0..7dd2def9094 100644 --- a/.github/workflows/daily-security-observability.md +++ b/.github/workflows/daily-security-observability.md @@ -114,6 +114,31 @@ steps: } }' > "$CACHE_FILE" + - name: Download firewall-enabled workflow runs + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + mkdir -p /tmp/gh-aw/agent/firewall + FIREWALL_RUNS=/tmp/gh-aw/agent/firewall/firewall-enabled-runs.json + EMPTY_DATA='{"runs":[],"summary":{"total_runs":0}}' + + # Download logs filtered to only runs with the firewall feature enabled. + # --artifacts activation: only download the activation artifact (aw_info.json) + # needed for firewall detection, avoiding large agent artifact downloads. + # --timeout 8: cap execution at 8 minutes to prevent runaway downloads. + gh aw logs --firewall --start-date -7d --json -c 100 \ + --artifacts activation --timeout 8 \ + > "$FIREWALL_RUNS" || true + + # Validate JSON output and fall back to an empty dataset on failure + if ! jq -e '.runs' "$FIREWALL_RUNS" > /dev/null 2>&1; then + echo "No valid firewall logs produced; continuing with empty dataset" + echo "$EMPTY_DATA" > "$FIREWALL_RUNS" + fi + + count=$(jq '.runs | length' "$FIREWALL_RUNS" 2>/dev/null || echo 0) + echo "Downloaded $count firewall-enabled workflow runs" + tools: bash: - "*" @@ -174,16 +199,7 @@ Both datasets cover the **last 7 days** and share the cache-memory path `/tmp/gh **ALWAYS PERFORM FRESH ANALYSIS**: This report must always use fresh data from the audit tool. Do NOT skip analysis based on cached results or reuse aggregated statistics from previous runs. -Use the `logs` tool from the agentic-workflows MCP server to collect workflow runs that have firewall enabled: - -**Tool call:** -```json -{ - "firewall": true, - "start_date": "-7d", - "count": 100 -} -``` +Read the pre-downloaded list of firewall-enabled workflow runs from `/tmp/gh-aw/agent/firewall/firewall-enabled-runs.json`. This file was populated by the startup step and contains runs from the last 7 days with the firewall feature enabled. Extract the `run_id` from each entry in the `runs` array. ### Step 1.2: Early Exit if No Firewall Data