π‘οΈ Sentinel: [CRITICAL] Fix Path Traversal in TypeScript Module Path Resolution#228
π‘οΈ Sentinel: [CRITICAL] Fix Path Traversal in TypeScript Module Path Resolution#228bashandbone wants to merge 1 commit into
Conversation
β¦Resolution Co-authored-by: bashandbone <89049923+bashandbone@users.noreply.github.com>
|
π Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a π emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
Reviewer's GuideFixes a critical path traversal vulnerability in TypeScript module path resolution by making the manual path normalization logic root-aware and preserving relative parent directory components, and documents the incident and fix in a Sentinel note. File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="crates/flow/src/incremental/extractors/typescript.rs" line_range="807" />
<code_context>
} else {
// If canonicalize fails (file doesn't exist), manually resolve
- let mut components = Vec::new();
+ let mut components: Vec<std::path::Component> = Vec::new();
for component in resolved.components() {
match component {
</code_context>
<issue_to_address>
**issue (bug_risk):** Specify the lifetime parameter for `std::path::Component` to avoid a compilation error.
`std::path::Component` is declared as `pub enum Component<'a>`, so using `Vec<std::path::Component>` without a lifetime wonβt compile. Either specify the lifetime (e.g. `Vec<std::path::Component<'_>>`) or drop the explicit type and rely on inference:
```rust
let mut components: Vec<std::path::Component<'_>> = Vec::new();
// or
let mut components = Vec::new();
```
</issue_to_address>Help me be more useful! Please click π or π on each comment and I'll use the feedback to improve your reviews.
| } else { | ||
| // If canonicalize fails (file doesn't exist), manually resolve | ||
| let mut components = Vec::new(); | ||
| let mut components: Vec<std::path::Component> = Vec::new(); |
There was a problem hiding this comment.
issue (bug_risk): Specify the lifetime parameter for std::path::Component to avoid a compilation error.
std::path::Component is declared as pub enum Component<'a>, so using Vec<std::path::Component> without a lifetime wonβt compile. Either specify the lifetime (e.g. Vec<std::path::Component<'_>>) or drop the explicit type and rely on inference:
let mut components: Vec<std::path::Component<'_>> = Vec::new();
// or
let mut components = Vec::new();There was a problem hiding this comment.
Pull request overview
This PR hardens the manual path-normalization fallback in TypeScriptDependencyExtractor::resolve_module_path (used when canonicalize fails). The previous implementation unconditionally popped the components stack on ParentDir, which could erase a RootDir/Prefix anchor (turning an absolute path into a relative one) or silently drop excess .. segments. The new implementation inspects the last component and either ignores the ParentDir (when at root/prefix), pushes it (when empty or already on a ParentDir), or pops only a normal segment.
Changes:
- Replace blind
components.pop()onParentDirwith an explicit last-component check that preservesRootDir/Prefixanchors and accumulates leading..components. - Add a Sentinel security note documenting the vulnerability and prevention guidance.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| crates/flow/src/incremental/extractors/typescript.rs | Safer path component normalization for the canonicalize fallback. |
| .jules/sentinel.md | New Sentinel log entry describing the vulnerability and fix. |
π‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
π¨ Severity: CRITICAL
π‘ Vulnerability: The manual path normalization fallback when canonicalizing failing paths in
TypeScriptDependencyExtractor::resolve_module_pathwas simply blindly popping from the components vector onstd::path::Component::ParentDir. This could allow traversal of arbitrary root or prefix paths (Component::RootDir/Component::Prefix) resulting in unanchoring absolute paths to relative ones, or dropping relative anchors by failing to addParentDirto the front of the list when empty.π― Impact: Attackers or poorly formatted path extraction dependencies could trigger arbitrary file reads, module inclusions, or extraction outside the bounds of the base system directories using crafted paths like
../../../../etc/passwdescaping normal limits.π§ Fix: Updated the component normalization loop to accurately check the last element before popping. If the element is a
RootDirorPrefix, it explicitly ignores popping. If the vector is empty or already ends with aParentDir, it correctly pushes the newParentDirto preserve relative structure (../../).β Verification: Ran extractor test
cargo test -p thread-flow --test extractor_typescript_testslocally verifying all 25 parser behavior tests passed.PR created automatically by Jules for task 10558821495673776827 started by @bashandbone
Summary by Sourcery
Harden TypeScript module path resolution against path traversal when canonicalization fails and document the vulnerability and fix in Sentinel notes.
Bug Fixes:
Documentation: