From 8f66193a5a3a2c6d454c9322f2e02de580cea771 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 17:49:33 +0000 Subject: [PATCH] Fix path traversal vulnerability in TypeScript dependency extractor During lexical path normalization, `../` components were handled by blindly calling `components.pop()`. This allowed paths to escape intended root scopes by popping root indicators, or incorrectly simplified paths like `../../foo` into `foo` if the components list was empty. The manual path resolution has been updated to explicitly preserve `..` ascensions and prevent traversal beyond roots. Co-authored-by: bashandbone <89049923+bashandbone@users.noreply.github.com> --- .jules/sentinel.md | 5 +++++ crates/flow/src/incremental/extractors/typescript.rs | 12 +++++++++++- 2 files changed, 16 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..92c50d3e --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,5 @@ + +## 2024-05-20 - Lexical Path Normalization Traversal Vulnerability +**Vulnerability:** In `crates/flow/src/incremental/extractors/typescript.rs`, manual path normalization handled `..` components by blindly calling `components.pop()`. +**Learning:** If `components` was empty or contained boundary components like `/` (`RootDir`), popping did nothing, effectively deleting `..` directories and breaking relative navigation. Alternatively, `components.pop()` would happily strip root indicators, allowing path ascensions to escape intended root scopes. +**Prevention:** When manually normalizing paths using `std::path::Component`, explicitly block `ParentDir` from popping `RootDir` or `Prefix`. If the components stack is empty or its top is already `ParentDir`, push the new `ParentDir` to preserve valid ascensions (e.g., `../../file`). diff --git a/crates/flow/src/incremental/extractors/typescript.rs b/crates/flow/src/incremental/extractors/typescript.rs index 1bdda4ef..2a44eaef 100644 --- a/crates/flow/src/incremental/extractors/typescript.rs +++ b/crates/flow/src/incremental/extractors/typescript.rs @@ -808,7 +808,17 @@ impl TypeScriptDependencyExtractor { for component in resolved.components() { match component { std::path::Component::ParentDir => { - components.pop(); + let last = components.last().copied(); + match last { + None | Some(std::path::Component::ParentDir) => { + components.push(component); + } + Some(std::path::Component::RootDir) + | Some(std::path::Component::Prefix(_)) => {} + _ => { + components.pop(); + } + } } std::path::Component::CurDir => {} _ => components.push(component),