1use std::sync::LazyLock;
2
3pub use error::Error;
4use regex::Regex;
5pub use sync::*;
6use uv_static::EnvVars;
7
8mod error;
9pub mod hash;
10pub mod stream;
11mod sync;
12mod vendor;
13
14static CONTROL_CHARACTERS_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\p{C}").unwrap());
15static REPLACEMENT_CHARACTER: &str = "\u{FFFD}";
16
17pub(crate) fn validate_archive_member_name(name: &str) -> Result<(), Error> {
32 if name.is_empty() {
33 return Err(Error::EmptyFilename);
34 }
35
36 match CONTROL_CHARACTERS_RE.replace_all(name, REPLACEMENT_CHARACTER) {
37 std::borrow::Cow::Borrowed(_) => Ok(()),
39 std::borrow::Cow::Owned(sanitized) => Err(Error::UnacceptableFilename {
40 filename: sanitized,
41 }),
42 }
43}
44
45pub(crate) fn insecure_no_validate() -> bool {
47 let Some(value) = std::env::var_os(EnvVars::UV_INSECURE_NO_ZIP_VALIDATION) else {
49 return false;
50 };
51 let Some(value) = value.to_str() else {
52 return false;
53 };
54 matches!(
55 value.to_lowercase().as_str(),
56 "y" | "yes" | "t" | "true" | "on" | "1"
57 )
58}
59
60#[cfg(test)]
61mod tests {
62 #[test]
63 fn test_validate_archive_member_name() {
64 for (testcase, ok) in &[
65 ("normal.txt", true),
67 ("__init__.py", true),
68 ("fine i guess.py", true),
69 ("🌈.py", true),
70 ("", false),
72 ("new\nline.py", false),
73 ("carriage\rreturn.py", false),
74 ("tab\tcharacter.py", false),
75 ("null\0byte.py", false),
76 ("control\x01code.py", false),
77 ("control\x02code.py", false),
78 ("control\x03code.py", false),
79 ("control\x04code.py", false),
80 ("backspace\x08code.py", false),
81 ("delete\x7fcode.py", false),
82 ] {
83 assert_eq!(
84 super::validate_archive_member_name(testcase).is_ok(),
85 *ok,
86 "testcase: {testcase}"
87 );
88 }
89 }
90
91 #[test]
92 fn test_unacceptable_filename_error_replaces_control_characters() {
93 let err = super::validate_archive_member_name("bad\nname").unwrap_err();
94 match err {
95 super::Error::UnacceptableFilename { filename } => {
96 assert_eq!(filename, "bad�name");
97 }
98 _ => panic!("expected UnacceptableFilename error"),
99 }
100 }
101}