soft-canonicalize 0.5.6

Path canonicalization that works with non-existing paths.
Documentation
use crate::{anchored_canonicalize, soft_canonicalize};
use std::path::Path;
use tempfile::TempDir;

#[test]
fn null_byte_injection_rejected() {
    #[cfg(unix)]
    {
        use std::ffi::OsStr;
        use std::os::unix::ffi::OsStrExt;
        let td = TempDir::new().unwrap();
        let anchor = td.path().join("root");
        std::fs::create_dir_all(&anchor).unwrap();
        let base = soft_canonicalize(&anchor).unwrap();

        let null_tail = OsStr::from_bytes(b"a\0b");
        let err = anchored_canonicalize(base, Path::new(null_tail)).unwrap_err();
        assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
    }

    #[cfg(windows)]
    {
        use std::ffi::OsString;
        use std::os::windows::ffi::OsStringExt;
        let td = TempDir::new().unwrap();
        let anchor = td.path().join("root");
        std::fs::create_dir_all(&anchor).unwrap();
        let base = soft_canonicalize(&anchor).unwrap();

        let null_tail: OsString = OsString::from_wide(&[97, 0, 98]);
        let err = anchored_canonicalize(base, Path::new(&null_tail)).unwrap_err();
        assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
    }
}

#[test]
fn zero_width_and_control_preserved() -> std::io::Result<()> {
    let td = TempDir::new()?;
    let anchor = td.path().join("r");
    std::fs::create_dir_all(&anchor)?;
    let base = soft_canonicalize(&anchor)?;

    let cases = [
        "file\u{200B}.txt",
        "file\u{0001}.txt",
        "résumé.txt",
        "🦀.txt",
    ];
    for c in cases {
        let out = anchored_canonicalize(&base, c)?;
        assert!(out.is_absolute());
        assert!(out.to_string_lossy().contains(".txt"));
    }
    Ok(())
}