chadtmiller.com/knotless
git that lives in your Atmosphere account
1
10
A site's file is named in UTF-8, and a refused path says why
crates/knotless/src/site.rs+20−3
| @@ -128,7 +128,8 @@ pub fn valid_path(path: &str) -> bool { | ||
} | ||
| ||
/// Every file under `dir`, by its path from it, in order. A link is | ||
/// refused rather than followed, so a site holds what the directory does. | ||
/// refused rather than followed, so a site holds what the directory does, | ||
/// and so is a name that is not UTF-8, as two such could read as one path. | ||
pub fn walk(dir: &Path) -> Result<Vec<(String, PathBuf)>> { | ||
let mut found = Vec::new(); | ||
let mut dirs = vec![(String::new(), dir.to_path_buf())]; | ||
| @@ -137,7 +138,8 @@ pub fn walk(dir: &Path) -> Result<Vec<(String, PathBuf)>> { | ||
for entry in std::fs::read_dir(&at).with_context(reading)? { | ||
let entry = entry.with_context(reading)?; | ||
let path = entry.path(); | ||
let name = entry.file_name().to_string_lossy().into_owned(); | ||
let utf8 = format!("{}: a site's file is named in UTF-8", path.display()); | ||
let name = entry.file_name().into_string().ok().context(utf8)?; | ||
let kind = entry.file_type().with_context(reading)?; | ||
let named = format!("{prefix}{name}"); | ||
if kind.is_dir() { | ||
| @@ -208,7 +210,9 @@ pub async fn deploy(ctx: &Ctx, url: &RepoUrl, spec: Deploy) -> Result<Deployed> | ||
let mut measured = Vec::new(); | ||
for (path, at) in found { | ||
if !valid_path(&path) { | ||
bail!("{path:?} is longer than a site's path can be, {MAX_PATH} bytes"); | ||
bail!( | ||
"{path:?} cannot be a site's path: at most {MAX_PATH} bytes, with no empty, . or .. part" | ||
); | ||
} | ||
let (size, sha256) = sha256_file(&at)?; | ||
measured.push((path, at, size, sha256)); | ||
| @@ -355,4 +359,17 @@ mod tests { | ||
assert_eq!(content_type("data.bin"), None); | ||
assert_eq!(content_type("dir.d/README"), None); | ||
} | ||
| ||
// Two names that are not UTF-8 could read as one path, so neither is | ||
// taken. Linux alone lets a file be named so. | ||
#[cfg(target_os = "linux")] | ||
#[test] | ||
fn a_name_not_in_utf8_is_refused() { | ||
use std::os::unix::ffi::OsStrExt; | ||
let dir = tempfile::tempdir().unwrap(); | ||
let name = std::ffi::OsStr::from_bytes(b"a\xff.html"); | ||
std::fs::write(dir.path().join(name), b"").unwrap(); | ||
let refused = walk(dir.path()).unwrap_err().to_string(); | ||
assert!(refused.contains("named in UTF-8"), "{refused}"); | ||
} | ||
} | ||
crates/knotless/tests/cli.rs+1−1
| @@ -1477,7 +1477,7 @@ fn sites() { | ||
let missing = s.path("missing").to_string_lossy().into_owned(); | ||
let refused = [ | ||
(vec![empty.as_str()], "a site holds 1 to 2000 files"), | ||
(vec![deep.as_str()], "longer than a site's path can be"), | ||
(vec![deep.as_str()], "cannot be a site's path"), | ||
(vec![linked.as_str()], "not links"), | ||
(vec![missing.as_str()], "reading"), | ||
( | ||