From 851635d780d653677d863e4e31334c3254e10196 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 04:40:06 +0000 Subject: [PATCH 1/3] Initial plan From 9284a81eecb49044e4196d23e4d87463656ff570 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 04:46:05 +0000 Subject: [PATCH 2/3] Add runtime check to disallow pull_request_target event in public repositories Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/8a526311-3cd5-4a00-aa52-2ad925d5ccd7 --- .../js/validate_lockdown_requirements.cjs | 27 +++++++ .../validate_lockdown_requirements.test.cjs | 79 +++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/actions/setup/js/validate_lockdown_requirements.cjs b/actions/setup/js/validate_lockdown_requirements.cjs index 992f38dee4c..a890e53e437 100644 --- a/actions/setup/js/validate_lockdown_requirements.cjs +++ b/actions/setup/js/validate_lockdown_requirements.cjs @@ -12,6 +12,10 @@ * strict mode enabled (GH_AW_COMPILED_STRICT=true). This ensures that public * repository workflows meet the security requirements enforced by strict mode. * + * Finally, the pull_request_target event is disallowed on public repositories + * to prevent "pwn request" attacks where a fork can trigger workflows with access + * to repository secrets. + * * This validation runs at the start of the workflow to fail fast if requirements * are not met, providing clear guidance to the user. * @@ -92,6 +96,29 @@ function validateLockdownRequirements(core) { if (isPublic && isStrict) { core.info("✓ Strict mode requirements validated: Public repository compiled with strict mode"); } + + // Disallow pull_request_target event in public repositories. + // The pull_request_target event runs workflows in the context of the base repository + // with access to secrets, even when triggered from a fork. This creates a significant + // security risk in public repositories where anyone can open a pull request from a fork + // and potentially exfiltrate secrets or cause unintended side effects. + const eventName = process.env.GITHUB_EVENT_NAME; + if (isPublic && eventName === "pull_request_target") { + const errorMessage = + "This workflow is triggered by the pull_request_target event on a public repository.\\n" + + "\\n" + + "The pull_request_target event is not allowed on public repositories because it runs\\n" + + "workflows with access to repository secrets even when triggered from a fork, which\\n" + + 'creates a significant security risk (known as a \\"pwn request\\").\\n' + + "\\n" + + "To fix this, use the pull_request event instead, or migrate to a private repository.\\n" + + "\\n" + + "See: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/security.mdx"; + + core.setOutput("lockdown_check_failed", "true"); + core.setFailed(errorMessage); + throw new Error(errorMessage); + } } module.exports = validateLockdownRequirements; diff --git a/actions/setup/js/validate_lockdown_requirements.test.cjs b/actions/setup/js/validate_lockdown_requirements.test.cjs index 3d248c58675..e6e2a323ad1 100644 --- a/actions/setup/js/validate_lockdown_requirements.test.cjs +++ b/actions/setup/js/validate_lockdown_requirements.test.cjs @@ -21,6 +21,7 @@ describe("validate_lockdown_requirements", () => { delete process.env.CUSTOM_GITHUB_TOKEN; delete process.env.GITHUB_REPOSITORY_VISIBILITY; delete process.env.GH_AW_COMPILED_STRICT; + delete process.env.GITHUB_EVENT_NAME; // Import the module validateLockdownRequirements = (await import("./validate_lockdown_requirements.cjs")).default; @@ -225,4 +226,82 @@ describe("validate_lockdown_requirements", () => { expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("Lockdown mode is enabled")); }); }); + + // pull_request_target event enforcement for public repositories + describe("pull_request_target event enforcement for public repositories", () => { + it("should fail when repository is public and event is pull_request_target", () => { + process.env.GITHUB_REPOSITORY_VISIBILITY = "public"; + process.env.GH_AW_COMPILED_STRICT = "true"; + process.env.GITHUB_EVENT_NAME = "pull_request_target"; + + expect(() => { + validateLockdownRequirements(mockCore); + }).toThrow("pull_request_target"); + + expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("pull_request_target event on a public repository")); + expect(mockCore.setOutput).toHaveBeenCalledWith("lockdown_check_failed", "true"); + }); + + it("should pass when repository is public but event is pull_request (not pull_request_target)", () => { + process.env.GITHUB_REPOSITORY_VISIBILITY = "public"; + process.env.GH_AW_COMPILED_STRICT = "true"; + process.env.GITHUB_EVENT_NAME = "pull_request"; + + validateLockdownRequirements(mockCore); + + expect(mockCore.setFailed).not.toHaveBeenCalled(); + }); + + it("should pass when repository is private and event is pull_request_target", () => { + process.env.GITHUB_REPOSITORY_VISIBILITY = "private"; + process.env.GITHUB_EVENT_NAME = "pull_request_target"; + + validateLockdownRequirements(mockCore); + + expect(mockCore.setFailed).not.toHaveBeenCalled(); + }); + + it("should pass when repository is internal and event is pull_request_target", () => { + process.env.GITHUB_REPOSITORY_VISIBILITY = "internal"; + process.env.GITHUB_EVENT_NAME = "pull_request_target"; + + validateLockdownRequirements(mockCore); + + expect(mockCore.setFailed).not.toHaveBeenCalled(); + }); + + it("should pass when event is pull_request_target but visibility is unknown", () => { + // GITHUB_REPOSITORY_VISIBILITY not set + process.env.GITHUB_EVENT_NAME = "pull_request_target"; + + validateLockdownRequirements(mockCore); + + expect(mockCore.setFailed).not.toHaveBeenCalled(); + }); + + it("should include security documentation link in pull_request_target error message", () => { + process.env.GITHUB_REPOSITORY_VISIBILITY = "public"; + process.env.GH_AW_COMPILED_STRICT = "true"; + process.env.GITHUB_EVENT_NAME = "pull_request_target"; + + expect(() => { + validateLockdownRequirements(mockCore); + }).toThrow(); + + expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/security.mdx")); + }); + + it("should fail on strict mode check before pull_request_target check when both fail", () => { + process.env.GITHUB_REPOSITORY_VISIBILITY = "public"; + process.env.GH_AW_COMPILED_STRICT = "false"; + process.env.GITHUB_EVENT_NAME = "pull_request_target"; + + expect(() => { + validateLockdownRequirements(mockCore); + }).toThrow("not compiled with strict mode"); + + // pull_request_target error should not be reached since strict mode check throws first + expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("not compiled with strict mode")); + }); + }); }); From 38ee5707606f7aa528f5a770c07ee2b74716479a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 05:10:50 +0000 Subject: [PATCH 3/3] Fix escaped quotes in pull_request_target error message Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/0dab3c2a-f1b9-48a4-bdff-b3d5f4a09c9f --- actions/setup/js/validate_lockdown_requirements.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/setup/js/validate_lockdown_requirements.cjs b/actions/setup/js/validate_lockdown_requirements.cjs index a890e53e437..14f74a99b30 100644 --- a/actions/setup/js/validate_lockdown_requirements.cjs +++ b/actions/setup/js/validate_lockdown_requirements.cjs @@ -109,7 +109,7 @@ function validateLockdownRequirements(core) { "\\n" + "The pull_request_target event is not allowed on public repositories because it runs\\n" + "workflows with access to repository secrets even when triggered from a fork, which\\n" + - 'creates a significant security risk (known as a \\"pwn request\\").\\n' + + 'creates a significant security risk (known as a "pwn request").\\n' + "\\n" + "To fix this, use the pull_request event instead, or migrate to a private repository.\\n" + "\\n" +