From 6529cdb4a8cf5bb5a220798e0976d48ff44e6931 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:43:38 +0000 Subject: [PATCH 1/2] Initial plan From 44fdd2430b68fc7a19d94dbe1775a9180063a430 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:58:48 +0000 Subject: [PATCH 2/2] Fix workflow_dispatch target repo validation fallback Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../setup/js/invocation_context_helpers.cjs | 27 ++++++++++++++ .../js/invocation_context_helpers.test.cjs | 36 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/actions/setup/js/invocation_context_helpers.cjs b/actions/setup/js/invocation_context_helpers.cjs index 930603450da..7c01161633f 100644 --- a/actions/setup/js/invocation_context_helpers.cjs +++ b/actions/setup/js/invocation_context_helpers.cjs @@ -161,6 +161,33 @@ function checkAllowedRepo(workflowRepo, targetRepo) { const defaultRepo = `${workflowRepo.owner}/${workflowRepo.repo}`; const targetRepoSlug = `${targetRepo.owner}/${targetRepo.repo}`; const allowedRepos = parseAllowedRepos(process.env.GH_AW_ALLOWED_REPOS); + + // Fall back to per-handler safe-output allowlists when a global allowlist is + // not provided. This prevents post-action context resolution from rejecting + // a repository that was already allowed by the active safe-output handler. + if (allowedRepos.size === 0) { + const handlerConfigRaw = process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG; + if (typeof handlerConfigRaw === "string" && handlerConfigRaw.trim() !== "") { + try { + const handlerConfig = JSON.parse(handlerConfigRaw); + if (handlerConfig && typeof handlerConfig === "object" && !Array.isArray(handlerConfig)) { + for (const value of Object.values(handlerConfig)) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + continue; + } + const parsed = parseAllowedRepos(value.allowed_repos); + for (const repo of parsed) { + allowedRepos.add(repo); + } + } + } + } catch (_error) { + // Best-effort only. If the handler config cannot be parsed, continue + // with the global allowlist (if any). + } + } + } + const validation = validateTargetRepo(targetRepoSlug, defaultRepo, allowedRepos); if (!validation.valid) { throw new Error(`${ERR_VALIDATION}: ${validation.error}`); diff --git a/actions/setup/js/invocation_context_helpers.test.cjs b/actions/setup/js/invocation_context_helpers.test.cjs index 541c4fe92eb..81b78706906 100644 --- a/actions/setup/js/invocation_context_helpers.test.cjs +++ b/actions/setup/js/invocation_context_helpers.test.cjs @@ -118,6 +118,42 @@ describe("invocation_context_helpers", () => { } }); + it("allows workflow_dispatch target_repo when handler allowlist includes it", () => { + const originalAllowedRepos = process.env.GH_AW_ALLOWED_REPOS; + const originalHandlerConfig = process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG; + try { + delete process.env.GH_AW_ALLOWED_REPOS; + process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG = JSON.stringify({ + create_pull_request: { + allowed_repos: ["target-owner/target-repo"], + }, + }); + + const resolved = resolveInvocationContext({ + eventName: "workflow_dispatch", + repo: { owner: "side-owner", repo: "side-repo" }, + payload: { + inputs: { + target_repo: "target-owner/target-repo", + }, + }, + }); + + expect(resolved.eventRepo).toEqual({ owner: "target-owner", repo: "target-repo" }); + } finally { + if (originalAllowedRepos === undefined) { + delete process.env.GH_AW_ALLOWED_REPOS; + } else { + process.env.GH_AW_ALLOWED_REPOS = originalAllowedRepos; + } + if (originalHandlerConfig === undefined) { + delete process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG; + } else { + process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG = originalHandlerConfig; + } + } + }); + it("allows workflow_dispatch without target_repo inputs", () => { const resolved = resolveInvocationContext({ eventName: "workflow_dispatch",