Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2025-02-27 - Path Traversal Vulnerability in Manual Path Normalization
**Vulnerability:** The `typescript` extractor manual path normalization (used when `canonicalize()` fails) suffered from a path traversal flaw. When reducing `../` (ParentDir) segments by popping from a vector of path components, the `pop()` method returned silently if the vector was empty. Consequently, paths starting with `../` lost those parent directory components, incorrectly anchoring relative references locally or resulting in unintended, out-of-bounds filesystem access attempts outside the original root directory.
**Learning:** Manual path component normalization utilizing vector reductions must account for starting segments (`../`) and constraints (Root or Prefix directives). Merely popping elements fails for valid, relative paths that start with multiple `../` components, collapsing paths into unintended domains.
**Prevention:** Always push `ParentDir` if the stack is empty or currently terminating in a `ParentDir`, and drop it if constrained by root scopes. Never blindly pop elements when reducing relative path directives.
13 changes: 10 additions & 3 deletions crates/flow/src/incremental/extractors/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,9 +807,16 @@ impl TypeScriptDependencyExtractor {
let mut components = Vec::new();
for component in resolved.components() {
match component {
std::path::Component::ParentDir => {
components.pop();
}
std::path::Component::ParentDir => match components.last() {
Some(std::path::Component::ParentDir) | None => {
Comment on lines 807 to +811
components.push(component)
}
Some(std::path::Component::RootDir)
| Some(std::path::Component::Prefix(_)) => {}
_ => {
components.pop();
}
},
Comment on lines +810 to +819
std::path::Component::CurDir => {}
_ => components.push(component),
}
Expand Down
Loading