From 95c029f2f1766fadd9e4b76980415016d475ea71 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 08:12:44 +0000 Subject: [PATCH 1/4] Initial plan From 7881b466a45e7a290a42ed640cd1da4c380339fb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 08:18:50 +0000 Subject: [PATCH 2/4] Initial exploration complete - planning safeinputs artifact upload Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/release.lock.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index e5577baff7..42744f2d5a 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -5969,19 +5969,19 @@ jobs: - name: Download Go modules run: go mod download - name: Generate SBOM (SPDX format) - uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 + uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0 with: artifact-name: sbom.spdx.json format: spdx-json output-file: sbom.spdx.json - name: Generate SBOM (CycloneDX format) - uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 + uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0 with: artifact-name: sbom.cdx.json format: cyclonedx-json output-file: sbom.cdx.json - name: Upload SBOM artifacts - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: sbom-artifacts path: | From 7575da5b039722edb6e3be1fe6e17625542ffd62 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 08:27:46 +0000 Subject: [PATCH 3/4] Add safeinputs artifact upload and enhance server.log creation Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../copilot-pr-merged-report.lock.yml | 14 +++++++++++++- .../daily-performance-summary.lock.yml | 14 +++++++++++++- .github/workflows/dev.lock.yml | 14 +++++++++++++- .../workflows/test-python-safe-input.lock.yml | 14 +++++++++++++- pkg/workflow/compiler_yaml.go | 18 ++++++++++++++++++ pkg/workflow/sh/start_safe_inputs_server.sh | 8 +++++++- 6 files changed, 77 insertions(+), 5 deletions(-) diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml index 4ec53522a7..951456110d 100644 --- a/.github/workflows/copilot-pr-merged-report.lock.yml +++ b/.github/workflows/copilot-pr-merged-report.lock.yml @@ -3140,9 +3140,14 @@ jobs: echo " Working directory: $(pwd)" # Ensure logs directory exists mkdir -p /tmp/gh-aw/safe-inputs/logs + # Create initial server.log file for artifact upload + echo "Safe Inputs MCP Server Log" > /tmp/gh-aw/safe-inputs/logs/server.log + echo "Start time: $(date)" >> /tmp/gh-aw/safe-inputs/logs/server.log + echo "===========================================" >> /tmp/gh-aw/safe-inputs/logs/server.log + echo "" >> /tmp/gh-aw/safe-inputs/logs/server.log # Start the HTTP server in the background echo "Starting safe-inputs MCP HTTP server..." - node mcp-server.cjs > /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & + node mcp-server.cjs >> /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & SERVER_PID=$! echo "Started safe-inputs MCP server with PID $SERVER_PID" # Wait for server to be ready (max 10 seconds) @@ -4878,6 +4883,13 @@ jobs: name: mcp-logs path: /tmp/gh-aw/mcp-logs/ if-no-files-found: ignore + - name: Upload SafeInputs logs + if: always() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: safeinputs + path: /tmp/gh-aw/safe-inputs/logs/ + if-no-files-found: ignore - name: Parse agent logs for step summary if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index 4e2e1bcf2e..fbd57e8a97 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -4014,9 +4014,14 @@ jobs: echo " Working directory: $(pwd)" # Ensure logs directory exists mkdir -p /tmp/gh-aw/safe-inputs/logs + # Create initial server.log file for artifact upload + echo "Safe Inputs MCP Server Log" > /tmp/gh-aw/safe-inputs/logs/server.log + echo "Start time: $(date)" >> /tmp/gh-aw/safe-inputs/logs/server.log + echo "===========================================" >> /tmp/gh-aw/safe-inputs/logs/server.log + echo "" >> /tmp/gh-aw/safe-inputs/logs/server.log # Start the HTTP server in the background echo "Starting safe-inputs MCP HTTP server..." - node mcp-server.cjs > /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & + node mcp-server.cjs >> /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & SERVER_PID=$! echo "Started safe-inputs MCP server with PID $SERVER_PID" # Wait for server to be ready (max 10 seconds) @@ -6266,6 +6271,13 @@ jobs: name: mcp-logs path: /tmp/gh-aw/mcp-logs/ if-no-files-found: ignore + - name: Upload SafeInputs logs + if: always() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: safeinputs + path: /tmp/gh-aw/safe-inputs/logs/ + if-no-files-found: ignore - name: Parse agent logs for step summary if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 39d198943c..a18a6b4199 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -1729,9 +1729,14 @@ jobs: echo " Working directory: $(pwd)" # Ensure logs directory exists mkdir -p /tmp/gh-aw/safe-inputs/logs + # Create initial server.log file for artifact upload + echo "Safe Inputs MCP Server Log" > /tmp/gh-aw/safe-inputs/logs/server.log + echo "Start time: $(date)" >> /tmp/gh-aw/safe-inputs/logs/server.log + echo "===========================================" >> /tmp/gh-aw/safe-inputs/logs/server.log + echo "" >> /tmp/gh-aw/safe-inputs/logs/server.log # Start the HTTP server in the background echo "Starting safe-inputs MCP HTTP server..." - node mcp-server.cjs > /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & + node mcp-server.cjs >> /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & SERVER_PID=$! echo "Started safe-inputs MCP server with PID $SERVER_PID" # Wait for server to be ready (max 10 seconds) @@ -2236,6 +2241,13 @@ jobs: name: mcp-logs path: /tmp/gh-aw/mcp-logs/ if-no-files-found: ignore + - name: Upload SafeInputs logs + if: always() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: safeinputs + path: /tmp/gh-aw/safe-inputs/logs/ + if-no-files-found: ignore - name: Parse agent logs for step summary if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/test-python-safe-input.lock.yml b/.github/workflows/test-python-safe-input.lock.yml index 76bd23b7d4..a8e6d04671 100644 --- a/.github/workflows/test-python-safe-input.lock.yml +++ b/.github/workflows/test-python-safe-input.lock.yml @@ -3005,9 +3005,14 @@ jobs: echo " Working directory: $(pwd)" # Ensure logs directory exists mkdir -p /tmp/gh-aw/safe-inputs/logs + # Create initial server.log file for artifact upload + echo "Safe Inputs MCP Server Log" > /tmp/gh-aw/safe-inputs/logs/server.log + echo "Start time: $(date)" >> /tmp/gh-aw/safe-inputs/logs/server.log + echo "===========================================" >> /tmp/gh-aw/safe-inputs/logs/server.log + echo "" >> /tmp/gh-aw/safe-inputs/logs/server.log # Start the HTTP server in the background echo "Starting safe-inputs MCP HTTP server..." - node mcp-server.cjs > /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & + node mcp-server.cjs >> /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & SERVER_PID=$! echo "Started safe-inputs MCP server with PID $SERVER_PID" # Wait for server to be ready (max 10 seconds) @@ -4508,6 +4513,13 @@ jobs: name: mcp-logs path: /tmp/gh-aw/mcp-logs/ if-no-files-found: ignore + - name: Upload SafeInputs logs + if: always() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: safeinputs + path: /tmp/gh-aw/safe-inputs/logs/ + if-no-files-found: ignore - name: Parse agent logs for step summary if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/pkg/workflow/compiler_yaml.go b/pkg/workflow/compiler_yaml.go index 2616d0eaf8..91c32cad9f 100644 --- a/pkg/workflow/compiler_yaml.go +++ b/pkg/workflow/compiler_yaml.go @@ -411,6 +411,11 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat // upload MCP logs (if any MCP tools were used) c.generateUploadMCPLogs(yaml) + // upload SafeInputs logs (if safe-inputs is enabled) + if IsSafeInputsEnabled(data.SafeInputs, data) { + c.generateUploadSafeInputsLogs(yaml) + } + // parse agent logs for GITHUB_STEP_SUMMARY c.generateLogParsing(yaml, engine) @@ -653,6 +658,19 @@ func (c *Compiler) generateUploadMCPLogs(yaml *strings.Builder) { yaml.WriteString(" if-no-files-found: ignore\n") } +func (c *Compiler) generateUploadSafeInputsLogs(yaml *strings.Builder) { + // Record artifact upload for validation + c.stepOrderTracker.RecordArtifactUpload("Upload SafeInputs logs", []string{"/tmp/gh-aw/safe-inputs/logs/"}) + + yaml.WriteString(" - name: Upload SafeInputs logs\n") + yaml.WriteString(" if: always()\n") + yaml.WriteString(fmt.Sprintf(" uses: %s\n", GetActionPin("actions/upload-artifact"))) + yaml.WriteString(" with:\n") + yaml.WriteString(" name: safeinputs\n") + yaml.WriteString(" path: /tmp/gh-aw/safe-inputs/logs/\n") + yaml.WriteString(" if-no-files-found: ignore\n") +} + func splitContentIntoChunks(content string) []string { const maxChunkSize = 20900 // 21000 - 100 character buffer const indentSpaces = " " // 10 spaces added to each line diff --git a/pkg/workflow/sh/start_safe_inputs_server.sh b/pkg/workflow/sh/start_safe_inputs_server.sh index b5e46f3fa9..06a5665cff 100644 --- a/pkg/workflow/sh/start_safe_inputs_server.sh +++ b/pkg/workflow/sh/start_safe_inputs_server.sh @@ -23,9 +23,15 @@ echo " Working directory: $(pwd)" # Ensure logs directory exists mkdir -p /tmp/gh-aw/safe-inputs/logs +# Create initial server.log file for artifact upload +echo "Safe Inputs MCP Server Log" > /tmp/gh-aw/safe-inputs/logs/server.log +echo "Start time: $(date)" >> /tmp/gh-aw/safe-inputs/logs/server.log +echo "===========================================" >> /tmp/gh-aw/safe-inputs/logs/server.log +echo "" >> /tmp/gh-aw/safe-inputs/logs/server.log + # Start the HTTP server in the background echo "Starting safe-inputs MCP HTTP server..." -node mcp-server.cjs > /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & +node mcp-server.cjs >> /tmp/gh-aw/safe-inputs/logs/server.log 2>&1 & SERVER_PID=$! echo "Started safe-inputs MCP server with PID $SERVER_PID" From 09a66bd4362b1c2103c05d5840e042a5f84f7e99 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 08:44:13 +0000 Subject: [PATCH 4/4] Add GET /health endpoint and update health check to use it Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../copilot-pr-merged-report.lock.yml | 14 +++++++- .../daily-performance-summary.lock.yml | 14 +++++++- .github/workflows/dev.lock.yml | 14 +++++++- .github/workflows/release.lock.yml | 6 ++-- .../workflows/test-python-safe-input.lock.yml | 14 +++++++- .../js/safe_inputs_mcp_server_http.cjs | 14 ++++++++ .../js/safe_inputs_mcp_server_http.test.cjs | 34 +++++++++++++++++++ pkg/workflow/sh/start_safe_inputs_server.sh | 2 +- 8 files changed, 104 insertions(+), 8 deletions(-) diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml index 951456110d..2582793417 100644 --- a/.github/workflows/copilot-pr-merged-report.lock.yml +++ b/.github/workflows/copilot-pr-merged-report.lock.yml @@ -2902,6 +2902,18 @@ jobs: res.end(); return; } + if (req.method === "GET" && req.url === "/health") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + status: "ok", + server: config.serverName || "safeinputs", + version: config.version || "1.0.0", + tools: config.tools.length, + }) + ); + return; + } if (req.method !== "POST") { res.writeHead(405, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Method not allowed" })); @@ -3161,7 +3173,7 @@ jobs: exit 1 fi # Check if server is responding - if curl -s -f -H "Authorization: Bearer $GH_AW_SAFE_INPUTS_API_KEY" http://localhost:$GH_AW_SAFE_INPUTS_PORT/ > /dev/null 2>&1; then + if curl -s -f http://localhost:$GH_AW_SAFE_INPUTS_PORT/health > /dev/null 2>&1; then echo "Safe Inputs MCP server is ready (attempt $i/10)" break fi diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index fbd57e8a97..2cb5185a57 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -3485,6 +3485,18 @@ jobs: res.end(); return; } + if (req.method === "GET" && req.url === "/health") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + status: "ok", + server: config.serverName || "safeinputs", + version: config.version || "1.0.0", + tools: config.tools.length, + }) + ); + return; + } if (req.method !== "POST") { res.writeHead(405, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Method not allowed" })); @@ -4035,7 +4047,7 @@ jobs: exit 1 fi # Check if server is responding - if curl -s -f -H "Authorization: Bearer $GH_AW_SAFE_INPUTS_API_KEY" http://localhost:$GH_AW_SAFE_INPUTS_PORT/ > /dev/null 2>&1; then + if curl -s -f http://localhost:$GH_AW_SAFE_INPUTS_PORT/health > /dev/null 2>&1; then echo "Safe Inputs MCP server is ready (attempt $i/10)" break fi diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index a18a6b4199..8a7a03e3fe 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -1491,6 +1491,18 @@ jobs: res.end(); return; } + if (req.method === "GET" && req.url === "/health") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + status: "ok", + server: config.serverName || "safeinputs", + version: config.version || "1.0.0", + tools: config.tools.length, + }) + ); + return; + } if (req.method !== "POST") { res.writeHead(405, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Method not allowed" })); @@ -1750,7 +1762,7 @@ jobs: exit 1 fi # Check if server is responding - if curl -s -f -H "Authorization: Bearer $GH_AW_SAFE_INPUTS_API_KEY" http://localhost:$GH_AW_SAFE_INPUTS_PORT/ > /dev/null 2>&1; then + if curl -s -f http://localhost:$GH_AW_SAFE_INPUTS_PORT/health > /dev/null 2>&1; then echo "Safe Inputs MCP server is ready (attempt $i/10)" break fi diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 42744f2d5a..e5577baff7 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -5969,19 +5969,19 @@ jobs: - name: Download Go modules run: go mod download - name: Generate SBOM (SPDX format) - uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0 + uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 with: artifact-name: sbom.spdx.json format: spdx-json output-file: sbom.spdx.json - name: Generate SBOM (CycloneDX format) - uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0 + uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 with: artifact-name: sbom.cdx.json format: cyclonedx-json output-file: sbom.cdx.json - name: Upload SBOM artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 with: name: sbom-artifacts path: | diff --git a/.github/workflows/test-python-safe-input.lock.yml b/.github/workflows/test-python-safe-input.lock.yml index a8e6d04671..79d87417be 100644 --- a/.github/workflows/test-python-safe-input.lock.yml +++ b/.github/workflows/test-python-safe-input.lock.yml @@ -2674,6 +2674,18 @@ jobs: res.end(); return; } + if (req.method === "GET" && req.url === "/health") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + status: "ok", + server: config.serverName || "safeinputs", + version: config.version || "1.0.0", + tools: config.tools.length, + }) + ); + return; + } if (req.method !== "POST") { res.writeHead(405, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Method not allowed" })); @@ -3026,7 +3038,7 @@ jobs: exit 1 fi # Check if server is responding - if curl -s -f -H "Authorization: Bearer $GH_AW_SAFE_INPUTS_API_KEY" http://localhost:$GH_AW_SAFE_INPUTS_PORT/ > /dev/null 2>&1; then + if curl -s -f http://localhost:$GH_AW_SAFE_INPUTS_PORT/health > /dev/null 2>&1; then echo "Safe Inputs MCP server is ready (attempt $i/10)" break fi diff --git a/pkg/workflow/js/safe_inputs_mcp_server_http.cjs b/pkg/workflow/js/safe_inputs_mcp_server_http.cjs index 21e36c520a..6e88ee7eaa 100644 --- a/pkg/workflow/js/safe_inputs_mcp_server_http.cjs +++ b/pkg/workflow/js/safe_inputs_mcp_server_http.cjs @@ -178,6 +178,20 @@ async function startHttpServer(configPath, options = {}) { return; } + // Handle GET /health endpoint for health checks + if (req.method === "GET" && req.url === "/health") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + status: "ok", + server: config.serverName || "safeinputs", + version: config.version || "1.0.0", + tools: config.tools.length, + }) + ); + return; + } + // Only handle POST requests for MCP protocol if (req.method !== "POST") { res.writeHead(405, { "Content-Type": "application/json" }); diff --git a/pkg/workflow/js/safe_inputs_mcp_server_http.test.cjs b/pkg/workflow/js/safe_inputs_mcp_server_http.test.cjs index 0d4f2c9429..9501e68a98 100644 --- a/pkg/workflow/js/safe_inputs_mcp_server_http.test.cjs +++ b/pkg/workflow/js/safe_inputs_mcp_server_http.test.cjs @@ -171,6 +171,40 @@ describe("safe_inputs_mcp_server_http.cjs integration", () => { sessionId = response.headers["mcp-session-id"]; }); + it("should respond to GET /health endpoint", async () => { + return new Promise((resolve, reject) => { + const req = http.request( + { + hostname: "localhost", + port: serverPort, + path: "/health", + method: "GET", + }, + res => { + let responseData = ""; + res.on("data", chunk => { + responseData += chunk; + }); + res.on("end", () => { + try { + expect(res.statusCode).toBe(200); + const data = JSON.parse(responseData); + expect(data.status).toBe("ok"); + expect(data.server).toBe("http-integration-test-server"); + expect(data.version).toBe("1.0.0"); + expect(data.tools).toBe(1); + resolve(); + } catch (e) { + reject(e); + } + }); + } + ); + req.on("error", reject); + req.end(); + }); + }); + it("should list tools via HTTP", async () => { const headers = sessionId ? { "Mcp-Session-Id": sessionId } : {}; diff --git a/pkg/workflow/sh/start_safe_inputs_server.sh b/pkg/workflow/sh/start_safe_inputs_server.sh index 06a5665cff..147bda2426 100644 --- a/pkg/workflow/sh/start_safe_inputs_server.sh +++ b/pkg/workflow/sh/start_safe_inputs_server.sh @@ -47,7 +47,7 @@ for i in {1..10}; do fi # Check if server is responding - if curl -s -f -H "Authorization: Bearer $GH_AW_SAFE_INPUTS_API_KEY" http://localhost:$GH_AW_SAFE_INPUTS_PORT/ > /dev/null 2>&1; then + if curl -s -f http://localhost:$GH_AW_SAFE_INPUTS_PORT/health > /dev/null 2>&1; then echo "Safe Inputs MCP server is ready (attempt $i/10)" break fi