-
Notifications
You must be signed in to change notification settings - Fork 12
docs(expr): warn that is_relative_to is a lexical prefix test, not containment #367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -150,9 +150,18 @@ Path-related operations in the expression language: | |
| | `with_number(n)` | `(path, int) -> path` / `(string, int) -> path` | Append frame number | | ||
| | `as_posix()` | `(path) -> string` | Convert to POSIX string | | ||
| | `is_absolute()` | `(path) -> bool` | Check if path is absolute | | ||
| | `is_relative_to(other)` | `(path, path) -> bool` | Check prefix relationship | | ||
| | `is_relative_to(other)` | `(path, path) -> bool` | Lexical prefix test (see note below) | | ||
| | `relative_to(other)` | `(path, path) -> path` | Compute relative path | | ||
|
|
||
| > **`is_relative_to` is a lexical prefix test, not a containment | ||
| > check.** `..` segments are ordinary components (see | ||
| > [`path-parse.md`](path-parse.md#normalization)), so | ||
| > `path("/allowed/../etc/passwd").is_relative_to(path("/allowed"))` | ||
| > returns `true`. This matches `PurePosixPath.is_relative_to()`. | ||
| > To reject traversal, combine with a `..` check: | ||
| > `p.is_relative_to(base) and not ("..") in p.parts` | ||
| > (note: `.parts` is a property, not a function). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Since this note exists to steer people away from an unsafe assumption, it would help to scope it explicitly (e.g. "for filesystem paths; for URI values |
||
|
|
||
| > **URI paths in path methods.** The path properties and the | ||
| > `with_*` methods all detect URI inputs (`uri_path::is_uri`) and | ||
| > route to `uri_path::*` so the result preserves URI grammar — | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The recommended guard reads as though the parentheses change the grouping, but they do not:
not ("..") in p.partsparses asnot (".." in p.parts)— the parenthesized("..")is just the left operand ofin. It happens to be the intended semantics, yet a reader scanning a security-hardening snippet is likely to misread it as(not "..") in p.parts.Since the expression language supports
not in(__not_contains__, seefunction-library.mdoperator table), the unambiguous form is:Worth using the idiomatic spelling here specifically because this snippet is the one people will copy into a path-traversal check.