Skip to content

Escape '.' and '..' path segments when normalizing URLs into ZIM paths - #361

Merged
benoit74 merged 1 commit into
mainfrom
fix-dot-segments-escaping-321
Oct 22, 2026
Merged

benoit74 merged 1 commit into
mainfrom
fix-dot-segments-escaping-321

Conversation

@benoit74

Copy link
Copy Markdown
Collaborator

What

A ZIM path must never contain a literal . or .. path segment: per RFC 3986 these dot-segments are resolved away by any URI consumer (including a reader turning a relative link back into an absolute one). When such a segment is folded into a ZIM path, e.g. from a querystring containing a /../ (xtree.html?css=../../prg), it is otherwise misinterpreted as navigation instead of being kept as a literal entry name, pointing the link to the wrong resource.

This is the bug reported in #321 (itself extracted from openzim/warc2zim#380). It was first tackled in #314, which fixed the (now already-gone) crash but, per review there, got the semantics wrong: it ignored ../. found in a querystring rather than treating them as the literal path segments they become once folded into the ZIM path. Resolving the correct approach required first settling openzim/overview#90 (should dot-segments be fixed in readers, or forbidden at the ZIM spec level), which landed on forbidding them, and the concrete escaping scheme agreed in #321.

Fix

Any path segment that is exactly . or .. is now prefixed with a _ when a URL is normalized into a ZIM path, e.g. a/../b becomes a/_../b. This is applied uniformly to the whole ZIM path (host + path + querystring), so it also covers dot-segments folded in from a querystring. Look-alike segments such as foo.., ..foo or ... are not dot-segments and are left untouched.

Implemented identically in both the Python (ArticleUrlRewriter.normalize) and JS (urlRewriteFunction) rewriters, per the earlier review on #314 asking for parity between the two.

Tests

  • ArticleUrlRewriter.escape_dot_segments / JS escapeDotSegments unit tests covering dot-segments, consecutive dot-segments, trailing dot-segments, and look-alikes.
  • TestNormalize / JS urlRewriteFunction tests covering the querystring-dot-segment crash case from Rewriter choke on a relative URL inside a querystring #321.
  • A test_relative_url_round_trip case asserting that resolving the rewritten link (as a reader would, including RFC 3986 dot-segment removal) gives back the correct, escaped ZIM path.

Full tests/rewriting/ suite passes (570 tests); JS yarn test passes (153 tests); ruff/pyright/eslint/prettier all clean.

Supersedes #314 (same underlying bug, different and now-agreed-upon fix).

Fixes #321

A `.` or `..` path segment is an RFC 3986 dot-segment, resolved away by any
URI consumer (including a reader turning a relative link back into an
absolute one). When such a segment is folded into a ZIM path, e.g. from a
querystring containing a `/../`, it is otherwise misinterpreted as
navigation instead of being kept as a literal entry name.

Prefix any path segment that is exactly `.` or `..` with a `_`, in both
Python and JS URL rewriting, so it stays inert for URI resolution while
remaining distinguishable. Look-alikes (`foo..`, `..foo`, `...`) are left
untouched since they are not dot-segments.

Supersedes #314, fixes #321
@benoit74
benoit74 force-pushed the fix-dot-segments-escaping-321 branch from 050fcf3 to b6be9a9 Compare October 22, 2026 14:11
@benoit74 benoit74 self-assigned this Oct 22, 2026
@codecov

codecov Bot commented Oct 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (b5ad688) to head (b6be9a9).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #361   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           42        42           
  Lines         2852      2855    +3     
  Branches       428       428           
=========================================
+ Hits          2852      2855    +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@benoit74
benoit74 merged commit 9a25ed3 into main Oct 22, 2026
10 checks passed
@benoit74
benoit74 deleted the fix-dot-segments-escaping-321 branch October 22, 2026 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rewriter choke on a relative URL inside a querystring

1 participant