Skip to content

Commit f3b2fc7

Browse files
Copilotpelikhan
andauthored
fix: patch_format runtime validation security and correctness improvements
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/f35fc6e6-684c-42d5-82d5-3e2ccca5b4b5 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
1 parent 81fc48f commit f3b2fc7

2 files changed

Lines changed: 50 additions & 6 deletions

File tree

actions/setup/js/safe_outputs_handlers.cjs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,12 @@ function createHandlers(server, appendSafeOutput, config = {}) {
319319

320320
// Determine transport format: "bundle" uses git bundle (preserves merge topology),
321321
// "am" (default) uses git format-patch / git am (good for linear histories).
322-
const patchFormat = prConfig["patch_format"] || config["patch_format"] || "am";
322+
// Use ?? (nullish coalescing) so an empty-string resolved value is preserved and
323+
// rejected below rather than silently falling back to "am".
324+
const patchFormat = prConfig["patch_format"] ?? config["patch_format"] ?? "am";
323325
const validPatchFormats = ["am", "bundle"];
324326
if (!validPatchFormats.includes(patchFormat)) {
325-
const errorMsg = `Invalid patch_format: "${patchFormat}". Must be one of: ${validPatchFormats.join(", ")}`;
327+
const errorMsg = `Invalid patch_format in configuration. Must be one of: ${validPatchFormats.join(", ")}`;
326328
server.debug(`create_pull_request: ${errorMsg}`);
327329
return {
328330
content: [
@@ -547,10 +549,12 @@ function createHandlers(server, appendSafeOutput, config = {}) {
547549

548550
// Determine transport format: "bundle" uses git bundle (preserves merge topology),
549551
// "am" (default) uses git format-patch / git am (good for linear histories).
550-
const pushPatchFormat = pushConfig["patch_format"] || config["patch_format"] || "am";
552+
// Use ?? (nullish coalescing) so an empty-string resolved value is preserved and
553+
// rejected below rather than silently falling back to "am".
554+
const pushPatchFormat = pushConfig["patch_format"] ?? config["patch_format"] ?? "am";
551555
const validPushPatchFormats = ["am", "bundle"];
552556
if (!validPushPatchFormats.includes(pushPatchFormat)) {
553-
const errorMsg = `Invalid patch_format: "${pushPatchFormat}". Must be one of: ${validPushPatchFormats.join(", ")}`;
557+
const errorMsg = `Invalid patch_format in configuration. Must be one of: ${validPushPatchFormats.join(", ")}`;
554558
server.debug(`push_to_pull_request_branch: ${errorMsg}`);
555559
return {
556560
content: [

actions/setup/js/safe_outputs_handlers.test.cjs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,12 +603,33 @@ describe("safe_outputs_handlers", () => {
603603
const responseData = JSON.parse(result.content[0].text);
604604
expect(responseData.result).toBe("error");
605605
expect(responseData.error).toContain("Invalid patch_format");
606-
expect(responseData.error).toContain("invalid-format");
607606
expect(responseData.error).toContain("am");
608607
expect(responseData.error).toContain("bundle");
608+
// Must not echo the raw resolved value (could be a secret expression result)
609+
expect(responseData.error).not.toContain("invalid-format");
609610
// Must not have appended any safe output
610611
expect(mockAppendSafeOutput).not.toHaveBeenCalled();
611612
});
613+
614+
it("should fail closed when patch_format resolves to an empty string", async () => {
615+
handlers = createHandlers(mockServer, mockAppendSafeOutput, {
616+
create_pull_request: {
617+
patch_format: "",
618+
},
619+
});
620+
621+
const result = await handlers.createPullRequestHandler({
622+
branch: "feature-branch",
623+
title: "Test PR",
624+
body: "Test description",
625+
});
626+
627+
expect(result.isError).toBe(true);
628+
const responseData = JSON.parse(result.content[0].text);
629+
expect(responseData.result).toBe("error");
630+
expect(responseData.error).toContain("Invalid patch_format");
631+
expect(mockAppendSafeOutput).not.toHaveBeenCalled();
632+
});
612633
});
613634

614635
describe("pushToPullRequestBranchHandler", () => {
@@ -818,12 +839,31 @@ describe("safe_outputs_handlers", () => {
818839
const responseData = JSON.parse(result.content[0].text);
819840
expect(responseData.result).toBe("error");
820841
expect(responseData.error).toContain("Invalid patch_format");
821-
expect(responseData.error).toContain("invalid-format");
822842
expect(responseData.error).toContain("am");
823843
expect(responseData.error).toContain("bundle");
844+
// Must not echo the raw resolved value (could be a secret expression result)
845+
expect(responseData.error).not.toContain("invalid-format");
824846
// Must not have appended any safe output
825847
expect(mockAppendSafeOutput).not.toHaveBeenCalled();
826848
});
849+
850+
it("should fail closed when patch_format resolves to an empty string", async () => {
851+
handlers = createHandlers(mockServer, mockAppendSafeOutput, {
852+
push_to_pull_request_branch: {
853+
patch_format: "",
854+
},
855+
});
856+
857+
const result = await handlers.pushToPullRequestBranchHandler({
858+
branch: "feature-branch",
859+
});
860+
861+
expect(result.isError).toBe(true);
862+
const responseData = JSON.parse(result.content[0].text);
863+
expect(responseData.result).toBe("error");
864+
expect(responseData.error).toContain("Invalid patch_format");
865+
expect(mockAppendSafeOutput).not.toHaveBeenCalled();
866+
});
827867
});
828868

829869
describe("handler structure", () => {

0 commit comments

Comments
 (0)