~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

Release files are read only by a hash that is a SHA-256

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:serve/release-hash
crates/knotless/src/release.rs+8−0
@@ -101,6 +101,14 @@ pub fn sha256(bytes: &[u8]) -> String {
format!("{:x}", Sha256::digest(bytes))
}
/// Whether `hash` is one [`sha256`] gives: 64 lowercase hex digits. A
/// record's hash names the file its bytes are kept in, so one that is not
/// could name a path outside where they are kept.
pub fn valid_sha256(hash: &str) -> bool {
let hex = |b: u8| matches!(b, b'0'..=b'9' | b'a'..=b'f');
hash.len() == 64 && hash.bytes().all(hex)
}
/// Publishes `spec` as the owner of `url`'s repository, logged in: its
/// files uploaded, then its record created. Returns the record's AT-URI.
/// A version published before is refused, before anything is uploaded,
crates/knotless-serve/src/releases.rs+28−1
@@ -146,7 +146,9 @@ impl Records {
&self,
file: &knotless::records::ReleaseFile,
) -> anyhow::Result<PathBuf> {
let at = self.kept.join(&file.sha256);
let hash = &file.sha256;
anyhow::ensure!(release::valid_sha256(hash), "{hash:?} is not a SHA-256");
let at = self.kept.join(hash);
if tokio::fs::try_exists(&at).await? {
return Ok(at);
}
@@ -250,3 +252,28 @@ async fn file(
Err(e) => failed(StatusCode::BAD_GATEWAY, e),
}
}
#[cfg(test)]
mod tests {
use super::*;
// A record's hash names the file its bytes are kept in: one that is
// not a SHA-256 is refused before it names a path.
#[tokio::test]
async fn a_file_is_kept_only_by_its_hash() {
let ctx = Ctx::new(knotless::env::Env::from_vars(|_| None));
let url = RepoUrl {
authority: "did:plc:a".into(),
name: "r".into(),
};
let records = Records::new(ctx, url, "p".into());
let file = knotless::records::ReleaseFile {
name: "x".into(),
size: 0,
sha256: format!("{:.<64}", "../../../../etc/passwd"),
parts: Vec::new(),
};
let refused = records.fetch(&file).await.unwrap_err().to_string();
assert!(refused.contains("is not a SHA-256"), "{refused}");
}
}