#[cfg(test)]
mod regressions;
mod secure;
#[cfg(test)]
mod tests;
use crate::model::artifacts::ArtifactChecksumRecord;
use sha2::{Digest, Sha256};
use std::{
io::{self, Read, Write},
path::{Path, PathBuf},
};
use thiserror::Error;
pub fn checksum_file(path: &Path) -> Result<ArtifactChecksumRecord, ArtifactError> {
secure::checksum_path(path, secure::ExpectedArtifactType::File)
}
pub fn checksum_path(path: &Path) -> Result<ArtifactChecksumRecord, ArtifactError> {
secure::checksum_path(path, secure::ExpectedArtifactType::Any)
}
pub fn checksum_directory(path: &Path) -> Result<ArtifactChecksumRecord, ArtifactError> {
secure::checksum_path(path, secure::ExpectedArtifactType::Directory)
}
pub fn checksum_reader(reader: &mut impl Read) -> Result<ArtifactChecksumRecord, ArtifactError> {
let mut hasher = Sha256::new();
let mut buffer = vec![0; 64 * 1024];
loop {
let read = reader.read(&mut buffer)?;
if read == 0 {
break;
}
hasher.update(&buffer[..read]);
}
Ok(ArtifactChecksumRecord::from_digest(
hasher.finalize().into(),
))
}
pub(crate) fn copy_from_reader(
reader: &mut impl Read,
writer: &mut impl Write,
) -> Result<ArtifactChecksumRecord, ArtifactError> {
let mut hasher = Sha256::new();
let mut buffer = vec![0; 64 * 1024];
loop {
let read = reader.read(&mut buffer)?;
if read == 0 {
break;
}
writer.write_all(&buffer[..read])?;
hasher.update(&buffer[..read]);
}
Ok(ArtifactChecksumRecord::from_digest(
hasher.finalize().into(),
))
}
pub(crate) fn checksum_relative_files(
mut files: Vec<(PathBuf, ArtifactChecksumRecord)>,
) -> ArtifactChecksumRecord {
files.sort_by(|left, right| left.0.cmp(&right.0));
let mut hasher = Sha256::new();
for (relative, checksum) in files {
hasher.update(relative.to_string_lossy().as_bytes());
hasher.update([0]);
hasher.update(checksum.hash().as_bytes());
hasher.update(*b"\n");
}
ArtifactChecksumRecord::from_digest(hasher.finalize().into())
}
pub fn checksum_relative_path(
root: &Path,
relative: &Path,
) -> Result<ArtifactChecksumRecord, ArtifactError> {
secure::checksum_relative_path(root, relative)
}
pub fn stage_relative_path(
root: &Path,
relative: &Path,
destination: &Path,
) -> Result<ArtifactChecksumRecord, ArtifactError> {
secure::stage_relative_path(root, relative, destination)
}
#[derive(Debug, Error)]
pub enum ArtifactError {
#[error("artifact path is not UTF-8: {path:?}")]
NonUtf8Path {
path: PathBuf,
},
#[error(transparent)]
Io(#[from] io::Error),
#[error("unsupported artifact entry at {path}: {kind}")]
UnsupportedEntry {
path: String,
kind: String,
},
#[error("secure artifact traversal is unsupported on platform {0}")]
UnsupportedPlatform(&'static str),
}