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),