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-28 - [Path Traversal bypass in PathBuf resolution]
**Vulnerability:** Found a vulnerable path component normalization in `crates/flow/src/incremental/extractors/typescript.rs`. Manual handling of `..` (`ParentDir`) blindly called `components.pop()`. This fails to handle root directories or prefixes correctly (converting absolute paths to relative, allowing traversal escapes), and incorrectly ignores `..` at the beginning of relative paths (e.g., resolving `../../a` to `a`).
**Learning:** `std::path::PathBuf::components()` correctly yields `Component::RootDir` and `Component::ParentDir`. Normalization algorithms MUST explicitly check the previous component before popping. Popping `RootDir` is inherently unsafe as it changes path type. Empty states or states ending in `ParentDir` must push `ParentDir` instead of doing nothing or blindly popping.
**Prevention:** Whenever manual path resolution loop involves popping on `Component::ParentDir`, check the top of the component stack first to prevent popping prefixes/roots or ignoring `ParentDir` when the stack is empty/only contains `ParentDir`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Consider adding an article in "Whenever manual path resolution loop" for grammatical correctness.

Consider rephrasing the beginning to: "Whenever a manual path resolution loop involves popping on Component::ParentDir, ..." to improve grammar and readability.

16 changes: 15 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,21 @@ impl TypeScriptDependencyExtractor {
for component in resolved.components() {
match component {
std::path::Component::ParentDir => {
components.pop();
let pop = match components.last() {
Some(std::path::Component::RootDir)
| Some(std::path::Component::Prefix(_)) => false,
None | Some(std::path::Component::ParentDir) => false,
Comment on lines +811 to +814
_ => true,
Comment on lines 810 to +815
};
if pop {
components.pop();
} else if !matches!(
components.last(),
Some(std::path::Component::RootDir)
| Some(std::path::Component::Prefix(_))
) {
components.push(component);
}
}
std::path::Component::CurDir => {}
_ => components.push(component),
Expand Down
Loading