From 567412f025c17ba8173e53e0a2db13a0435dca4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 22:39:05 +0000 Subject: [PATCH 1/5] Initial plan From 41aa758e7ca7dd88a3ccda70dca56df01378a823 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 22:47:17 +0000 Subject: [PATCH 2/5] Fix insecure temporary file creation by adding secure permissions Co-authored-by: eaftan <4733401+eaftan@users.noreply.github.com> --- actions/setup/src/index.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/actions/setup/src/index.js b/actions/setup/src/index.js index 822b3964a2..e47e9f7c3a 100644 --- a/actions/setup/src/index.js +++ b/actions/setup/src/index.js @@ -1,9 +1,9 @@ // Setup Activation Action // Copies activation job files to the agent environment -const core = require('@actions/core'); -const fs = require('fs'); -const path = require('path'); +const core = require("@actions/core"); +const fs = require("fs"); +const path = require("path"); // Embedded activation files will be inserted here during build const FILES = { @@ -12,29 +12,29 @@ const FILES = { async function run() { try { - const destination = core.getInput('destination') || '/tmp/gh-aw/actions/activation'; - + const destination = core.getInput("destination") || "/tmp/gh-aw/actions/activation"; + core.info(`Copying activation files to ${destination}`); - - // Create destination directory if it doesn't exist + + // Create destination directory with secure permissions if it doesn't exist if (!fs.existsSync(destination)) { - fs.mkdirSync(destination, { recursive: true }); + fs.mkdirSync(destination, { recursive: true, mode: 0o700 }); core.info(`Created directory: ${destination}`); } - + let fileCount = 0; - + // Copy each embedded file for (const [filename, content] of Object.entries(FILES)) { const filePath = path.join(destination, filename); - fs.writeFileSync(filePath, content, 'utf8'); + // Create file with secure permissions (readable/writable only by owner) + fs.writeFileSync(filePath, content, { encoding: "utf8", mode: 0o600 }); core.info(`Copied: ${filename}`); fileCount++; } - - core.setOutput('files-copied', fileCount.toString()); + + core.setOutput("files-copied", fileCount.toString()); core.info(`✓ Successfully copied ${fileCount} files`); - } catch (error) { core.setFailed(`Action failed: ${error.message}`); } From ecc70b5bf15099472ab2c50dfaa8e32c8efb09dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 22:54:14 +0000 Subject: [PATCH 3/5] Add Windows compatibility notes to secure file permissions Co-authored-by: eaftan <4733401+eaftan@users.noreply.github.com> --- actions/setup/src/index.js | 2 ++ pkg/workflow/claude_engine.go | 2 +- pkg/workflow/codex_engine.go | 4 ++-- pkg/workflow/copilot_engine_execution.go | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/actions/setup/src/index.js b/actions/setup/src/index.js index e47e9f7c3a..e646ec7614 100644 --- a/actions/setup/src/index.js +++ b/actions/setup/src/index.js @@ -17,6 +17,7 @@ async function run() { core.info(`Copying activation files to ${destination}`); // Create destination directory with secure permissions if it doesn't exist + // Note: mode parameter is ignored on Windows; relies on default NTFS permissions if (!fs.existsSync(destination)) { fs.mkdirSync(destination, { recursive: true, mode: 0o700 }); core.info(`Created directory: ${destination}`); @@ -28,6 +29,7 @@ async function run() { for (const [filename, content] of Object.entries(FILES)) { const filePath = path.join(destination, filename); // Create file with secure permissions (readable/writable only by owner) + // Note: mode parameter is ignored on Windows; relies on default NTFS permissions fs.writeFileSync(filePath, content, { encoding: "utf8", mode: 0o600 }); core.info(`Copied: ${filename}`); fileCount++; diff --git a/pkg/workflow/claude_engine.go b/pkg/workflow/claude_engine.go index 587e483b0c..fcf1714703 100644 --- a/pkg/workflow/claude_engine.go +++ b/pkg/workflow/claude_engine.go @@ -229,7 +229,7 @@ func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str // Use claude command directly (available in PATH from hostedtoolcache mount) commandName = "claude" } - + commandParts := []string{commandName} commandParts = append(commandParts, claudeArgs...) commandParts = append(commandParts, promptCommand) diff --git a/pkg/workflow/codex_engine.go b/pkg/workflow/codex_engine.go index ec0edecb5a..7a9d9b3fe9 100644 --- a/pkg/workflow/codex_engine.go +++ b/pkg/workflow/codex_engine.go @@ -174,7 +174,7 @@ func (e *CodexEngine) GetExecutionSteps(workflowData *WorkflowData, logFile stri // PATH will be set to find codex in hostedtoolcache when firewall is enabled commandName = "codex" } - + codexCommand := fmt.Sprintf("%s %sexec%s%s%s\"$INSTRUCTION\"", commandName, modelParam, webSearchParam, fullAutoParam, customArgsParam) @@ -316,7 +316,7 @@ mkdir -p "$CODEX_HOME/logs" } else { commandName = "codex" } - + if workflowData.AgentFile != "" { agentPath := ResolveAgentFilePath(workflowData.AgentFile) command = fmt.Sprintf(`set -o pipefail diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go index f577959c0b..c1491cc857 100644 --- a/pkg/workflow/copilot_engine_execution.go +++ b/pkg/workflow/copilot_engine_execution.go @@ -151,7 +151,7 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st if sandboxEnabled { // Build base command var baseCommand string - + // Check if custom command is specified var commandName string if workflowData.EngineConfig != nil && workflowData.EngineConfig.Command != "" { @@ -171,7 +171,7 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st commandName = "/usr/local/bin/copilot" } } - + baseCommand = fmt.Sprintf("%s %s", commandName, shellJoinArgs(copilotArgs)) // Add conditional model flag if needed @@ -189,7 +189,7 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st } else { commandName = "copilot" } - + baseCommand := fmt.Sprintf("%s %s", commandName, shellJoinArgs(copilotArgs)) // Add conditional model flag if needed From ed7b174081ed473afe76e23e864b8399077e9771 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 23:07:44 +0000 Subject: [PATCH 4/5] Use tmp library for secure temporary file creation Co-authored-by: eaftan <4733401+eaftan@users.noreply.github.com> --- actions/setup/js/package-lock.json | 18 +++++++++++++++ actions/setup/js/package.json | 1 + actions/setup/src/index.js | 37 ++++++++++++++++++++---------- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/actions/setup/js/package-lock.json b/actions/setup/js/package-lock.json index 8665673238..f34a977c18 100644 --- a/actions/setup/js/package-lock.json +++ b/actions/setup/js/package-lock.json @@ -16,6 +16,7 @@ "@vitest/ui": "^4.0.10", "prettier": "^3.6.2", "terser": "^5.44.1", + "tmp": "^0.2.5", "typescript": "^5.9.3", "vite": "^7.2.2", "vitest": "^4.0.10" @@ -775,6 +776,7 @@ "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -1315,6 +1317,7 @@ "integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1454,6 +1457,7 @@ "integrity": "sha512-oWtNM89Np+YsQO3ttT5i1Aer/0xbzQzp66NzuJn/U16bB7MnvSzdLKXgk1kkMLYyKSSzA2ajzqMkYheaE9opuQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/utils": "4.0.10", "fflate": "^0.8.2", @@ -1894,6 +1898,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -2087,6 +2092,7 @@ "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -2141,6 +2147,16 @@ "node": ">=14.0.0" } }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -2208,6 +2224,7 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -2283,6 +2300,7 @@ "integrity": "sha512-2Fqty3MM9CDwOVet/jaQalYlbcjATZwPYGcqpiYQqgQ/dLC7GuHdISKgTYIVF/kaishKxLzleKWWfbSDklyIKg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.0.10", "@vitest/mocker": "4.0.10", diff --git a/actions/setup/js/package.json b/actions/setup/js/package.json index 9a1580f044..91e74dab70 100644 --- a/actions/setup/js/package.json +++ b/actions/setup/js/package.json @@ -11,6 +11,7 @@ "@vitest/ui": "^4.0.10", "prettier": "^3.6.2", "terser": "^5.44.1", + "tmp": "^0.2.5", "typescript": "^5.9.3", "vite": "^7.2.2", "vitest": "^4.0.10" diff --git a/actions/setup/src/index.js b/actions/setup/src/index.js index e646ec7614..47a743c256 100644 --- a/actions/setup/src/index.js +++ b/actions/setup/src/index.js @@ -4,6 +4,7 @@ const core = require("@actions/core"); const fs = require("fs"); const path = require("path"); +const tmp = require("tmp"); // Embedded activation files will be inserted here during build const FILES = { @@ -12,25 +13,37 @@ const FILES = { async function run() { try { - const destination = core.getInput("destination") || "/tmp/gh-aw/actions/activation"; + const requestedDestination = core.getInput("destination") || "/tmp/gh-aw/actions/activation"; + + // Use tmp library to create secure temporary files + // This ensures files are created with secure permissions and are inaccessible to other users + let destination; + + if (requestedDestination.startsWith("/tmp")) { + // For /tmp paths, use tmp library to create a secure directory + const tmpDir = tmp.dirSync({ mode: 0o700, prefix: "gh-aw-", unsafeCleanup: false }); + destination = tmpDir.name; + core.info(`Created secure temporary directory: ${destination}`); + } else { + // For other paths, create directory with secure permissions + destination = requestedDestination; + if (!fs.existsSync(destination)) { + fs.mkdirSync(destination, { recursive: true, mode: 0o700 }); + core.info(`Created directory: ${destination}`); + } + } core.info(`Copying activation files to ${destination}`); - // Create destination directory with secure permissions if it doesn't exist - // Note: mode parameter is ignored on Windows; relies on default NTFS permissions - if (!fs.existsSync(destination)) { - fs.mkdirSync(destination, { recursive: true, mode: 0o700 }); - core.info(`Created directory: ${destination}`); - } - let fileCount = 0; - // Copy each embedded file + // Copy each embedded file using tmp library for secure file creation for (const [filename, content] of Object.entries(FILES)) { const filePath = path.join(destination, filename); - // Create file with secure permissions (readable/writable only by owner) - // Note: mode parameter is ignored on Windows; relies on default NTFS permissions - fs.writeFileSync(filePath, content, { encoding: "utf8", mode: 0o600 }); + + // Use tmp library to create secure temporary file + const tmpFile = tmp.fileSync({ mode: 0o600, dir: destination, name: filename, keep: true }); + fs.writeFileSync(tmpFile.name, content, { encoding: "utf8" }); core.info(`Copied: ${filename}`); fileCount++; } From 9cbe53f997751fdfda7b597ba1fdcd72c74ab087 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 23:15:51 +0000 Subject: [PATCH 5/5] Revert tmp library dependency - use built-in fs with secure permissions Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/package-lock.json | 18 --------------- actions/setup/js/package.json | 1 - actions/setup/src/index.js | 37 ++++++++++-------------------- 3 files changed, 12 insertions(+), 44 deletions(-) diff --git a/actions/setup/js/package-lock.json b/actions/setup/js/package-lock.json index f34a977c18..8665673238 100644 --- a/actions/setup/js/package-lock.json +++ b/actions/setup/js/package-lock.json @@ -16,7 +16,6 @@ "@vitest/ui": "^4.0.10", "prettier": "^3.6.2", "terser": "^5.44.1", - "tmp": "^0.2.5", "typescript": "^5.9.3", "vite": "^7.2.2", "vitest": "^4.0.10" @@ -776,7 +775,6 @@ "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -1317,7 +1315,6 @@ "integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1457,7 +1454,6 @@ "integrity": "sha512-oWtNM89Np+YsQO3ttT5i1Aer/0xbzQzp66NzuJn/U16bB7MnvSzdLKXgk1kkMLYyKSSzA2ajzqMkYheaE9opuQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/utils": "4.0.10", "fflate": "^0.8.2", @@ -1898,7 +1894,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2092,7 +2087,6 @@ "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -2147,16 +2141,6 @@ "node": ">=14.0.0" } }, - "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -2224,7 +2208,6 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -2300,7 +2283,6 @@ "integrity": "sha512-2Fqty3MM9CDwOVet/jaQalYlbcjATZwPYGcqpiYQqgQ/dLC7GuHdISKgTYIVF/kaishKxLzleKWWfbSDklyIKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.10", "@vitest/mocker": "4.0.10", diff --git a/actions/setup/js/package.json b/actions/setup/js/package.json index 91e74dab70..9a1580f044 100644 --- a/actions/setup/js/package.json +++ b/actions/setup/js/package.json @@ -11,7 +11,6 @@ "@vitest/ui": "^4.0.10", "prettier": "^3.6.2", "terser": "^5.44.1", - "tmp": "^0.2.5", "typescript": "^5.9.3", "vite": "^7.2.2", "vitest": "^4.0.10" diff --git a/actions/setup/src/index.js b/actions/setup/src/index.js index 47a743c256..e646ec7614 100644 --- a/actions/setup/src/index.js +++ b/actions/setup/src/index.js @@ -4,7 +4,6 @@ const core = require("@actions/core"); const fs = require("fs"); const path = require("path"); -const tmp = require("tmp"); // Embedded activation files will be inserted here during build const FILES = { @@ -13,37 +12,25 @@ const FILES = { async function run() { try { - const requestedDestination = core.getInput("destination") || "/tmp/gh-aw/actions/activation"; - - // Use tmp library to create secure temporary files - // This ensures files are created with secure permissions and are inaccessible to other users - let destination; - - if (requestedDestination.startsWith("/tmp")) { - // For /tmp paths, use tmp library to create a secure directory - const tmpDir = tmp.dirSync({ mode: 0o700, prefix: "gh-aw-", unsafeCleanup: false }); - destination = tmpDir.name; - core.info(`Created secure temporary directory: ${destination}`); - } else { - // For other paths, create directory with secure permissions - destination = requestedDestination; - if (!fs.existsSync(destination)) { - fs.mkdirSync(destination, { recursive: true, mode: 0o700 }); - core.info(`Created directory: ${destination}`); - } - } + const destination = core.getInput("destination") || "/tmp/gh-aw/actions/activation"; core.info(`Copying activation files to ${destination}`); + // Create destination directory with secure permissions if it doesn't exist + // Note: mode parameter is ignored on Windows; relies on default NTFS permissions + if (!fs.existsSync(destination)) { + fs.mkdirSync(destination, { recursive: true, mode: 0o700 }); + core.info(`Created directory: ${destination}`); + } + let fileCount = 0; - // Copy each embedded file using tmp library for secure file creation + // Copy each embedded file for (const [filename, content] of Object.entries(FILES)) { const filePath = path.join(destination, filename); - - // Use tmp library to create secure temporary file - const tmpFile = tmp.fileSync({ mode: 0o600, dir: destination, name: filename, keep: true }); - fs.writeFileSync(tmpFile.name, content, { encoding: "utf8" }); + // Create file with secure permissions (readable/writable only by owner) + // Note: mode parameter is ignored on Windows; relies on default NTFS permissions + fs.writeFileSync(filePath, content, { encoding: "utf8", mode: 0o600 }); core.info(`Copied: ${filename}`); fileCount++; }