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
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -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()`.
Comment on lines +1 to +3
**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`).
12 changes: 11 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,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(_)) => {}
Comment on lines +816 to +817
_ => {
components.pop();
}
}
}
std::path::Component::CurDir => {}
_ => components.push(component),
Expand Down