lib.Uri: Fix local path edge cases #84

Merged
Jan Lindemann merged 2 commits from jan/fix/20260905-lib-uri-fix-path-join-for-empty-authority into master 2026-09-05 14:10:53 +02:00 AGit

This PR fixes edge cases with local paths lib.Uri is not prepared for.

lib.Uri: Fix __assemble(): No '://' for relative paths

Assembling a scheme-bearing form always writes '://' after the scheme, even if the input has no authority part. For a schemeless relative path such as '../../local/path', .full then becomes 'file://../../local/path', which re-parses with '..' as the host and '/../local/path' as the path, both entirely broken.

Fix __assemble() to write a bare ':' instead of '://' when the input has no host and a relative path, so the assembled form re-parses back to the same relative path: full of '../../local/path' is now 'file:../../local/path'. Absolute paths and forms with an authority are unchanged.

The assembled 'file:../../local/path' form is a valid URI under the RFC 3986 generic grammar (scheme with a rootless path), while RFC 8089's file URI ABNF admits only an empty or an absolute path. Uri thus trades RFC 8089 conformance for RFC 3986 compatibility: a relative path survives the full-and-reparse cycle unchanged.

lib.Uri: Fix path join for empty authority

__new_with_path() joins base and path with exactly one '/', assuming a trailing '/' in base is a path separator. That assumption breaks for URIs with empty authority, where scheme_plus_authority ends in '://' (e.g. 'file:///tmp/x'): new_replace_path('/etc/hosts') drops the leading slash of the path and produces 'file://etc/hosts', which parses with hostname 'etc' and path '/hosts' instead of path '/etc/hosts'.

Treat a base ending in '://' separately and keep a leading slash in the path, adding one if it is missing.

This PR fixes edge cases with local paths lib.Uri is not prepared for. #### lib.Uri: Fix __assemble(): No '://' for relative paths Assembling a scheme-bearing form always writes '://' after the scheme, even if the input has no authority part. For a schemeless relative path such as '../../local/path', .full then becomes 'file://../../local/path', which re-parses with '..' as the host and '/../local/path' as the path, both entirely broken. Fix __assemble() to write a bare ':' instead of '://' when the input has no host and a relative path, so the assembled form re-parses back to the same relative path: full of '../../local/path' is now 'file:../../local/path'. Absolute paths and forms with an authority are unchanged. The assembled 'file:../../local/path' form is a valid URI under the RFC 3986 generic grammar (scheme with a rootless path), while RFC 8089's file URI ABNF admits only an empty or an absolute path. Uri thus trades RFC 8089 conformance for RFC 3986 compatibility: a relative path survives the full-and-reparse cycle unchanged. #### lib.Uri: Fix path join for empty authority __new_with_path() joins base and path with exactly one '/', assuming a trailing '/' in base is a path separator. That assumption breaks for URIs with empty authority, where scheme_plus_authority ends in '://' (e.g. 'file:///tmp/x'): new_replace_path('/etc/hosts') drops the leading slash of the path and produces 'file://etc/hosts', which parses with hostname 'etc' and path '/hosts' instead of path '/etc/hosts'. Treat a base ending in '://' separately and keep a leading slash in the path, adding one if it is missing.
Assembling a scheme-bearing form always writes '://' after the scheme, even
if the input has no authority part. For a schemeless relative path such as
'../../local/path', .full then becomes 'file://../../local/path', which
re-parses with '..' as the host and '/../local/path' as the path, both
entirely broken.

Fix __assemble() to write a bare ':' instead of '://' when the input has no
host and a relative path, so the assembled form re-parses back to the same
relative path: full of '../../local/path' is now 'file:../../local/path'.
Absolute paths and forms with an authority are unchanged.

The assembled 'file:../../local/path' form is a valid URI under the RFC
3986 generic grammar (scheme with a rootless path), while RFC 8089's file
URI ABNF admits only an empty or an absolute path. Uri thus trades RFC 8089
conformance for RFC 3986 compatibility: a relative path survives the
full-and-reparse cycle unchanged.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
lib.Uri: Fix path join for empty authority
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m36s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m29s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 4m8s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m15s
CI / Packaging test (push) Successful in 0s
e8ade91f21
__new_with_path() joins base and path with exactly one '/', assuming a
trailing '/' in base is a path separator. That assumption breaks for
URIs with empty authority, where scheme_plus_authority ends in '://'
(e.g. 'file:///tmp/x'): new_replace_path('/etc/hosts') drops the
leading slash of the path and produces 'file://etc/hosts', which
parses with hostname 'etc' and path '/hosts' instead of path
'/etc/hosts'.

Treat a base ending in '://' separately and keep a leading slash in
the path, adding one if it is missing.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
Jan Lindemann scheduled this pull request to auto merge when all checks succeed 2026-09-05 14:01:48 +02:00
Jan Lindemann changed title from lib.Uri: Fix path join for empty authority to lib.Uri: Fix local path edge cases 2026-09-05 14:25:48 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
janware/jw-pkg!84
No description provided.