All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- NUL-byte detection now always path-aware: Moved
reject_nul_bytesto Stage 0 ofsoft_canonicalizeand added a final pass on the output. Guarantees thatIoErrorPathExt::soft_canon_detail()consistently returns the path-aware"path contains null byte"detail instead of stdlib's genericInvalidInput, even when a NUL enters via symlink-target resolution. - Fixed
anchored_canonicalizeescape via absolute..symlink targets: Absolute symlink targets that began with..could escape the anchor. Extractednormalize_and_clamp_to_anchorso every clamp site in the crate uses the same..-normalizing, common-prefix-clamping logic. - Forward-slash UNC rejection:
is_incomplete_uncnow detects forward-slash forms (//server) and mixed-separator variants, while still correctly excluding verbatim (\\?\) and device (\\.\) namespaces for both separator kinds. - Upgraded
proc-canonicalize0.1.2 → 0.1.3: pulls in the upstream fix for a namespace-boundary bypass where paths like/proc/<PID>/../<PID>/rootlexically normalized to/proc/<PID>/rootbut evaded detection; the scanner now performs lexical normalization before boundary detection. Also includes an indirect-symlink scanner performance improvement (hoisted scratch buffers eliminate per-iteration heap allocations).
- Windows:
simple_normalize_pathnow preservesVerbatimDiskprefix: Building\\?\then pushing a drive letter silently dropped the verbatim marker, producing aPrefix::Diskoutput. SincePath::starts_withtreatsDiskandVerbatimDiskas distinct, anchored-symlink clamp checks against a verbatim floor would fail. Fixed by constructing the verbatim path as a singlePathBuf::from(format!(r"\\?\{drive}\")). Drive-relative inputs (C:foo) still skip the verbatim prefix to preserve per-drive CWD semantics. - Windows:
anchored_canonicalize..pop skipped onDiskvsVerbatimDiskprefix mismatch: Whensoft_canonicalizereturned aDisk-prefixed anchor floor (with theduncefeature) but symlink resolution brought the working path back throughfs::canonicalizeasVerbatimDisk, the stdlibPath::starts_withgate returned false, silently skipping the..pop and leaking the pre-..component into the final result. Now uses a component-wiseis_strictly_belowpredicate built on the crate'scomponent_eqhelper, which equates the two prefix forms.
component_eqhelper (Windows-aware): treatsVerbatimDiskvsDiskandVerbatimUNCvsUNCas equal, case-insensitive for drive letters and UNC server/share.normalize_and_clamp_to_anchorhelper: single source of truth for the..-normalizing, common-prefix-clamping logic used across the anchored code paths.- Regression tests:
tests/anchored_absolute_symlink_dotdot_escape.rs— covers the anchored..escape fix for absolute symlink targets.tests/regression_nul_byte_error_detail.rs— pins the path-awaresoft_canon_detail()output.tests/regression_incomplete_unc_forward_slash.rs— pins forward-slash UNC rejection.anchored_security::windows_symlink::anchored_symlink_or_junction_keeps_clamp_windows(sibling toanchored_relative_symlink_keeps_clamp_windows) — uses a directory symlink with NTFS-junction fallback on error 1314 so non-admin / non-Developer-Mode Windows sessions still exercise the anchor..pop via a real reparse point.normalize::verbatim_prefix_regressionandsymlink::clamp_verbatim_regression— pinVerbatimDiskpreservation through normalization and clamping without requiring symlink-create privileges.
- AGENTS.md "Junction Fallback" rules: codifies the pattern so tests exercise the code path locally via NTFS junctions (
create_symlink_or_junctionhelper for integration tests, inline junction fallback for unit tests) instead of silently skipping on error 1314.
- AGENTS.md rewrite: reorganized around maintenance principles (general-not-reactive, context-free rules, principles-over-examples), with expanded guidance on feature testing policy, symlink testing traps, git usage, coding guidelines, and test assertion style.
- Test reorganization: split
unicodeandwindows_8_3unit tests into thematic files for better readability and RAG retrieval. - CI — cross-platform Clippy:
ci-local.shandci-local.ps1now run clippy against the complement target (x86_64-unknown-linux-gnuon Windows,x86_64-pc-windows-gnuon Linux), so cfg-gated files for the opposite platform are linted locally before hitting CI.
- Fixed
..traversal bypass when normalized path differs from original (#53): The normalized-path fast-path (fs::canonicalizeon the normalized form) was incorrectly succeeding for paths containing..components that pointed to existing locations after normalization. This causedsoft_canonicalizeto return a fully-canonicalized result that silently resolved..through symlinks instead of applying lexical..resolution per the crate's symlink-first semantics. The fast-path is now skipped when..is present in the normalized path.
- Refactored source layout: Extracted
anchored_canonicalizeinto dedicatedsrc/anchored.rsmodule (compiled only with--features anchored). Split large test files by platform and concern for better maintainability and RAG retrieval. - Improved documentation: Updated README.md and lib.rs with feature comparison tables, exotic filesystem support details, and refreshed test counts.
- CI: Removed redundant
cargo auditJSON-format step from audit workflow.
- Regression test for issue #53 (
tests/issue_53_symlink_dotdot_lexical_collapse.rs): Verifies that..traversal respects symlink-first lexical semantics and does not silently resolve through symlinks. - Comprehensive security test coverage: New test files for cross-platform ADS security, anchored edge cases, component length limits, invalid UTF-8 handling, junction discrimination, permission TOCTOU, rename TOCTOU, special files, UTF-16 surrogates, and Windows 8.3 components.
- Exotic filesystem fallback tests (
tests/exotic_filesystem_fallback.rs): Coverage for graceful behavior on exotic filesystem failures. - Cross-platform path tests: macOS NFD normalization, private symlinks, resource forks/firmlinks, and volumes anchored edge cases.
- Expanded feature combination tests: Split into core and platform-specific test files.
- Compatibility test suites: Dedicated test files for
..traversal, existing paths, and symlink compatibility withstd::fs::canonicalize.
- Switched to
junction-verbatim1.3.0 for stability and MSRV compatibility: Replaced DK26 junction fork (GitHub dependency) with the dedicatedjunction-verbatimcrate published on crates.io. This provides a frozen, stable version while maintaining Rust 1.70 support. The upstreamjunctionv1.4.1 requires Rust 1.71+, which would break our MSRV guarantee.
- Fixed CI script stderr handling in
ci-local.ps1Run-Check()andRun-Fix()functions to prevent false failure detection.
- Improved test documentation for Windows junction handling.
- Simplified test helper by removing unnecessary
canonicalize()call.
- Updated dev-dependency to DK26/junction fork to fix tesuji/junction#30 (verbatim path corruption).
- Upgraded
proc-canonicalizeto 0.0.4: Addresses critical security hardening for Linux namespace boundaries.- Fixes relative symlink namespace bypass (e.g.,
link -> ../proc/self/root). - Fixes normalization bypass via
..before symlink detection.
- Fixes relative symlink namespace bypass (e.g.,
- Fixed manual traversal vulnerability:
soft_canonicalizenow correctly preserves/proc/PID/rootboundaries when resolving non-existing paths on Linux.- Previously, manual symlink resolution for non-existing paths could resolve
/proc/PID/rootto/, bypassing the protection provided byproc-canonicalize. - Added
is_proc_magic_linkcheck inresolve_simple_symlink_chainto stop resolution at magic boundaries. - Now handles both process-level (
/proc/PID/root) and task-level (/proc/PID/task/TID/root) namespace boundaries. - New: Added protection against
..escaping/proc/PID/rootduring manual traversal.
- Previously, manual symlink resolution for non-existing paths could resolve
-
New security tests for proc-canonicalize 0.0.4 attack vectors (
tests/linux_proc_indirect_symlink.rs):test_relative_symlink_resolving_to_proc_self_root: Tests relative symlink bypass.test_indirect_symlink_to_proc_pid_task_tid_root: Tests task-level namespace symlink protection.test_dotdot_escape_from_proc_root: Tests..escape attempts from magic boundaries.
-
Additional security edge case tests (
tests/proc_additional_security.rs):test_dotdot_after_entering_proc_root: Verifies..cannot escape after entering namespace.test_idempotency_for_proc_paths: Verifies canonicalization is idempotent.test_double_slash_in_proc_path: Tests double-slash edge cases.test_proc_root_in_middle_of_chain: Tests multi-hop chains ending in/proc.test_proc_self_cwd_preserved: Verifies/proc/self/cwdboundary preservation.test_triple_chain_with_nonexisting: Tests triple-depth chains with non-existing suffixes.
- Indirect symlinks to
/proc/PID/rootnow correctly preserve namespace boundaries- Previously, symlinks pointing to
/proc/PID/root(e.g.,/tmp/container -> /proc/self/root) would resolve to/, bypassing namespace protection - This was because the input path didn't lexically start with
/proc/, soproc-canonicalizedelegated tostd::fs::canonicalize - Upgraded
proc-canonicalizedependency from 0.0.2 to 0.0.3 which fixes this security bypass - Security: Critical fix for container boundary enforcement - prevents attackers from escaping container isolation via indirect symlinks
- Previously, symlinks pointing to
- Regression test suite for indirect
/proc/PID/rootsymlink bypass (tests/linux_proc_indirect_symlink.rs)- 10 tests covering: direct symlinks, chained symlinks, suffix paths, attack scenarios, anchored API
- Tests validate that namespace boundaries are preserved for indirect symlinks
- Covers
/proc/self/root,/proc/PID/root, and/proc/thread-self/rootvariants
- Dependency: Upgraded
proc-canonicalizefrom 0.0.2 to 0.0.3
-
Linux
/proc/PID/rootmagic symlinks are now preserved (#44)- Previously,
soft_canonicalize("/proc/PID/root")would resolve to/(followingstd::fs::canonicalizebehavior) - Now preserves the namespace boundary: returns
/proc/PID/rootas-is - Affects:
/proc/PID/root,/proc/PID/cwd,/proc/self/root,/proc/thread-self/root - Migration: Code that relied on
/procpaths resolving to/needs updating - Security: This is a security fix - the old behavior could allow namespace escapes in container tooling
- Previously,
-
proc-canonicalizeis now a default feature- Users who need the old
std::fs::canonicalizebehavior can disable withdefault-features = false - The
duncefeature works with or withoutproc-canonicalize
- Users who need the old
-
Windows drive-relative anchor paths now produce correct absolute verbatim paths (#43)
anchored_canonicalize("C:Users\\test", "data")previously returned malformed\\?\C:Users\test\data(missing backslash after colon)- Now correctly returns
\\?\C:\Users\test\data - This was a security-relevant fix as downstream boundary checks could fail on malformed paths
-
Windows junction/symlink handling in
anchored_canonicalize- Fixed prefix format mismatch: anchor uses verbatim format (
\\?\C:\...) while junction targets use regular disk format (C:\...), causingstrip_prefixto fail - Fixed 8.3 short name mismatch: junction targets may contain short names (e.g.,
RUNNER~1) while the anchor uses long names (runneradmin) - Implemented component-aware prefix comparison treating
VerbatimDisk(C)andDisk(C)as equivalent - Added canonicalization of junction targets before comparison to expand 8.3 short names
- Added comprehensive regression test suite (
tests/windows_junction_anchored_fix.rs) with 10 tests
- Fixed prefix format mismatch: anchor uses verbatim format (
-
New
proc-canonicalizefeature (default enabled)- Fixes Linux
/proc/PID/rootmagic symlink handling via theproc-canonicalizecrate - Can be disabled with
default-features = falseto usestd::fs::canonicalizebehavior - The
duncefeature now uses conditional forwarding (proc-canonicalize?/dunce)
- Fixes Linux
-
Comprehensive feature combination test suite (
tests/feature_combinations.rs)- Tests all four feature combinations on Windows and Linux
- Validates
proc-canonicalizebehavior differences - Validates
duncepath simplification on Windows
- Dependency: Added optional
proc-canonicalizedependency (zero runtime deps, ~200 lines) - Feature matrix:
Features Backend default (proc-canonicalize) proc_canonicalize::canonicalizedefault + dunce proc_canonicalizewith dunce feature--no-default-features std::fs::canonicalize--no-default-features + dunce dunce::canonicalize(Windows)
- Documentation improvements and reorganization (#40)
- Clarified
realpath()terminology: Mentioned once thatstd::fs::canonicalizeis Rust's equivalent to Unixrealpath(), then consistently usedstd::fs::canonicalizethroughout to avoid confusion (Rust doesn't have arealpath()function) - Streamlined comparison table: Removed
realpath()from "Comparison with Alternatives" table to focus exclusively on Rust crates and std functions (std::fs::canonicalize,std::path::absolute,dunce::canonicalize) - Added MSRV badge to README.md
- Added comprehensive "Lexical vs. Filesystem-Based Resolution" section explaining the difference between I/O-based and lexical path resolution approaches with practical guidance
- Added "Use Cases" section with practical examples (Path Comparison, Build Systems, Configuration Validation, Deduplication, Cross-Platform Normalization)
- Streamlined optional features documentation (removed redundant Windows-specific notes that are now clear from target-conditional dependency)
- Added "Related Projects" section highlighting
strict-pathintegration - Enhanced "Security & CVE Coverage" section with bullet-point list of built-in protections (ADS validation, symlink cycle detection, path traversal clamping, null byte rejection, UNC/device semantics, TOCTOU race resistance)
- Moved security content higher in README for better visibility
- Removed redundant "What is Path Canonicalization?" section (concepts integrated into other sections)
- Removed unnecessary
use std::path::PathBuf;import from Basic Example doctest - Better structured documentation flow focusing on user needs first, then implementation details
- Clarified
anchored_canonicalize: Relative symlinks with excessive..components are now clamped during resolution instead of relying on caller post-processing- Improves performance by eliminating redundant safety checks
- Enforces virtual filesystem semantics at the correct layer (defense-in-depth)
- No observable behavior change - final output identical to previous versions
- Both absolute and relative symlinks now consistently clamped in
resolve_anchored_symlink_chain
- Windows path prefix comparison bug: Fixed component-based comparison to properly handle Windows path prefix format differences (
Prefix::VerbatimDiskvsPrefix::Disk)- Previously, symlink clamping could fail when anchor had
\\?\prefix but resolved symlink didn't (or vice versa) - Added
components_equal_windows_awarehelper that treatsVerbatimDisk(C)andDisk(C)as equivalent - Fixes 3 test failures on GitHub Actions Windows runners with symlink privileges enabled
- Previously, symlink clamping could fail when anchor had
- Documentation reorganization: "How It Works" and security sections moved lower for better user experience
- Improved discoverability and clarity of advanced implementation details
- New symlink-first resolution tests for anchored canonicalization, including Windows-compatible coverage
- Comprehensive test coverage for relative symlink clamping behavior (7 new tests in
anchored_relative_symlink_clamping.rs) - Feature-conditional assertions in Windows tests to properly validate dunce vs non-dunce output formats
- Documentation discoverability improvements
- Added
#[doc(alias)]attributes to improve API discoverability:soft_canonicalize: aliases forrealpath,canonicalize,resolve,absoluteanchored_canonicalize: aliases forchroot,jail,sandbox,virtual_rootMAX_SYMLINK_DEPTH: aliases forELOOP,symlink_limit
- Added
#[must_use]attributes tosoft_canonicalizeandanchored_canonicalizeto prevent accidental result dropping
- Added
- Documentation enhancements
- Enhanced "Why Use This?" section to mention
duncefeature in compatibility bullet point - Enhanced "Why Use This?" section to highlight
anchoredfeature for virtual filesystem support - Fixed cross-platform doctest compatibility by adding
#[cfg(windows)]to Windows-specific Basic Example
- Enhanced "Why Use This?" section to mention
- Fixed raw string escaping in doc comments and test examples (#34)
- New
virtual_filesystem_demoexample demonstrating multi-tenant security scenarios (#31)- Complete example showing anchored canonicalization preventing directory traversal attacks
- Demonstrates proper symlink clamping in virtual filesystem contexts
- Includes both attack scenarios (what doesn't work) and correct usage patterns
- Documentation polish and reorganization
- Improved README.md tagline for better clarity and searchability (mentions
realpathfor SEO) - Enhanced "Why Use This?" section with clearer value propositions
- Streamlined "Comparison with Alternatives" section with "When to Use Each" bullet points
- Removed redundant "Testing & Quality" section (covered by value props)
- Added references to
virtual_filesystem_demoexample in README and lib.rs - Enhanced lib.rs documentation with comprehensive "Why Use This?" section matching README quality
- Eliminated redundant messaging between sections
- Improved README.md tagline for better clarity and searchability (mentions
-
New optional
duncefeature for simplified Windows path output (Windows-only) (#26)- Windows-specific: Feature only affects Windows; has no effect and adds no dependencies on Unix/Linux/macOS
- Configured as target-conditional dependency in Cargo.toml (
[target.'cfg(windows)'.dependencies]) - When enabled on Windows, returns familiar paths (
C:\foo) instead of extended-length UNC format (\\?\C:\foo) when safe - Zero code duplication - delegates all safety logic to the battle-tested dunce crate
- Opt-in feature provides user choice between security (UNC, default) and compatibility (simplified)
- Captures the dunce crate's market: "Like dunce, but works with non-existing paths"
- Automatically keeps UNC format for:
- Paths longer than 260 characters
- Reserved device names (CON, PRN, NUL, COM1-9, LPT1-9)
- Paths with trailing spaces or dots
- Paths containing literal
..components
-
Comprehensive exotic edge case tests from dunce/MSDN analysis (#28)
- 14 new tests covering Windows filename edge cases
- Reserved names with extensions and trailing characters
- Unicode normalization and multibyte UTF-16 handling
- Long paths and deeply nested directories
- Control characters in different contexts
- All edge cases verified - no implementation changes needed
-
Cross-platform path handling tests (
tests/cross_platform_paths.rs)- 12 new tests verifying graceful handling of Windows-style paths on Unix and vice versa
- Unix tests: Windows UNC paths, drive letters, backslash handling, absolute paths
- Windows tests: UNC network paths, device namespaces, mixed separators, Unix-style forward slashes
- Important for build systems, package managers, and cross-compilation tools
-
Comprehensive Windows 8.3 short name test coverage
- 16 new tests for 8.3 detection and expansion behavior (
windows_8_3_actual_expansion.rs,windows_8_3_toctou_anchored.rs,windows_8_3_unit_tests.rs) - 9 new tests for symlink+8.3 interaction scenarios (
windows_symlink_8_3_interaction.rs) - Validates correct handling of short names, TOCTOU race conditions, and symlink resolution with extended-length prefixes
- 16 new tests for 8.3 detection and expansion behavior (
-
Documentation improvements (#23, #24, #25)
- Added
realpath()(libc) to comparison tables for better discoverability - Added
std::path::absolute()to comparison tables - Updated version references from 0.3 to 0.4
- Enhanced feature documentation in README and lib.rs
- Added
- Critical bug in
anchored_canonicalizesymlink clamping (#27)- Issue: When a relative symlink resolved outside the anchor boundary, the function would discard all path information and return just the anchor itself
- Impact: Broke virtual filesystem semantics for downstream crates (strict-path-rs)
- Fix: Implemented proper common ancestor detection to preserve path structure while clamping
- Example:
jail/special -> ../../opt/subdirnow correctly resolves tojail/opt/subdir/...instead of justjail - Discovered via downstream CI failure in strict-path-rs (issue #18)
- Updated benchmark results (October 8, 2025, 5-run median protocol)
- Windows: 9,907 paths/s (1.31x faster than Python pathlib) — improved from 7,985 paths/s
- Linux (WSL): 238,038 paths/s (2.90x faster than Python 3.13 pathlib) — improved from 1.68x to 2.90x speedup
- Test suite enhancements
- Test count increased from 339 to 434 tests (429 unit tests + 5 doc tests)
- Added comprehensive dunce feature test suite (585 lines)
- Added format verification tests to ensure exact output format per feature state
- Added cross-platform path handling tests (12 tests, 369 lines)
- Added Windows 8.3 short name tests (25 tests across 4 files)
- Added exotic edge case tests (14 tests, 880 lines)
- Refactored 13+ test files to use explicit
#[cfg]blocks for feature-conditional testing - Enhanced CI with feature matrix testing (anchored, anchored+dunce combinations)
- Performance optimizations: Added
#[inline]to hot paths (simple_normalize_path,compute_existing_prefix,resolve_simple_symlink_chain) - Enhanced CI scripts (
ci-local.ps1,ci-local.sh) with feature combination testing
- Optimized
soft_canonicalizeandanchored_canonicalizefunctions- Reduced allocations by eliminating unnecessary temporary
OsStringinstances - Simplified control flow by replacing queue-based iteration with direct component streaming
- Reduced memory usage by removing
VecDequeoverhead - Optimized string comparisons to avoid unnecessary allocations
- Benchmark Results (October 2025, 5-run median protocol):
- Windows: 7,985 paths/s (1.57x faster than Python pathlib)
- Linux (WSL): 239,059 paths/s (1.68x faster than Python 3.13 pathlib)
- See
benches/README.mdfor complete benchmark data and protocol
- Reduced allocations by eliminating unnecessary temporary
- BEHAVIOR CHANGE:
anchored_canonicalizenow clamps absolute symlinks to the anchor (virtual filesystem semantics)- Previous Behavior: Absolute symlinks resolved to their actual filesystem targets (e.g.,
/etc/config) - New Behavior: Absolute symlink targets are reinterpreted relative to the anchor (e.g.,
/etc/config→anchor/etc/config) - Implementation: New
resolve_anchored_symlink_chain()function with dual-case clamping:- Case 1: Target within anchor → strip anchor prefix, rejoin to anchor
- Case 2: Target outside anchor → strip root prefix, join to anchor
- Rationale: Makes
anchored_canonicalizebehave like a virtual filesystem where the anchor is the root - Use Cases: Archive extraction, containerized paths, virtual directory trees where absolute symlinks should stay within the tree
- Previous Behavior: Absolute symlinks resolved to their actual filesystem targets (e.g.,
- Enhanced documentation: Comprehensive explanation of the dual-case clamping algorithm in
src/symlink.rsandsrc/lib.rs
-
Added: Comprehensive CVE-2024-2025 security test suite (
src/tests/cve_2024_2025_security.rs)- 30+ blackbox/whitebox tests covering recent CVE patterns:
- CVE-2025-27210: Windows device name path traversal
- CVE-2025-23084: Windows drive handling vulnerabilities
- CVE-2024-23651: Symlink TOCTOU race conditions (Docker/Buildkit)
- CVE-2024-21626: File descriptor leaks via /proc/self/fd
- CVE-2025-9566: Podman symlink traversal (ConfigMap/Secret escapes)
- CVE-2024-38819: Path traversal via crafted HTTP requests
- Tests validate resilience against similar attack patterns
- 30+ blackbox/whitebox tests covering recent CVE patterns:
-
Added: Dedicated symlink clamping test suite (
src/tests/anchored_symlink_clamping.rs)- 12+ tests documenting and verifying correct absolute symlink clamping behavior
- Archive extraction scenarios, chained symlinks, mixed absolute/relative chains
- Confirms that ALL absolute symlink targets are clamped to anchor (virtual filesystem semantics)
-
Added: Windows path stripping tests (
src/tests/windows_path_stripping.rs)- Validates
strip_root_prefixlogic for all Windows path types - Covers: disk paths, UNC paths, extended-length paths, verbatim paths, drive-relative paths
- Validates
-
Updated: Test names updated to reflect new behavior
absolute_symlink_drops_clamp→absolute_symlink_is_clamped(multiple files)- Test assertions updated to verify clamping instead of escape
- Updated: Aligned documentation with new behavior
- Updated
docs/SECURITY.md, README.md, and inline function documentation to match the new implementation - Changed "drop the clamp by design" statements to "are clamped to the anchor"
- Added detailed examples showing how absolute symlink clamping works in practice
- Clarified that anchor acts as a chroot-like virtual root for all path resolution
- Updated
- For Users: If you're using
anchored_canonicalize, absolute symlinks now resolve relative to the anchor (virtual filesystem semantics). Previously they resolved to their actual filesystem location. - Example: A symlink
anchor/link -> /etc/confignow resolves toanchor/etc/configinstead of/etc/config - Compatibility: If your code expected absolute symlinks to resolve to their actual filesystem targets, this behavior has changed. The anchor now acts as a virtual root for all path resolution.
- All existing tests pass; 50+ new tests added covering the new clamping behavior.
- Corrected
strict-pathfeature comparison:VirtualRoot(notPathBoundary) is the correct equivalent to ouranchored_canonicalizefunctionality
- Updated feature comparison table in README.md to reflect the new
strict-pathcrate, replacing the previousjailed-pathreference - Clarified "Anchored canonicalization" feature description as "Virtual/bounded canonicalization" for better terminology alignment
- Version bump to 0.3.5
- Enhanced crate comparison clarity by updating the feature comparison table with more accurate descriptions of virtual/bounded path canonicalization capabilities
- Documentation and examples now pass raw anchors to
anchored_canonicalize; clarified that the API soft-canonicalizes the anchor internally. No API changes.
- New Windows tests asserting exact, literal extended-length paths (e.g.,
\\?\C:\Users\…) for non-existing anchors and inputs. - Test covering anchors that include
..segments, confirming internal soft-canonicalization normalizes the base and yields equal results for equivalent inputs.
- Test style hardened across the suite:
- Prefer full
assert_eq!comparisons overstarts_with/ends_withhints. - Use raw strings for Windows inputs and readable, single-join or full-literal expected paths.
- Prefer full
- Added “Testing Rules for Agents (must follow)” to
AGENTS.mdto codify exact-equality expectations, Windows raw-string usage, anchored semantics, symlink policy, and environment assumptions. - Clarified ADS/CVE coverage in security tests; no behavior changes.
- Behavior is unchanged; this release focuses on clearer docs/examples and stricter, more readable tests.
- 🎯 NEW FEATURE: Anchored Canonicalization - Correct symlink resolution within virtual/constrained directory spaces
- New public API:
anchored_canonicalize(anchor_dir, path)function for anchor-relative path resolution - Feature-gated: Available under the optional
anchoredfeature flag (no additional dependencies) - Virtual space symlinks: Ensures proper symlink resolution behavior within bounded directory trees
- Use cases: Virtual filesystems, containerized environments, chroot-like scenarios, build systems
- Cross-platform: Works on Windows, macOS, and Linux with platform-specific optimizations
- New public API:
- Comprehensive test coverage: Expanded from 273 to 299 comprehensive tests (+26 new tests)
- New test modules covering symlink resolution in virtual spaces
- Enhanced boundary condition testing and Unicode edge case coverage
- Platform-specific behavior validation for Windows UNC paths and Unix symlinks
- Performance optimizations: Added
#[inline]attributes to hot-path functions in symlink and Windows modules - Documentation: New examples and enhanced security guidance for the anchored canonicalization feature
- Feature flag
anchoredadds the newanchored_canonicalizefunction without increasing compile time for existing users - Maintains zero runtime dependencies while providing enterprise-grade path security
- All existing APIs remain unchanged - this is a pure feature addition
- Major code refactoring: Split monolithic
lib.rsinto focused modules for better maintainability:src/error.rs- Error handling utilities and path-aware error constructionsrc/normalize.rs- Path normalization algorithms (simple_normalize_path)src/prefix.rs- Existing prefix computation and symlink handling (compute_existing_prefix)src/symlink.rs- Symlink chain resolution (resolve_simple_symlink_chain)src/windows.rs- Windows-specific functionality (ADS validation, UNC handling, 8.3 detection)
- Better error reporting: ADS validation now uses path-aware error construction for clearer error messages
- Better symlink handling: Improved
.and..processing during symlink traversal with "symlink-first semantics" - Performance optimization: Restored fast-path optimization for non-existing first components
- Code organization: Better separation of concerns and module boundaries for easier maintenance
- Moved ~1000+ lines from
lib.rsto specialized modules while preserving all functionality - Improved
validate_windows_ads_layouterror reporting with path-aware error construction - Maintained full API compatibility - no breaking changes to public interface
- All 110+ unit tests and integration tests continue to pass
- Fix path resolution order to prevent an edge case where a symlink followed by a
..component could incorrectly resolve against the symlink's parent instead of the symlink target. This behavior was incorrect; the change restores the intended semantics and aligns behavior with platform expectations for existing paths by attemptingstd::fs::canonicalizeon the original absolute path first, then lexically normalizing and retrying when appropriate. This is a bug fix, not a breaking behaviour change.
- Unit tests covering symlink-first
..resolution semantics (src/tests/symlink_dotdot_symlink_first.rs).
- Symlink chain resolution algorithm and cycle detection (smaller allocation strategy and safer textual cycle checks).
- Minor README and bench README clarifications; updated reported test count.
- Optimized Windows path handling (small runtime optimizations to path processing on Windows).
- Windows NTFS Alternate Data Stream (ADS) Security Validation: Comprehensive protection against ADS-based path traversal attacks
- New
validate_windows_ads_layout()function to detect malicious ADS patterns - Early and late ADS validation to prevent CVE-2025-8088 style attacks (e.g.,
file.txt:..\\..\\evil.exe) - Stream name validation for proper syntax, length limits, and forbidden content
- Type token validation for NTFS stream types (
$DATA,$BITMAP, etc.) - Unicode manipulation attack prevention (zero-width characters, BOM, etc.)
- Reserved device name protection in stream names
- New
- Comprehensive ADS Attack Vector Test Suite: 16 new test files covering sophisticated attack patterns:
ads_advanced_exploits.rs: Type token confusion, chaining attacks, filesystem limit exploitationads_comprehensive_security.rs: CVE patterns and malicious attack vectorsads_cross_platform_security.rs: Cross-platform ADS security validationads_performance_exploits.rs: Memory exhaustion and DoS attack preventionads_race_conditions.rs: TOCTOU attack protection during ADS parsingads_security_verification.rs: High-risk attack vector verificationarchive_ads_exploits.rs: Archive-style path pattern testscrypto_ads_bypass.rs: Cryptographic bypass vulnerability testsencoding_penetration.rs: Advanced Unicode/encoding attack testsfilesystem_boundary_attacks.rs: Filesystem limits and boundary condition testsfilesystem_metadata_attacks.rs: Extended attributes and metadata exploitation testskernel_boundary_ads.rs: Kernel/syscall boundary vulnerability testsprotocol_confusion.rs: Protocol confusion attack tests (UNC, HTTP, file URIs)unicode_advanced_attacks.rs: Sophisticated Unicode-based attack vectorswindows_ads_traversal.rs: Windows-specific ADS traversal and CVE-2025-8088 regression testswindows_std_ads_behavior.rs: Empiricalstd::fs::canonicalizebehavior validation
- CVE-2025-8088 Protection: Specific protection against WinRAR-style ADS path traversal attacks
- Malicious Stream Detection: Validates NTFS ADS syntax, rejecting patterns like
file:../../../evil.exe - Unicode Normalization Security: Consistent behavior with Unicode normalization forms and edge cases
- Path Boundary Validation: Comprehensive testing of path resolution boundaries and component limits
- Symlink Cycle Protection: Enhanced detection and rejection of circular symlink references
- Race Condition Robustness: Protection against filesystem changes during canonicalization
- CI Quality: Added MSRV Clippy auto-fix to CI scripts (
ci-local.ps1,ci-local.sh) for better code quality - Documentation: Updated to reflect 250+ comprehensive tests (previously 182)
- Security Messaging: Improved focus on robustness validation rather than penetration testing terminology
- Test Coverage: Expanded from 182 to 250+ comprehensive tests including Windows-specific attack vectors
- Error Handling: Enhanced InvalidInput error reporting for malformed ADS patterns
- Trailing Whitespace: Removed trailing whitespace in documentation causing formatting check failures
- Windows 8.3 CVE Protection Suite: Comprehensive protection against 6 known Windows short filename vulnerabilities:
- CVE-2019-9855 (LibreOffice): Protection against Windows 8.3 path equivalence handling flaws
- CVE-2017-17793 (BlogoText): Prevention of backup file access through predictable 8.3 short names
- CVE-2020-12279 (Git): Protection against NTFS short name equivalence confusion
- CVE-2005-0471 (Java): Mitigation of predictable temporary file names from 8.3 truncation
- CVE-2002-2413 (WebSite Pro): Prevention of script source disclosure via 8.3 equivalent filenames
- CVE-2001-0795 (LiteServe): Protection against CGI script source disclosure through 8.3 exploitation
- Security Audit Short Filename Module: New
src/tests/security_audit/short_filename_bypass.rs(3 test suites) - Windows 8.3 CVE Test Suite: New
tests/windows_8_3_cve_tests.rswith 7 comprehensive CVE-specific tests (504 lines) - 8.3 Detection Validation Tests: New
tests/test_8_3_detection_validation.rs(2 security-critical tests, 150 lines) - Performance Regression Protection: New
tests/blackbox_performance_regression.rswith advanced performance testing:- Memory stress testing with very wide paths (1000+ components)
- Tilde component stress testing (500 iterations)
- Windows-specific performance attack vectors
- Concurrent performance stress testing (multi-threaded validation)
- Algorithmic complexity validation preventing quadratic-time attacks
- Edge Case Fuzzing Suite: New
tests/blackbox_edge_case_fuzzing.rs(4 boundary condition and Unicode tests)
- Unicode Filename Security: Protection against Unicode characters with tildes being misinterpreted as 8.3 short names
- Cross-Platform Security Validation: Enhanced test coverage ensuring security properties work across Windows and Unix
- Performance Attack Prevention: Comprehensive algorithmic complexity validation with memory exhaustion protection
- Memory Exhaustion Protection: Stress testing against memory consumption attacks with 4000+ character components
- Concurrent Security Testing: Multi-threaded stress testing (4 threads, 100 iterations each) ensuring security under concurrent load
- Test Module Organization: Enhanced Windows-only test organization with proper module naming:
- Renamed
mod teststomod windows_unc_testsin UNC-related test files for better organization - Improved
#[cfg(windows)]guards for better CI compatibility across platforms
- Renamed
- Performance Testing: Advanced memory stress testing with component counts up to 1000 and individual component sizes up to 4000 characters
- Documentation: Updated README with comprehensive CVE protection details and security feature documentation
- Cross-Platform CI: Improved CI configuration preventing Linux pipeline issues with Windows-only code
- Windows UNC Path Support: New Windows-specific implementation with comprehensive UNC path handling
- Windows Extended-Length Path Support: Automatic conversion of Drive and UNC paths to
\\?\extended-length format - UNC Path Detection: Advanced UNC path detection including fallback parsing for raw
\\server\sharepatterns - Windows Device Namespace Handling: Lexical-only processing for
\\.\and\\?\GLOBALROOT\device paths - Comprehensive UNC Test Suite: Added 3 new black-box UNC test modules:
tests/blackbox_unc_attacks.rs: UNC-specific security penetration tests (4 tests)tests/blackbox_unc_corner_cases.rs: UNC edge case handling (6 tests)tests/blackbox_unc_extras.rs: Unicode obfuscation and long path tests (8 tests)
- Enhanced Platform-Specific Tests: Added 17 new Windows-specific tests in
src/tests/platform_specific.rs - Security Audit UNC Module: New
src/tests/security_audit/unc.rswhite-box UNC penetration tests (4 tests)
- Windows Path Canonicalization: Enhanced Windows implementation with proper UNC, Drive, and DeviceNS path handling
- UNC Server/Share Preservation: Exact preservation of Unicode sequences in UNC server and share names
- Mixed Separator Normalization: Robust handling of mixed
\and/separators in Windows paths - Parent Directory Clamping: Smart
.and..resolution with proper clamping at drive/UNC share roots - Unicode Attack Resistance: Preserves exact Unicode byte sequences to prevent normalization-based security bypasses
- UNC Jail Break Prevention: Parent directory traversal cannot escape above UNC share root (
\\server\share) - Drive Root Protection: Parent traversal properly clamped at drive roots for extended-length paths
- Long Path Attack Mitigation: Safe handling of very long paths (>260 chars) using Windows extended-length prefixes
- Alternate Data Stream Preservation: Textual preservation of ADS suffixes (
:stream_name) in path components
- Documentation Restructuring: Major reorganization of README.md and lib.rs documentation for better clarity and user experience
- Quick Start Section: Moved installation and basic usage examples to the top of documentation for easier onboarding
- Algorithm Documentation: Enhanced technical documentation with detailed time complexity analysis and optimization explanations
- Performance Information: Consolidated and improved performance benchmarking information with clearer presentation
- Security Documentation: Better organization of security features and vulnerability testing information
- Code Example Improvements: Simplified and streamlined code examples for better readability
- Documentation Structure: Reorganized content flow to prioritize practical usage over technical details
- Technical Details: Moved detailed algorithm explanations to more appropriate sections in the documentation
- Enhanced Security Test Suite: Comprehensive security audit module reorganization with platform-specific tests
- Blackbox TOCTOU Attack Testing: New
blackbox_toctou_attacks.rswith Time-of-Check-to-Time-of-Use race condition testing - Platform-Specific Security Tests: Dedicated Unix and Windows security test modules for platform-specific edge cases
- Unicode Security Testing: Enhanced Unicode path edge case testing including emoji, zero-width characters, and mixed scripts
- Unix-Specific Testing: Non-UTF8 filename handling tests with macOS UTF-8 enforcement vs Linux permissive behavior
- Windows-Specific Testing: Windows 8.3 short name symlink expansion tests
- Test Organization: Reorganized security tests into dedicated
security_auditmodule structure - Cross-Platform Coverage: Better separation of platform-specific test cases for Unix and Windows
- Race Condition Testing: Advanced TOCTOU attack simulation with atomic directory-to-symlink replacement
- Unicode Handling: More comprehensive Unicode normalization and encoding bypass prevention tests
- Error Handling: Enhanced null byte injection testing with platform-specific error validation
- Test Structure: Moved and reorganized security hardening tests from single file to modular security audit structure
- Platform Compatibility: Improved handling of platform-specific filesystem limitations and behaviors
- Major Algorithm Optimization: Complete rewrite for 1.3x-1.5x performance improvement over Python's pathlib
- Binary Search Boundary Detection: Replaced O(n) linear search with O(log n) binary search for existing path components
- Fast-path Optimization: Direct
std::fs::canonicalizefor existing paths (inspired by Python's strategy) - Single-pass Path Normalization: Efficient batch processing of
.and..components with minimal allocations - Optimized Symlink Resolution: Smart symlink chain handling with O(1) cycle detection using HashSet
- Comprehensive Performance Benchmarking: Added extensive benchmark suite comparing against Python 3.12.4 pathlib
- Mixed workloads: 6,089-6,769 paths/s (1.3x-1.5x faster than Python's 4,627 paths/s)
- Existing paths: 10,057-12,851 paths/s (1.5x-1.9x faster than Python's ~6,600 paths/s)
- Path traversal: 11,551-13,529 paths/s (1.8x-2.1x faster than Python's ~6,500 paths/s)
- Non-existing paths: 1,950-2,072 paths/s (competitive with Python's 2,516-4,441 paths/s)
- Comprehensive Black-box Security Testing: New
blackbox_security.rstest suite with extensive fuzzing and attack simulation - Advanced Attack Vector Testing: Directory traversal, symlink escapes, performance attacks, race conditions, filesystem boundary crossing
- Windows-specific Security Tests: Enhanced testing for Windows short names (8.3), device names (CON, NUL, etc.), and NTFS Alternate Data Streams (ADS)
- Resource Exhaustion Protection: Added safeguards against long filenames, deep directory structures, and excessive path components
- Complex Attack Pattern Testing: Broken symlink jail escapes, case sensitivity bypasses, and API contract violations
- Algorithm Complexity: Reduced from O(n) to O(log n) for boundary detection, O(k) overall where k = existing components
- Memory Optimization: Efficient component collection with reduced allocations and smarter buffering
- Cross-platform Robustness: Improved handling of platform-specific filesystem limits and system symlinks
- Security Test Coverage: Comprehensive test suite with 108 security-focused tests covering sophisticated attack patterns
- Removed unnecessary clippy allows and improved code consistency across test modules
- Better test organization with clear attack vector categorization and comprehensive documentation
- Enhanced inline documentation explaining security test purposes, algorithm optimizations, and performance characteristics
- Fast-path optimization: Added fast-path for absolute existing paths without dot components using
std::fs::canonicalizedirectly - CVE Testing: Added
src/tests/cve_tests.rsmodule with CVE-2022-21658 race condition tests - Security Hardening: Added
src/tests/security_hardening.rsmodule with comprehensive security tests including null byte injection, Unicode normalization bypasses, double-encoding attacks, case sensitivity bypasses, and TOCTOU prevention - Symlink Resolution Order: Added
symlink_dotdot_resolution_ordertest module validating lexical dot-dot resolution behavior - Null Byte Handling: Added explicit null byte detection for Unix and Windows platforms
- Symlink Cycle Detection: Changed from
HashSet<PathBuf>toHashSet<Rc<PathBuf>>for visited symlink tracking - Test Coverage: Added new test modules (
cve_tests,security_hardening,symlink_dotdot_resolution_order) - Error Handling: Enhanced null byte error consistency with
std::fs::canonicalize
- Cross-platform Test Compatibility: Fixed CVE-2022-21658 race condition test to handle macOS symlink canonicalization where
/varis a symlink to/private/var - Windows UNC Path Compatibility: Fixed
std_compattest to correctly expect Windows UNC path format (\\?\C:\) returned bystd::fs::canonicalize
- Fast-path Implementation: Added condition checking for
path.is_absolute() && path.exists() && !path.components().any(|c| matches!(c, CurDir | ParentDir)) - Memory Optimization: Use
Rc<PathBuf>for symlink cycle detection to reduce memory allocations - Cross-platform: Added platform-specific null byte detection using
OsStrExttraits - Symlink Cycle Detection: Changed from
HashSet<PathBuf>toHashSet<Rc<PathBuf>>for visited symlink tracking - Test Coverage: Added new test modules (
cve_tests,security_hardening,symlink_dotdot_resolution_order) - Error Handling: Enhanced null byte error consistency with
std::fs::canonicalize
- Fast-path Implementation: Added condition checking for
path.is_absolute() && path.exists() && !path.components().any(|c| matches!(c, CurDir | ParentDir)) - Memory Optimization: Use
Rc<PathBuf>for symlink cycle detection to reduce memory allocations - Cross-platform: Added platform-specific null byte detection using
OsStrExttraits
- Security Tests: Added advanced tests for symlinked directory jail break prevention, including scenarios with new files and nested symlinked directories to ensure robust security boundaries.
- Edge Case Robustness Module: Introduced
edge_case_robustnesstest module for improved coverage of rare and complex path resolution scenarios.
- Performance Documentation: Clarified time complexity as O(k) where k = existing path components (best: O(1), worst: O(n)), and updated all relevant documentation and README sections for accuracy.
- Test Coverage: Expanded from 51 to 59 tests, including new security and edge case tests, and updated README to reflect the increased coverage.
- Comparison Table: Enhanced README comparison table to clarify handling of
..components, jail enforcement, and type-safe jail markers for all compared crates. - Security Documentation: Added explicit documentation of symlink cycle detection and jail break prevention mechanisms in README, with references to new tests.
- Documentation: Enhanced README.md with better code example formatting and improved readability
- Code Examples: Added proper spacing in code examples for better visual separation of logical steps
- Security Examples: Improved security validation example with clearer
.expect()usage patterns
- Documentation: Enhanced README.md with better code example formatting and improved readability
- Code Examples: Added proper spacing in code examples for better visual separation of logical steps
- Security Examples: Improved security validation example with clearer
.expect()usage patterns
- Comprehensive Test Suite: Added 40 unit tests across 11 specialized modules, including Python-inspired edge cases, cross-platform validation, symlink handling, and advanced canonicalization scenarios
- Python-Inspired Testing: Added comprehensive edge case testing derived from Python's mature pathlib.resolve() implementation
- Performance Optimization: Added hybrid boundary detection optimization that uses
std::fs::canonicalizefor existing path portions before falling back to incremental resolution
- Test Coverage: Expanded from 28 tests to 51 comprehensive tests (37 unit + 11 std compatibility + 3 doctests) covering Python-inspired edge cases, cross-platform scenarios, and advanced canonicalization patterns
- Performance: Enhanced boundary detection algorithm for better performance on paths with existing prefixes
- Cross-Platform Robustness: Enhanced CI-safe testing patterns with panic-safe cleanup and working directory handling
- Documentation: Enhanced "How It Works" section to clearly explain use of
std::fs::canonicalizeinternally
- std Library Compatibility Tests: Added comprehensive test suite (
tests/std_compat.rs) importing and adapting original std library canonicalize tests to ensure 100% behavioral compatibility for existing paths - API Enhancement: Updated
soft_canonicalizeto acceptimpl AsRef<Path>instead of generic<P: AsRef<Path>>for cleaner, more modern API following Rust 2018+ best practices - Contributing Guidelines: Added
CONTRIBUTING.mdwith project philosophy, AI prompt for contributors, testing guidelines, and development workflow - Documentation Examples: Added comprehensive examples showing usage with different path types (
&str,PathBuf,&Path, etc.)
- API Modernization: Function signature changed from
soft_canonicalize<P: AsRef<Path>>(path: P)tosoft_canonicalize(path: impl AsRef<Path>)for consistency with modern Rust patterns - Test Infrastructure: Standardized all tests to use
tempfilecrate instead of custom temporary directory implementation for better reliability and consistency - Version Bump: First stable release (0.1.0) indicating API stability and production readiness
- Test Coverage: Added 8 new tests specifically for API compatibility with different path parameter types
- Test Reliability: Replaced custom
create_temp_dir()andcleanup_temp_dir()functions with industry-standardtempfile::tempdir()for automatic cleanup and thread safety - Code Quality: Removed ~40 lines of custom temporary directory logic in favor of standard practices
- Documentation: Enhanced function documentation with more comprehensive examples showing all supported input types
- Maintains 100% backward compatibility for function behavior
- All 28 tests pass (14 unit tests + 11 std compatibility tests + 3 doctests)
- Zero breaking changes for existing users
- Enhanced API ergonomics without performance impact
- Standardized development and testing practices
- Streamlined README: Reduced verbosity and condensed examples for better readability
- Added Security Examples: Added security validation examples with proper jail directory handling
- Enhanced Comparison Table: Added "Prevents symlink jail breaks" row highlighting security advantages
- Added Ecosystem Context: Added footnote showing
jailed-pathdependency relationship - Added Usage Guidance: Added note clarifying when to use
std::fs::canonicalizevssoft_canonicalize
- Improved Algorithm: Redesigned canonicalization algorithm inspired by Python's
pathlib.Path.resolve(strict=False) - Better Performance: Switched from "find existing prefix" approach to incremental symlink resolution
- Reduced I/O: Now performs lexical resolution first, only checking filesystem when paths actually exist
- Enhanced Efficiency: Single-pass processing instead of multiple walks up the directory tree
- Windows Compatibility: Better handling of Windows path edge cases and root component preservation
- Root Traversal: Fixed excessive
..component handling to properly maintain absolute paths on Windows - Symlink Resolution: More robust incremental symlink resolution strategy
- Code Quality: Cleaner, more maintainable implementation with better separation of concerns
- Updated README with Python
pathlib.Path.resolve()inspiration - Enhanced algorithm description to reflect new lexical + incremental approach
- Improved performance section with updated characteristics
- Added
normpath::PathExt::normalizeto comparison table for comprehensive ecosystem overview - Updated Quick Start example to reference version 0.0.2
- Lexical resolution now processes
..and.components mathematically before filesystem access - Incremental symlink resolution builds path component-by-component
- Optimized filesystem access patterns for better performance
- Maintained backward compatibility and all existing security guarantees
- Initial release of
soft-canonicalizecrate soft_canonicalize()function for pure path canonicalization- Support for non-existing paths through logical path resolution
- Cross-platform compatibility (Windows, macOS, Linux)
- Comprehensive test suite with 7 test cases covering:
- Existing path canonicalization
- Non-existing path handling
- Deep nested non-existing paths
- Relative path resolution
- Directory traversal (
..) component handling - Mixed existing/non-existing path resolution
- Root boundary traversal protection
- Zero-dependency implementation using only std
- Security-focused algorithm with mathematical path resolution
- Comprehensive documentation with examples
- Basic usage example demonstrating all major features
- Security demo example showing directory traversal prevention
- Pure Algorithm: No filesystem modification during canonicalization
- Directory Traversal Security: Logical resolution of
..components before filesystem access - Symlink Resolution: Proper handling of symlinks in existing path portions
- Performance: O(k) time complexity where k = existing components (k ≤ n), with minimal filesystem access
- Cross-Platform: Handles Windows drive letters, UNC paths, and Unix absolute paths
- Zero-Cost: Minimal memory overhead with efficient path processing
- Comprehensive README with usage examples
- API documentation with detailed algorithm explanation
- Security considerations and best practices
- Performance characteristics and complexity analysis
- Cross-platform compatibility notes
- Comparison with existing canonicalization solutions