From d001753916a874728e491b1d0a3065fca54eb8ed Mon Sep 17 00:00:00 2001 From: Daniel Pinheiro Date: Mon, 17 Aug 2026 14:22:27 -0700 Subject: [PATCH 1/2] Resolve Semgrep findings on dev The push-to-dev Semgrep job does a full-repo scan and blocks on all findings, so a standing set of 18 kept it red. Addressing them: - Sanitize saved-request file names with Path.GetFileName in both test tools so a crafted name cannot escape the request directory (unsafe-path-combine). - Add a cooldown to the github-actions dependabot config (dependabot-missing-cooldown). - Ignore the TestWebApp integration-test host (intentionally unauthenticated fixtures) and the v1 preview test tool's Blazor debug UI (developer exception page is intended) in .semgrepignore, since a code "fix" there would break the intended behavior. --- .github/dependabot.yml | 4 ++++ .semgrepignore | 8 ++++++++ .../Services/LambdaRequestManager.cs | 3 ++- .../SampleRequests/SampleRequestManager.cs | 3 ++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5141e8016..b3b0e9b90 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,10 @@ updates: schedule: # Check for updates to GitHub Actions every quarter interval: "quarterly" + # Wait a few days after a release before opening an update PR, to avoid + # picking up versions that are pulled shortly after publishing. + cooldown: + default-days: 7 labels: - "Release Not Needed" target-branch: "dev" diff --git a/.semgrepignore b/.semgrepignore index 5a02b9ea5..d820d0511 100644 --- a/.semgrepignore +++ b/.semgrepignore @@ -7,6 +7,14 @@ **/*.min.js **/env.configs.yml +# Ignore the ASP.NET integration-test host app. Its controllers are intentionally +# unauthenticated fixtures used to exercise the Lambda bridge and are never shipped. +**/test/TestWebApp/** + +# Ignore the v1 (preview) test tool's Blazor debug UI. It intentionally shows the +# developer exception page since its purpose is local debugging of Lambda code. +**/Amazon.Lambda.TestTool.BlazorTester/** + # Ignore third-party libraries **/node_modules/** **/vendor/** diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Services/LambdaRequestManager.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Services/LambdaRequestManager.cs index 9386a82e4..3f2f14d49 100644 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Services/LambdaRequestManager.cs +++ b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Services/LambdaRequestManager.cs @@ -85,7 +85,8 @@ public string GetRequest(string functionName, string requestName) if(requestName.StartsWith(Constants.SavedRequestDirectory + "@")) { requestName = requestName.Substring(requestName.IndexOf("@", StringComparison.Ordinal) + 1); - var path = Path.Combine(requestDirectory, requestName); + // Use only the file name so a crafted request name cannot escape requestDirectory. + var path = Path.Combine(requestDirectory, Path.GetFileName(requestName)); return File.ReadAllText(path); } return GetEmbeddedResource(requestName); diff --git a/Tools/LambdaTestTool/src/Amazon.Lambda.TestTool/SampleRequests/SampleRequestManager.cs b/Tools/LambdaTestTool/src/Amazon.Lambda.TestTool/SampleRequests/SampleRequestManager.cs index c7f978d97..8da59a282 100644 --- a/Tools/LambdaTestTool/src/Amazon.Lambda.TestTool/SampleRequests/SampleRequestManager.cs +++ b/Tools/LambdaTestTool/src/Amazon.Lambda.TestTool/SampleRequests/SampleRequestManager.cs @@ -102,7 +102,8 @@ public string GetRequest(string name) if(name.StartsWith(SAVED_REQUEST_DIRECTORY + "@")) { name = name.Substring(name.IndexOf("@") + 1); - var path = Path.Combine(this.GetSavedRequestDirectory(), name); + // Use only the file name so a crafted request name cannot escape the saved-request directory. + var path = Path.Combine(this.GetSavedRequestDirectory(), Path.GetFileName(name)); return File.ReadAllText(path); } return GetEmbeddedResource(name); From 9ec11e399a4702381b7d014f5865ca90f131ed30 Mon Sep 17 00:00:00 2001 From: Daniel Pinheiro Date: Mon, 17 Aug 2026 14:37:21 -0700 Subject: [PATCH 2/2] Address PR review: ordinal comparisons and path-traversal test Use ordinal StartsWith/IndexOf for the saved-request routing token in both test tools (it is a fixed internal token on a file-path security boundary). Add a unit test covering that a traversal-style saved-request name cannot read outside the saved-request directory. --- .../Services/LambdaRequestManager.cs | 2 +- .../SampleRequests/SampleRequestManager.cs | 4 ++-- .../SampleRequestTests.cs | 24 +++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Services/LambdaRequestManager.cs b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Services/LambdaRequestManager.cs index 3f2f14d49..1878fe233 100644 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Services/LambdaRequestManager.cs +++ b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Services/LambdaRequestManager.cs @@ -82,7 +82,7 @@ public string GetRequest(string functionName, string requestName) if (requestDirectory is null) return string.Empty; - if(requestName.StartsWith(Constants.SavedRequestDirectory + "@")) + if(requestName.StartsWith(Constants.SavedRequestDirectory + "@", StringComparison.Ordinal)) { requestName = requestName.Substring(requestName.IndexOf("@", StringComparison.Ordinal) + 1); // Use only the file name so a crafted request name cannot escape requestDirectory. diff --git a/Tools/LambdaTestTool/src/Amazon.Lambda.TestTool/SampleRequests/SampleRequestManager.cs b/Tools/LambdaTestTool/src/Amazon.Lambda.TestTool/SampleRequests/SampleRequestManager.cs index 8da59a282..c6edfad07 100644 --- a/Tools/LambdaTestTool/src/Amazon.Lambda.TestTool/SampleRequests/SampleRequestManager.cs +++ b/Tools/LambdaTestTool/src/Amazon.Lambda.TestTool/SampleRequests/SampleRequestManager.cs @@ -99,9 +99,9 @@ public static bool TryDetermineSampleRequestName(string value, out string sample public string GetRequest(string name) { - if(name.StartsWith(SAVED_REQUEST_DIRECTORY + "@")) + if(name.StartsWith(SAVED_REQUEST_DIRECTORY + "@", StringComparison.Ordinal)) { - name = name.Substring(name.IndexOf("@") + 1); + name = name.Substring(name.IndexOf('@') + 1); // Use only the file name so a crafted request name cannot escape the saved-request directory. var path = Path.Combine(this.GetSavedRequestDirectory(), Path.GetFileName(name)); return File.ReadAllText(path); diff --git a/Tools/LambdaTestTool/tests/Amazon.Lambda.TestTool.Tests/SampleRequestTests.cs b/Tools/LambdaTestTool/tests/Amazon.Lambda.TestTool.Tests/SampleRequestTests.cs index 41d7e1d15..2197de07a 100644 --- a/Tools/LambdaTestTool/tests/Amazon.Lambda.TestTool.Tests/SampleRequestTests.cs +++ b/Tools/LambdaTestTool/tests/Amazon.Lambda.TestTool.Tests/SampleRequestTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; using Xunit; @@ -26,5 +27,28 @@ public void DetermineSampleName(string testValue, string expected) Assert.Null(expected); } } + + [Fact] + public void GetRequest_WithTraversalName_StaysWithinSavedRequestDirectory() + { + var preferenceDirectory = Path.Combine(Path.GetTempPath(), "LambdaTestToolTests", Guid.NewGuid().ToString()); + var savedRequestDirectory = Path.Combine(preferenceDirectory, SampleRequestManager.SAVED_REQUEST_DIRECTORY); + Directory.CreateDirectory(savedRequestDirectory); + try + { + File.WriteAllText(Path.Combine(savedRequestDirectory, "target.json"), "INSIDE"); + File.WriteAllText(Path.Combine(preferenceDirectory, "target.json"), "OUTSIDE"); + + var manager = new SampleRequestManager(preferenceDirectory); + + var content = manager.GetRequest($"{SampleRequestManager.SAVED_REQUEST_DIRECTORY}@../target.json"); + + Assert.Equal("INSIDE", content); + } + finally + { + Directory.Delete(preferenceDirectory, recursive: true); + } + } } }