From 4e044fa95775b1fa24159e5a80c63fd3ac72a716 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 05:00:45 +0000 Subject: [PATCH 1/4] jsweep: modernize validate_secrets.cjs with fetch API Replace legacy https.request Promise wrappers in makeRequest and makePostRequest with native fetch + AbortSignal.timeout(). Remove the unused https require. Update tests to mock fetch instead of https.request. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- actions/setup/js/validate_secrets.cjs | 78 +++++----------------- actions/setup/js/validate_secrets.test.cjs | 63 ++++++----------- 2 files changed, 37 insertions(+), 104 deletions(-) diff --git a/actions/setup/js/validate_secrets.cjs b/actions/setup/js/validate_secrets.cjs index 0ae4a79c32f..51ae5a72c13 100644 --- a/actions/setup/js/validate_secrets.cjs +++ b/actions/setup/js/validate_secrets.cjs @@ -14,7 +14,6 @@ */ const fs = require("fs"); -const https = require("https"); const { promisify } = require("util"); const { exec } = require("child_process"); const execAsync = promisify(exec); @@ -47,80 +46,39 @@ const SECRET_DOCS = { }; /** - * Make an HTTPS request + * Make an HTTPS GET request * @param {string} hostname * @param {string} path - * @param {Object} headers - * @param {string} method + * @param {Record} headers * @returns {Promise<{statusCode: number, data: string}>} */ -function makeRequest(hostname, path, headers, method = "GET") { - return new Promise((resolve, reject) => { - const options = { - hostname, - path, - method, - headers, - }; - - const req = https.request(options, res => { - let data = ""; - res.on("data", chunk => { - data += chunk; - }); - res.on("end", () => { - resolve({ statusCode: res.statusCode || 0, data }); - }); - }); - - req.on("error", err => { - reject(err); - }); - - req.setTimeout(10000, () => { - req.destroy(); - reject(new Error("Request timeout")); - }); - - req.end(); +async function makeRequest(hostname, path, headers) { + const res = await fetch(`https://${hostname}${path}`, { + method: "GET", + headers, + signal: AbortSignal.timeout(10000), }); + const data = await res.text(); + return { statusCode: res.status, data }; } /** * Make an HTTPS POST request * @param {string} hostname * @param {string} path - * @param {Object} headers + * @param {Record} headers * @param {string} body * @returns {Promise<{statusCode: number, data: string}>} */ -function makePostRequest(hostname, path, headers, body) { - return new Promise((resolve, reject) => { - const options = { - hostname, - path, - method: "POST", - headers: { ...headers, "Content-Length": Buffer.byteLength(body) }, - }; - - const req = https.request(options, res => { - let data = ""; - res.on("data", chunk => { - data += chunk; - }); - res.on("end", () => { - resolve({ statusCode: res.statusCode || 0, data }); - }); - }); - - req.on("error", reject); - req.setTimeout(10000, () => { - req.destroy(); - reject(new Error("Request timeout")); - }); - req.write(body); - req.end(); +async function makePostRequest(hostname, path, headers, body) { + const res = await fetch(`https://${hostname}${path}`, { + method: "POST", + headers: { ...headers, "Content-Type": headers["Content-Type"] || "application/json" }, + body, + signal: AbortSignal.timeout(10000), }); + const data = await res.text(); + return { statusCode: res.status, data }; } /** diff --git a/actions/setup/js/validate_secrets.test.cjs b/actions/setup/js/validate_secrets.test.cjs index d3dada3c9fe..5ad94a96b03 100644 --- a/actions/setup/js/validate_secrets.test.cjs +++ b/actions/setup/js/validate_secrets.test.cjs @@ -1,8 +1,6 @@ // @ts-check import { describe, it, expect, vi, afterEach } from "vitest"; -import https from "https"; -import { EventEmitter } from "events"; import { makePostRequest, testGitHubRESTAPI, @@ -121,65 +119,42 @@ describe("validate_secrets", () => { }); describe("makePostRequest", () => { - /** @type {EventEmitter & {setTimeout: any, destroy: any, write: any, end: any, timeoutCallback?: () => void}} */ - let mockRequest; - /** @type {EventEmitter & {statusCode: number}} */ - let mockResponse; - afterEach(() => { vi.restoreAllMocks(); }); - function setupHttpsMock(onEnd) { - mockResponse = Object.assign(new EventEmitter(), { statusCode: 200 }); - mockRequest = Object.assign(new EventEmitter(), { - setTimeout: vi.fn().mockImplementation((ms, cb) => { - mockRequest.timeoutCallback = cb; - }), - destroy: vi.fn(), - write: vi.fn(), - end: vi.fn().mockImplementation(() => { - if (onEnd) onEnd(); - }), - }); - vi.spyOn(https, "request").mockImplementation((_options, callback) => { - process.nextTick(() => callback?.(/** @type {any} */ mockResponse)); - return /** @type {any} */ mockRequest; - }); + /** + * @param {number} statusCode + * @param {string} responseBody + */ + function mockFetch(statusCode, responseBody) { + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ + status: statusCode, + text: () => Promise.resolve(responseBody), + }) + ); } it("resolves with statusCode and data on success", async () => { - setupHttpsMock(() => { - process.nextTick(() => { - mockResponse.emit("data", '{"ok":true}'); - mockResponse.emit("end"); - }); - }); + mockFetch(200, '{"ok":true}'); const result = await makePostRequest("api.example.com", "/v1/test", { "Content-Type": "application/json" }, '{"query":"test"}'); expect(result.statusCode).toBe(200); expect(result.data).toBe('{"ok":true}'); }); - it("rejects on request error", async () => { - setupHttpsMock(null); - const networkError = new Error("connection refused"); - - const promise = makePostRequest("api.example.com", "/v1/test", {}, "{}"); - process.nextTick(() => mockRequest.emit("error", networkError)); + it("rejects on network error", async () => { + vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("connection refused"))); - await expect(promise).rejects.toThrow("connection refused"); + await expect(makePostRequest("api.example.com", "/v1/test", {}, "{}")).rejects.toThrow("connection refused"); }); - it("rejects with timeout error after 10 s", async () => { - setupHttpsMock(null); - - const promise = makePostRequest("api.example.com", "/v1/test", {}, "{}"); - // Trigger the timeout callback registered via req.setTimeout - process.nextTick(() => mockRequest.timeoutCallback?.()); + it("rejects with abort error on timeout", async () => { + vi.stubGlobal("fetch", vi.fn().mockRejectedValue(Object.assign(new Error("The operation was aborted"), { name: "AbortError" }))); - await expect(promise).rejects.toThrow("Request timeout"); - expect(mockRequest.destroy).toHaveBeenCalled(); + await expect(makePostRequest("api.example.com", "/v1/test", {}, "{}")).rejects.toThrow(); }); }); From 56d09476c54703d5fdaa55d6e01d3eb6a598016e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:46:35 +0000 Subject: [PATCH 2/4] plan: address reviewer feedback on validate_secrets Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/sighthound-security-scan.lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sighthound-security-scan.lock.yml b/.github/workflows/sighthound-security-scan.lock.yml index 1fa6e597a0a..8d42c68ef95 100644 --- a/.github/workflows/sighthound-security-scan.lock.yml +++ b/.github/workflows/sighthound-security-scan.lock.yml @@ -1454,7 +1454,7 @@ jobs: export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.30/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"fable\",\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.30,squid=sha256:eb74fca5309c7542df0f32aef41b92c728ecef7ac3b0cca9f9a6b97cc324d22a,agent=sha256:3cc1e14efa9e52ed1fc29d72a0eabf02ff86b30c6af9685fa9ddd687caca6613,agent-act=sha256:d0beee47dd38e2df577d15b9ddf763a2b771367326fecb02e1aa924bc395954a,api-proxy=sha256:fdbd94bb668ed736a27c146633842db5c7e658dc7a0d6a0e6011e74e18132785,cli-proxy=sha256:959c876217038ac6f9c2047b591e819e5f1f726625a34490ccdcacaa71d83e4c\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.31/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"fable\",\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.31,squid=sha256:c05a3f086946fab0833e078f46d35571080f187ca72f038958d45aa5cc150494,agent=sha256:84d861cb6da723ac10b7a00dddf778be681b8cd74b2091f18ce1d67fe4b3e7a1,agent-act=sha256:58fee05c1c54ba5ca1e7056b3aaea30281841d5899093002e2c650710c50540f,api-proxy=sha256:80d982fe7925c640d76cbbfbe94081d2d34f7657b7c37494d8d5488f5dae3c63,cli-proxy=sha256:7c63bc4e57d6eac1be996bb793a5a2d74d40b15a616003f4b6805a457046c673\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" GH_AW_DOCKER_HOST="" From 78a0b78a7fdcef5f9a5becae6c45ab0c56a70ad7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:41:20 +0000 Subject: [PATCH 3/4] fix: format eslint-factory rules files to pass lint-cjs check Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../src/rules/no-core-exportvariable-non-string.test.ts | 1 - .../src/rules/no-core-exportvariable-non-string.ts | 4 +--- .../src/rules/require-spawnsync-error-check.test.ts | 9 ++------- .../src/rules/require-spawnsync-error-check.ts | 9 +-------- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/eslint-factory/src/rules/no-core-exportvariable-non-string.test.ts b/eslint-factory/src/rules/no-core-exportvariable-non-string.test.ts index 9e4ce201a64..fedfc24b12b 100644 --- a/eslint-factory/src/rules/no-core-exportvariable-non-string.test.ts +++ b/eslint-factory/src/rules/no-core-exportvariable-non-string.test.ts @@ -168,5 +168,4 @@ describe("no-core-exportvariable-non-string", () => { ], }); }); - }); diff --git a/eslint-factory/src/rules/no-core-exportvariable-non-string.ts b/eslint-factory/src/rules/no-core-exportvariable-non-string.ts index 09aaee563e9..6ac483ad7c3 100644 --- a/eslint-factory/src/rules/no-core-exportvariable-non-string.ts +++ b/eslint-factory/src/rules/no-core-exportvariable-non-string.ts @@ -75,9 +75,7 @@ export const noCoreExportVariableNonStringRule = createRule({ // Property must be `exportVariable` (direct or computed string-literal access) const prop = callee.property; - const isExportVariableProp = - (!callee.computed && prop.type === AST_NODE_TYPES.Identifier && prop.name === "exportVariable") || - (callee.computed && prop.type === AST_NODE_TYPES.Literal && prop.value === "exportVariable"); + const isExportVariableProp = (!callee.computed && prop.type === AST_NODE_TYPES.Identifier && prop.name === "exportVariable") || (callee.computed && prop.type === AST_NODE_TYPES.Literal && prop.value === "exportVariable"); if (!isExportVariableProp) return; // core.exportVariable expects exactly two arguments: (name, value) diff --git a/eslint-factory/src/rules/require-spawnsync-error-check.test.ts b/eslint-factory/src/rules/require-spawnsync-error-check.test.ts index 075a2fbc7ed..d0fb03aa83c 100644 --- a/eslint-factory/src/rules/require-spawnsync-error-check.test.ts +++ b/eslint-factory/src/rules/require-spawnsync-error-check.test.ts @@ -11,9 +11,7 @@ const cjsRuleTester = new RuleTester({ describe("require-spawnsync-error-check", () => { it("uses the correct docs URL", () => { - expect(requireSpawnSyncErrorCheckRule.meta.docs.url).toBe( - "https://github.com/github/gh-aw/tree/main/eslint-factory#require-spawnsync-error-check", - ); + expect(requireSpawnSyncErrorCheckRule.meta.docs.url).toBe("https://github.com/github/gh-aw/tree/main/eslint-factory#require-spawnsync-error-check"); }); it("valid: result.error is checked alongside result.status", () => { @@ -54,10 +52,7 @@ describe("require-spawnsync-error-check", () => { it("valid: non-spawnSync call is ignored", () => { cjsRuleTester.run("require-spawnsync-error-check", requireSpawnSyncErrorCheckRule, { - valid: [ - `const result = execSync("git status"); if (!result) throw new Error("failed");`, - `const result = spawnSync; result.toString();`, - ], + valid: [`const result = execSync("git status"); if (!result) throw new Error("failed");`, `const result = spawnSync; result.toString();`], invalid: [], }); }); diff --git a/eslint-factory/src/rules/require-spawnsync-error-check.ts b/eslint-factory/src/rules/require-spawnsync-error-check.ts index ee7f7b658f1..727b87382c9 100644 --- a/eslint-factory/src/rules/require-spawnsync-error-check.ts +++ b/eslint-factory/src/rules/require-spawnsync-error-check.ts @@ -84,14 +84,7 @@ export const requireSpawnSyncErrorCheckRule = createRule({ const hasErrorCheck = variable.references.some(ref => { const id = ref.identifier; const parent = id.parent; - return ( - parent !== undefined && - parent.type === AST_NODE_TYPES.MemberExpression && - !parent.computed && - parent.object === id && - parent.property.type === AST_NODE_TYPES.Identifier && - parent.property.name === "error" - ); + return parent !== undefined && parent.type === AST_NODE_TYPES.MemberExpression && !parent.computed && parent.object === id && parent.property.type === AST_NODE_TYPES.Identifier && parent.property.name === "error"; }); if (!hasErrorCheck) { From 24b78012f980e37318d205716b26c825661dc591 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 19:00:35 +0000 Subject: [PATCH 4/4] fix: normalize AbortError, case-insensitive Content-Type, fix test stub leak Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/validate_secrets.cjs | 48 ++++++++++++++-------- actions/setup/js/validate_secrets.test.cjs | 6 +-- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/actions/setup/js/validate_secrets.cjs b/actions/setup/js/validate_secrets.cjs index 51ae5a72c13..7bb18d30899 100644 --- a/actions/setup/js/validate_secrets.cjs +++ b/actions/setup/js/validate_secrets.cjs @@ -53,32 +53,48 @@ const SECRET_DOCS = { * @returns {Promise<{statusCode: number, data: string}>} */ async function makeRequest(hostname, path, headers) { - const res = await fetch(`https://${hostname}${path}`, { - method: "GET", - headers, - signal: AbortSignal.timeout(10000), - }); - const data = await res.text(); - return { statusCode: res.status, data }; + try { + const res = await fetch(`https://${hostname}${path}`, { + method: "GET", + headers, + signal: AbortSignal.timeout(10000), + }); + const data = await res.text(); + return { statusCode: res.status, data }; + } catch (err) { + if (err instanceof Error && err.name === "AbortError") { + throw new Error("Request timeout"); + } + throw err; + } } /** * Make an HTTPS POST request * @param {string} hostname * @param {string} path - * @param {Record} headers + * @param {Record} headers - If no `Content-Type` key is present (case-insensitive), + * defaults to `application/json`. Pass an explicit `Content-Type` to override. * @param {string} body * @returns {Promise<{statusCode: number, data: string}>} */ async function makePostRequest(hostname, path, headers, body) { - const res = await fetch(`https://${hostname}${path}`, { - method: "POST", - headers: { ...headers, "Content-Type": headers["Content-Type"] || "application/json" }, - body, - signal: AbortSignal.timeout(10000), - }); - const data = await res.text(); - return { statusCode: res.status, data }; + const hasContentType = Object.keys(headers).some(k => k.toLowerCase() === "content-type"); + try { + const res = await fetch(`https://${hostname}${path}`, { + method: "POST", + headers: hasContentType ? headers : { ...headers, "Content-Type": "application/json" }, + body, + signal: AbortSignal.timeout(10000), + }); + const data = await res.text(); + return { statusCode: res.status, data }; + } catch (err) { + if (err instanceof Error && err.name === "AbortError") { + throw new Error("Request timeout"); + } + throw err; + } } /** diff --git a/actions/setup/js/validate_secrets.test.cjs b/actions/setup/js/validate_secrets.test.cjs index 5ad94a96b03..8711f1c0487 100644 --- a/actions/setup/js/validate_secrets.test.cjs +++ b/actions/setup/js/validate_secrets.test.cjs @@ -120,7 +120,7 @@ describe("validate_secrets", () => { describe("makePostRequest", () => { afterEach(() => { - vi.restoreAllMocks(); + vi.unstubAllGlobals(); }); /** @@ -151,10 +151,10 @@ describe("validate_secrets", () => { await expect(makePostRequest("api.example.com", "/v1/test", {}, "{}")).rejects.toThrow("connection refused"); }); - it("rejects with abort error on timeout", async () => { + it("rejects with timeout error on abort", async () => { vi.stubGlobal("fetch", vi.fn().mockRejectedValue(Object.assign(new Error("The operation was aborted"), { name: "AbortError" }))); - await expect(makePostRequest("api.example.com", "/v1/test", {}, "{}")).rejects.toThrow(); + await expect(makePostRequest("api.example.com", "/v1/test", {}, "{}")).rejects.toThrow("Request timeout"); }); });