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 @@
## 2024-05-24 - Path Traversal in Manual Path Resolution
**Vulnerability:** In `crates/flow/src/incremental/extractors/typescript.rs`, manual path component normalization allowed dropping `ParentDir` components indiscriminately, which could allow path traversals escaping the prefix/root if a path like `/../../` was evaluated.
**Learning:** Manual path normalization logic with `std::path::Component` needs to carefully consider the boundaries of `Component::ParentDir`. Specifically, a `ParentDir` cannot pop a `RootDir` or `Prefix`. If we're at the root or prefix, we must stop popping or we'll drop the root and accidentally allow traversal or malformed paths. Also, if there are multiple consecutive `ParentDir`s (e.g. `../../`), we must preserve them rather than ignoring them or just not popping.
**Prevention:** When dealing with `std::path::Component` to normalize paths with `..`, always verify what component is being popped. Do not pop if the previous component is a `RootDir`, `Prefix`, or another `ParentDir`. If it's another `ParentDir`, we should push the new `ParentDir` onto the stack to correctly represent relative paths like `../../a`.
10 changes: 9 additions & 1 deletion crates/flow/src/incremental/extractors/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,15 @@ impl TypeScriptDependencyExtractor {
for component in resolved.components() {
match component {
std::path::Component::ParentDir => {
components.pop();
// Prevent path traversal escaping root, and handle consecutive ParentDirs
let last = components.last().copied();
match last {
Some(std::path::Component::Normal(_)) => {
components.pop();
}
Some(std::path::Component::RootDir) | Some(std::path::Component::Prefix(_)) => {}
_ => components.push(component),
}
Comment on lines +811 to +819
}
std::path::Component::CurDir => {}
_ => components.push(component),
Expand Down