Skip to content

test(storage): add explicit test coverage for GHSA-h7c7-3mfc-m7pj forward-slash path traversal#16593

Open
AcevedoR wants to merge 1 commit into
developfrom
fix/GHSA-h7c7-3mfc-m7pj-path-traversal-tests
Open

test(storage): add explicit test coverage for GHSA-h7c7-3mfc-m7pj forward-slash path traversal#16593
AcevedoR wants to merge 1 commit into
developfrom
fix/GHSA-h7c7-3mfc-m7pj-path-traversal-tests

Conversation

@AcevedoR

@AcevedoR AcevedoR commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds dedicated test cases for GHSA-h7c7-3mfc-m7pj — the Windows-specific path traversal where File.separator (\ on Windows) was used in the guard, causing forward-slash HTTP URI payloads like /x/../../../foo.txt to bypass it entirely
  • The fix was shipped in commit b41d0ceeeb as part of GHSA-qw4v-6w32-xx9h, but carried no test cases for this specific forward-slash / Windows-JVM attack surface
  • The reporter correctly pointed out the gap; this PR closes it

What changed

  • FileUtilsTest — adds forward-slash traversal cases (/x/../../../escaped.txt, etc.) to both the URI and String parameterized tests, labeled GHSA-h7c7-3mfc-m7pj
  • NamespaceFileTest — adds shouldThrowOnForwardSlashPathTraversal() asserting that NamespaceFile.normalize() rejects these payloads; the toLogicalPath() + FileUtils.isParentTraversal() chain normalizes \/ regardless of host OS, so forward-slash /../ segments are always caught
  • NamespaceFileControllerTest — adds shouldRejectForwardSlashPathTraversalOnWriteAndDelete() exercising the two primitives the advisory demonstrated end-to-end: POST .../files?path=/x/../../../escaped.txt (write) and DELETE .../files?path=/x/../../../safe.txt (delete), both over HTTP

Test plan

  • FileUtilsTest — 29 tests, 0 failures
  • NamespaceFileTest — 17 tests, 0 failures
  • NamespaceFileControllerTest#shouldRejectForwardSlashPathTraversalOnWriteAndDelete — passes

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

🐋 Docker image

ghcr.io/kestra-io/kestra-pr:16593
docker run --pull=always --rm -it -p 8080:8080 --user=root -v /var/run/docker.sock:/var/run/docker.sock -v /tmp:/tmp ghcr.io/kestra-io/kestra-pr:16593 server local

🧪 Java Unit Tests

TestsPassed ✅Skipped ⚠️FailedTime ⏱
Java Tests Report6824 ran6804 ✅20 ⚠️0 ❌49m 37s 82ms

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Tests report quick summary:

success ✅ > tests: 6824, success: 6804, skipped: 20, failed: 0 (🔄 6824 executed, 📦 0 from cache)

unfold for details
Project Status Success Skipped Failed
cli success ✅ 🔄 54 1 0
core success ✅ 🔄 2655 6 0
executor success ✅ 🔄 51 3 0
jdbc success ✅ 🔄 30 6 0
jdbc-h2 success ✅ 🔄 900 1 0
jdbc-mysql success ✅ 🔄 891 2 0
jdbc-postgres success ✅ 🔄 905 0 0
processor success ✅ 🔄 7 0 0
queue success ✅ 🔄 39 0 0
runner-memory success ✅ 🔄 1 0 0
scheduler success ✅ 🔄 87 0 0
script success ✅ 🔄 37 0 0
storage-local success ✅ 🔄 66 0 0
tests success ✅ 🔄 2 0 0
webserver success ✅ 🔄 888 1 0
worker success ✅ 🔄 58 0 0
worker-controller success ✅ 🔄 133 0 0

Develocity build scan: https://develocity.kestra.io/s/6gnmcw6wc6ygy


Flaky tests report quick summary:

failed ❌ > tests: 14, success: 13, skipped: 0, failed: 1

Project Status Success Skipped Failed
cli success ✅ 2 0 0
jdbc-h2 success ✅ 1 0 0
jdbc-mysql success ✅ 2 0 0
jdbc-postgres success ✅ 1 0 0
script success ✅ 2 0 0
webserver failed ❌ 5 0 1

Failed tests:

webserver > io.kestra.webserver.controllers.api.MiscControllerTest > canTriggerAWebhookWithoutBasicAuth() failed ❌ in 30.500
java.lang.AssertionError: [can trigger this Flow webhook when not authenticated] 
Expecting code not to raise a throwable but caught
  "io.micronaut.http.client.exceptions.HttpClientResponseException: Client '/': Internal Server Error
	at io.micronaut.http.client.netty.DefaultHttpClient.makeErrorFromRequestBody(DefaultHttpClient.java:2142)
	at io.micronaut.http.client.netty.DefaultHttpClient.handleExchangeResponse(DefaultHttpClient.java:960)
	at io.micronaut.http.client.netty.DefaultHttpClient.lambda$exchange$9(DefaultHttpClient.java:913)
	at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
	at io.micronaut.http.client.netty.DefaultHttpClient.lambda$exchange$10(DefaultHttpClient.java:913)
	at io.micronaut.http.client.netty.DefaultHttpClient.lambda$sendRequestWithRedirectsNoFilter$44(DefaultHttpClient.java:1666)
	at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl$FlatMap.apply(DelayedExecutionFlowImpl.java:359)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl.work(DelayedExecutionFlowImpl.java:58)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl.completeLazy(DelayedExecutionFlowImpl.java:80)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl.complete(DelayedExecutionFlowImpl.java:99)
	at io.micronaut.http.client.netty.DefaultHttpClient$6.complete(DefaultHttpClient.java:1763)
	at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.complete(Http1ResponseHandler.java:226)
	at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.read(Http1ResponseHandler.java:187)
	at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.read(Http1ResponseHandler.java:161)
	at io.micronaut.http.client.netty.Http1ResponseHandler.channelReadInstrumented(Http1ResponseHandler.java:75)
	at io.micronaut.http.client.netty.Http1ResponseHandler.channelReadInstrumented(Http1ResponseHandler.java:50)
	at io.micronaut.http.client.netty.SimpleChannelInboundHandlerInstrumented.channelRead0(SimpleChannelInboundHandlerInstrumented.java:46)
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:170)
	at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:48)
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:91)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:434)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:361)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:325)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:249)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:288)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1429)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:176)
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.handle(AbstractNioChannel.java:445)
	at io.netty.channel.nio.NioIoHandler$DefaultNioRegistration.handle(NioIoHandler.java:388)
	at io.netty.channel.nio.NioIoHandler.processSelectedKey(NioIoHandler.java:596)
	at io.netty.channel.nio.NioIoHandler.processSelectedKeysPlain(NioIoHandler.java:541)
	at io.netty.channel.nio.NioIoHandler.processSelectedKeys(NioIoHandler.java:514)
	at io.netty.channel.nio.NioIoHandler.run(NioIoHandler.java:484)
	at io.netty.channel.SingleThreadIoEventLoop.runIo(SingleThreadIoEventLoop.java:225)
	at io.netty.channel.SingleThreadIoEventLoop.run(SingleThreadIoEventLoop.java:196)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:1195)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:1474)
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:104)
		at reactor.core.publisher.Mono.block(Mono.java:1779)
		at io.micronaut.http.client.netty.DefaultHttpClient$1.exchange(DefaultHttpClient.java:612)
		at io.micronaut.http.client.netty.DefaultHttpClient$1.retrieve(DefaultHttpClient.java:620)
		at io.micronaut.http.client.BlockingHttpClient.retrieve(BlockingHttpClient.java:123)
		at io.micronaut.http.client.BlockingHttpClient.retrieve(BlockingHttpClient.java:172)
		at io.kestra.webserver.controllers.api.MiscControllerTest.lambda$canTriggerAWebhookWithoutBasicAuth$1(MiscControllerTest.java:220)
		at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:66)
		at org.assertj.core.api.AssertionsForClassTypes.catchThrowable(AssertionsForClassTypes.java:908)
		at org.assertj.core.api.AssertionsForClassTypes.assertThatCode(AssertionsForClassTypes.java:879)
		at org.assertj.core.api.Assertions.assertThatCode(Assertions.java:1396)
		at io.kestra.webserver.controllers.api.MiscControllerTest.canTriggerAWebhookWithoutBasicAuth(MiscControllerTest.java:219)
		at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
		at java.base/java.lang.reflect.Method.invoke(Method.java:565)
		at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786)
		at org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:514)
		at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
		at io.micronaut.test.extensions.junit5.MicronautJunit5Extension$2.proceed(MicronautJunit5Extension.java:154)
		at io.micronaut.test.extensions.AbstractMicronautExtension.interceptEach(AbstractMicronautExtension.java:171)
		at io.micronaut.test.extensions.AbstractMicronautExtension.interceptTest(AbstractMicronautExtension.java:128)
		at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.interceptTestMethod(MicronautJunit5Extension.java:141)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
		at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:161)
		at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:152)
		at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:91)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:93)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:87)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$4(TestMethodTestDescriptor.java:221)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:217)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:159)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:157)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)
		at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)
		at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
		at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)
		at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)
		at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
		at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)
		at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)
		at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
		at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
		at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:230)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:204)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:172)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:101)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:64)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:150)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:63)
		at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:109)
		at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:91)
		at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
		at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$1(InterceptingLauncher.java:39)
		at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25)
		at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:38)
		at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
		at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor$CollectThenExecuteTestDefinitionConsumer.processAllTestDefinitions(JUnitPlatformTestDefinitionProcessor.java:179)
		at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor$CollectThenExecuteTestDefinitionConsumer.access$000(JUnitPlatformTestDefinitionProcessor.java:122)
		at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor.stop(JUnitPlatformTestDefinitionProcessor.java:116)
		at org.gradle.api.internal.tasks.testing.SuiteTestDefinitionProcessor.stop(SuiteTestDefinitionProcessor.java:63)
		at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
		at java.base/java.lang.reflect.Method.invoke(Method.java:565)
		at org.gradle.internal.dispatch.MethodInvocation.invokeOn(MethodInvocation.java:77)
		at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:28)
		at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:19)
		at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
		at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:88)
		at jdk.proxy2/jdk.proxy2.$Proxy6.stop(Unknown Source)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:195)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:126)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63)
		at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
		at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:122)
		at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:72)
		at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
		at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
"

java.lang.AssertionError: [can trigger this Flow webhook when not authenticated] 
Expecting code not to raise a throwable but caught
  "io.micronaut.http.client.exceptions.HttpClientResponseException: Client '/': Internal Server Error
	at io.micronaut.http.client.netty.DefaultHttpClient.makeErrorFromRequestBody(DefaultHttpClient.java:2142)
	at io.micronaut.http.client.netty.DefaultHttpClient.handleExchangeResponse(DefaultHttpClient.java:960)
	at io.micronaut.http.client.netty.DefaultHttpClient.lambda$exchange$9(DefaultHttpClient.java:913)
	at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
	at io.micronaut.http.client.netty.DefaultHttpClient.lambda$exchange$10(DefaultHttpClient.java:913)
	at io.micronaut.http.client.netty.DefaultHttpClient.lambda$sendRequestWithRedirectsNoFilter$44(DefaultHttpClient.java:1666)
	at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl$FlatMap.apply(DelayedExecutionFlowImpl.java:359)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl.work(DelayedExecutionFlowImpl.java:58)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl.completeLazy(DelayedExecutionFlowImpl.java:80)
	at io.micronaut.core.execution.DelayedExecutionFlowImpl.complete(DelayedExecutionFlowImpl.java:99)
	at io.micronaut.http.client.netty.DefaultHttpClient$6.complete(DefaultHttpClient.java:1763)
	at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.complete(Http1ResponseHandler.java:226)
	at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.read(Http1ResponseHandler.java:187)
	at io.micronaut.http.client.netty.Http1ResponseHandler$BufferedContent.read(Http1ResponseHandler.java:161)
	at io.micronaut.http.client.netty.Http1ResponseHandler.channelReadInstrumented(Http1ResponseHandler.java:75)
	at io.micronaut.http.client.netty.Http1ResponseHandler.channelReadInstrumented(Http1ResponseHandler.java:50)
	at io.micronaut.http.client.netty.SimpleChannelInboundHandlerInstrumented.channelRead0(SimpleChannelInboundHandlerInstrumented.java:46)
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:170)
	at io.netty.handler.codec.http.HttpContentDecoder.decode(HttpContentDecoder.java:48)
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:91)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:434)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:361)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:325)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:249)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:288)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1429)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:176)
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.handle(AbstractNioChannel.java:445)
	at io.netty.channel.nio.NioIoHandler$DefaultNioRegistration.handle(NioIoHandler.java:388)
	at io.netty.channel.nio.NioIoHandler.processSelectedKey(NioIoHandler.java:596)
	at io.netty.channel.nio.NioIoHandler.processSelectedKeysPlain(NioIoHandler.java:541)
	at io.netty.channel.nio.NioIoHandler.processSelectedKeys(NioIoHandler.java:514)
	at io.netty.channel.nio.NioIoHandler.run(NioIoHandler.java:484)
	at io.netty.channel.SingleThreadIoEventLoop.runIo(SingleThreadIoEventLoop.java:225)
	at io.netty.channel.SingleThreadIoEventLoop.run(SingleThreadIoEventLoop.java:196)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:1195)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:1474)
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:104)
		at reactor.core.publisher.Mono.block(Mono.java:1779)
		at io.micronaut.http.client.netty.DefaultHttpClient$1.exchange(DefaultHttpClient.java:612)
		at io.micronaut.http.client.netty.DefaultHttpClient$1.retrieve(DefaultHttpClient.java:620)
		at io.micronaut.http.client.BlockingHttpClient.retrieve(BlockingHttpClient.java:123)
		at io.micronaut.http.client.BlockingHttpClient.retrieve(BlockingHttpClient.java:172)
		at io.kestra.webserver.controllers.api.MiscControllerTest.lambda$canTriggerAWebhookWithoutBasicAuth$1(MiscControllerTest.java:220)
		at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:66)
		at org.assertj.core.api.AssertionsForClassTypes.catchThrowable(AssertionsForClassTypes.java:908)
		at org.assertj.core.api.AssertionsForClassTypes.assertThatCode(AssertionsForClassTypes.java:879)
		at org.assertj.core.api.Assertions.assertThatCode(Assertions.java:1396)
		at io.kestra.webserver.controllers.api.MiscControllerTest.canTriggerAWebhookWithoutBasicAuth(MiscControllerTest.java:219)
		at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
		at java.base/java.lang.reflect.Method.invoke(Method.java:565)
		at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786)
		at org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:514)
		at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
		at io.micronaut.test.extensions.junit5.MicronautJunit5Extension$2.proceed(MicronautJunit5Extension.java:154)
		at io.micronaut.test.extensions.AbstractMicronautExtension.interceptEach(AbstractMicronautExtension.java:171)
		at io.micronaut.test.extensions.AbstractMicronautExtension.interceptTest(AbstractMicronautExtension.java:128)
		at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.interceptTestMethod(MicronautJunit5Extension.java:141)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
		at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:161)
		at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:152)
		at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:91)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:112)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:94)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
		at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:93)
		at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:87)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$4(TestMethodTestDescriptor.java:221)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:217)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:159)
		at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:157)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)
		at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)
		at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
		at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)
		at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)
		at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
		at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:161)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:147)
		at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:145)
		at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:144)
		at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:101)
		at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
		at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
		at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:230)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:204)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:172)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:101)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:64)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:150)
		at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:63)
		at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:109)
		at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:91)
		at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
		at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$1(InterceptingLauncher.java:39)
		at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25)
		at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:38)
		at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
		at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor$CollectThenExecuteTestDefinitionConsumer.processAllTestDefinitions(JUnitPlatformTestDefinitionProcessor.java:179)
		at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor$CollectThenExecuteTestDefinitionConsumer.access$000(JUnitPlatformTestDefinitionProcessor.java:122)
		at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestDefinitionProcessor.stop(JUnitPlatformTestDefinitionProcessor.java:116)
		at org.gradle.api.internal.tasks.testing.SuiteTestDefinitionProcessor.stop(SuiteTestDefinitionProcessor.java:63)
		at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
		at java.base/java.lang.reflect.Method.invoke(Method.java:565)
		at org.gradle.internal.dispatch.MethodInvocation.invokeOn(MethodInvocation.java:77)
		at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:28)
		at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:19)
		at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
		at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:88)
		at jdk.proxy2/jdk.proxy2.$Proxy6.stop(Unknown Source)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:195)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:126)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103)
		at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63)
		at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
		at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:122)
		at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:72)
		at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
		at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
"
	at io.kestra.webserver.controllers.api.MiscControllerTest.canTriggerAWebhookWithoutBasicAuth(MiscControllerTest.java:230)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension$2.proceed(MicronautJunit5Extension.java:154)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptEach(AbstractMicronautExtension.java:171)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptTest(AbstractMicronautExtension.java:128)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.interceptTestMethod(MicronautJunit5Extension.java:141)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)

…ward-slash path traversal

Adds dedicated test cases for the Windows-specific path traversal reported in
GHSA-h7c7-3mfc-m7pj, where the old guard used File.separator ('\\' on Windows)
and never matched forward-slash HTTP URI payloads like /x/../../../foo.txt.

The fix was shipped as part of GHSA-qw4v-6w32-xx9h (commit b41d0ce) but had
no test cases explicitly covering the forward-slash / Windows-JVM attack surface.

- FileUtilsTest: add forward-slash traversal cases to both URI and String
  parameterized tests, labeled GHSA-h7c7-3mfc-m7pj
- NamespaceFileTest: add shouldThrowOnForwardSlashPathTraversal() asserting that
  NamespaceFile.normalize() rejects /x/../../../ payloads (the toLogicalPath() +
  FileUtils.isParentTraversal() chain must work regardless of host OS)
- NamespaceFileControllerTest: add shouldRejectForwardSlashPathTraversalOnWriteAndDelete()
  exercising the POST (write) and DELETE primitives end-to-end via the HTTP layer
@AcevedoR AcevedoR force-pushed the fix/GHSA-h7c7-3mfc-m7pj-path-traversal-tests branch from 014c661 to 77cacee Compare June 5, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To review

Development

Successfully merging this pull request may close these issues.

1 participant