From d19a3adc0fe78814e7052829496372d04c244cbb Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 17:55:02 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITICAL]?= =?UTF-8?q?=20Fix=20path=20traversal=20in=20TypeScript=20module=20resoluti?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a critical path traversal vulnerability in `crates/flow/src/incremental/extractors/typescript.rs` where manual path normalization blindly popped directory components when resolving `..`. This allowed traversal paths like `/..` to strip the root directory (`/`), or escaping above the starting boundary when traversing upwards via `../../`. The fix correctly checks the last component in the stack, preventing the popping of `RootDir` and `Prefix`, and correctly pushing `ParentDir` when the stack is empty or already at `ParentDir`. Co-authored-by: bashandbone <89049923+bashandbone@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ .../src/incremental/extractors/typescript.rs | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 00000000..3ad22a0e --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2024-05-24 - [Path Traversal Vulnerability in Manual Path Resolution] +**Vulnerability:** Path traversal vulnerability during manual path normalization. +**Learning:** `Component::ParentDir` was handled by blindly popping the last path component when canonicalization failed. This could cause it to mistakenly pop root (`/`) or prefix components, allowing traversal out of safe boundaries or stripping absolute path roots incorrectly. It also failed to correctly preserve `..` elements when traversing up from `..`. +**Prevention:** Always explicitly match against previous components during manual path normalization. Prevent `Component::ParentDir` from popping `Component::RootDir` or `Component::Prefix`. If the components list is empty or ends in `Component::ParentDir`, the new `Component::ParentDir` must be pushed to properly handle paths that traverse beyond the starting directory. diff --git a/crates/flow/src/incremental/extractors/typescript.rs b/crates/flow/src/incremental/extractors/typescript.rs index 1bdda4ef..adcd017f 100644 --- a/crates/flow/src/incremental/extractors/typescript.rs +++ b/crates/flow/src/incremental/extractors/typescript.rs @@ -808,7 +808,21 @@ impl TypeScriptDependencyExtractor { for component in resolved.components() { match component { std::path::Component::ParentDir => { - components.pop(); + if let Some(last) = components.last() { + match last { + std::path::Component::RootDir | std::path::Component::Prefix(_) => { + // Path traversal block: do not pop root or prefix + } + std::path::Component::ParentDir => { + components.push(component); + } + _ => { + components.pop(); + } + } + } else { + components.push(component); + } } std::path::Component::CurDir => {} _ => components.push(component),