feat: add support for SHA1 checksum verification and update related documentation
This commit is contained in:
Generated
+1
@@ -444,6 +444,7 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_ignored",
|
"serde_ignored",
|
||||||
|
"sha1",
|
||||||
"sha2",
|
"sha2",
|
||||||
"signal-hook 0.4.3",
|
"signal-hook 0.4.3",
|
||||||
"suppaftp",
|
"suppaftp",
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ serde_ignored = "0.1.14"
|
|||||||
lz4_flex = "0.13.0"
|
lz4_flex = "0.13.0"
|
||||||
lzma-rust2 = "0.16.2"
|
lzma-rust2 = "0.16.2"
|
||||||
signal-hook = "0.4.3"
|
signal-hook = "0.4.3"
|
||||||
|
sha1 = "0.10.6"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "=3.27.0"
|
tempfile = "=3.27.0"
|
||||||
|
|||||||
+38
-2
@@ -25,7 +25,7 @@
|
|||||||
use crate::fakeroot;
|
use crate::fakeroot;
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::{ErrorKind, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, ExitStatus, Stdio};
|
use std::process::{Command, ExitStatus, Stdio};
|
||||||
|
|
||||||
@@ -549,7 +549,11 @@ fn run_command_with_optional_stdin(
|
|||||||
command.stdin(Stdio::piped());
|
command.stdin(Stdio::piped());
|
||||||
let mut child = command.spawn()?;
|
let mut child = command.spawn()?;
|
||||||
if let Some(mut stdin) = child.stdin.take() {
|
if let Some(mut stdin) = child.stdin.take() {
|
||||||
stdin.write_all(payload.as_bytes())?;
|
match stdin.write_all(payload.as_bytes()) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(err) if err.kind() == ErrorKind::BrokenPipe => {}
|
||||||
|
Err(err) => return Err(err.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(child.wait()?)
|
Ok(child.wait()?)
|
||||||
} else {
|
} else {
|
||||||
@@ -674,6 +678,38 @@ needs_paths = true
|
|||||||
assert_eq!(out, "install:pre:foo");
|
assert_eq!(out, "install:pre:foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_transaction_hooks_ignores_broken_pipe_for_needs_paths_hooks() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
write_hook(
|
||||||
|
tmp.path(),
|
||||||
|
"noop.toml",
|
||||||
|
r#"
|
||||||
|
[hook]
|
||||||
|
name = "noop"
|
||||||
|
|
||||||
|
[when]
|
||||||
|
phase = "pre"
|
||||||
|
operation = ["install"]
|
||||||
|
packages = ["foo"]
|
||||||
|
|
||||||
|
[exec]
|
||||||
|
command = "true"
|
||||||
|
needs_paths = true
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
let affected = vec!["usr/bin/foo".to_string()];
|
||||||
|
let ctx = HookExecutionContext {
|
||||||
|
phase: HookPhase::Pre,
|
||||||
|
operation: HookOperation::Install,
|
||||||
|
package: "foo",
|
||||||
|
affected_paths: &affected,
|
||||||
|
};
|
||||||
|
let ran = run_transaction_hooks(tmp.path(), &ctx).unwrap();
|
||||||
|
assert_eq!(ran, 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn run_transaction_hooks_respects_negation_and_filters_out() {
|
fn run_transaction_hooks_respects_negation_and_filters_out() {
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ fn prompt_manual_sources() -> Result<Vec<ManualSource>> {
|
|||||||
if single_entry {
|
if single_entry {
|
||||||
let checksum = prompt_optional_text(
|
let checksum = prompt_optional_text(
|
||||||
"Manual source checksum (optional):",
|
"Manual source checksum (optional):",
|
||||||
"sha256:/sha512:/md5:/b2:/b2sum:, raw SHA256 hex, or 'skip' (empty omits field)",
|
"sha256:/sha512:/sha1:/md5:/b2:/b2sum:, raw SHA256 hex, or 'skip' (empty omits field)",
|
||||||
)?;
|
)?;
|
||||||
if !checksum.is_empty() {
|
if !checksum.is_empty() {
|
||||||
manual.sha256 = Some(checksum);
|
manual.sha256 = Some(checksum);
|
||||||
@@ -411,7 +411,7 @@ pub fn create_interactive() -> Result<PackageSpec> {
|
|||||||
|
|
||||||
let source_sha256 = Text::new("Source checksum:")
|
let source_sha256 = Text::new("Source checksum:")
|
||||||
.with_help_message(
|
.with_help_message(
|
||||||
"Accepts sha256:, sha512:, md5:, b2:, b2sum:, or raw SHA256 hex (use 'skip' to bypass)",
|
"Accepts sha256:, sha512:, sha1:, md5:, b2:, b2sum:, or raw SHA256 hex (use 'skip' to bypass)",
|
||||||
)
|
)
|
||||||
.with_default(computed_sha_default.as_str())
|
.with_default(computed_sha_default.as_str())
|
||||||
.prompt()?;
|
.prompt()?;
|
||||||
|
|||||||
+1
-1
@@ -3930,7 +3930,7 @@ impl Alternatives {
|
|||||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct Source {
|
pub struct Source {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
/// Checksum for the source (e.g. `sha256:...`, `sha512:...`, `md5:...`, `b2:...`, `b2sum:...`, or raw SHA256 hex).
|
/// Checksum for the source (e.g. `sha256:...`, `sha512:...`, `sha1:...`, `md5:...`, `b2:...`, `b2sum:...`, or raw SHA256 hex).
|
||||||
/// Defaults to `skip` when omitted.
|
/// Defaults to `skip` when omitted.
|
||||||
#[serde(default = "default_source_sha256")]
|
#[serde(default = "default_source_sha256")]
|
||||||
pub sha256: String,
|
pub sha256: String,
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ pub fn fetch_archive(spec: &PackageSpec, source: &Source, cache_dir: &Path) -> R
|
|||||||
/// Supported formats:
|
/// Supported formats:
|
||||||
/// - `sha256:<hex>` (or just `<hex>` — default)
|
/// - `sha256:<hex>` (or just `<hex>` — default)
|
||||||
/// - `sha512:<hex>`
|
/// - `sha512:<hex>`
|
||||||
|
/// - `sha1:<hex>`
|
||||||
/// - `md5:<hex>`
|
/// - `md5:<hex>`
|
||||||
/// - `b2:<hex>` / `b2sum:<hex>`
|
/// - `b2:<hex>` / `b2sum:<hex>`
|
||||||
/// - `skip` to bypass verification
|
/// - `skip` to bypass verification
|
||||||
@@ -732,7 +733,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_checksum_accepts_md5_sha512_b2sum_and_default_sha256() {
|
fn verify_checksum_accepts_sha1_md5_sha512_b2sum_and_default_sha256() {
|
||||||
|
use sha1::Sha1;
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
use sha2::Sha512;
|
use sha2::Sha512;
|
||||||
@@ -752,6 +754,11 @@ mod tests {
|
|||||||
h.update(b"abc");
|
h.update(b"abc");
|
||||||
format!("{:x}", h.finalize())
|
format!("{:x}", h.finalize())
|
||||||
};
|
};
|
||||||
|
let sha1_hex = {
|
||||||
|
let mut h = Sha1::new();
|
||||||
|
h.update(b"abc");
|
||||||
|
format!("{:x}", h.finalize())
|
||||||
|
};
|
||||||
|
|
||||||
let md5_hex = format!("{:x}", md5::compute(b"abc"));
|
let md5_hex = format!("{:x}", md5::compute(b"abc"));
|
||||||
let b2_hex = b2sum_rust::Blake2bSum::new(64)
|
let b2_hex = b2sum_rust::Blake2bSum::new(64)
|
||||||
@@ -763,6 +770,7 @@ mod tests {
|
|||||||
// explicit prefixes
|
// explicit prefixes
|
||||||
assert!(verify_checksum(tmp.path(), &format!("sha256:{}", sha256_hex)).unwrap());
|
assert!(verify_checksum(tmp.path(), &format!("sha256:{}", sha256_hex)).unwrap());
|
||||||
assert!(verify_checksum(tmp.path(), &format!("sha512:{}", sha512_hex)).unwrap());
|
assert!(verify_checksum(tmp.path(), &format!("sha512:{}", sha512_hex)).unwrap());
|
||||||
|
assert!(verify_checksum(tmp.path(), &format!("sha1:{}", sha1_hex)).unwrap());
|
||||||
assert!(verify_checksum(tmp.path(), &format!("md5:{}", md5_hex)).unwrap());
|
assert!(verify_checksum(tmp.path(), &format!("md5:{}", md5_hex)).unwrap());
|
||||||
assert!(verify_checksum(tmp.path(), &format!("b2:{}", b2_hex)).unwrap());
|
assert!(verify_checksum(tmp.path(), &format!("b2:{}", b2_hex)).unwrap());
|
||||||
assert!(verify_checksum(tmp.path(), &format!("b2sum:{}", b2_hex)).unwrap());
|
assert!(verify_checksum(tmp.path(), &format!("b2sum:{}", b2_hex)).unwrap());
|
||||||
|
|||||||
+24
-2
@@ -279,8 +279,8 @@ fn copy_manual_source_file(
|
|||||||
|
|
||||||
/// Verify a file against an `expected` checksum string.
|
/// Verify a file against an `expected` checksum string.
|
||||||
///
|
///
|
||||||
/// Formats accepted: `sha256:HEX`, `sha512:HEX`, `md5:HEX`, `b2:HEX`,
|
/// Formats accepted: `sha256:HEX`, `sha512:HEX`, `sha1:HEX`, `md5:HEX`,
|
||||||
/// `b2sum:HEX`, or plain `HEX` (assumed sha256).
|
/// `b2:HEX`, `b2sum:HEX`, or plain `HEX` (assumed sha256).
|
||||||
fn verify_file_hash(path: &Path, expected: &str) -> Result<bool> {
|
fn verify_file_hash(path: &Path, expected: &str) -> Result<bool> {
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
@@ -335,6 +335,21 @@ fn verify_file_hash(path: &Path, expected: &str) -> Result<bool> {
|
|||||||
let actual = format!("{:x}", hasher.finalize());
|
let actual = format!("{:x}", hasher.finalize());
|
||||||
Ok(actual == hex)
|
Ok(actual == hex)
|
||||||
}
|
}
|
||||||
|
"sha1" => {
|
||||||
|
use sha1::Sha1;
|
||||||
|
let mut f = fs::File::open(path)?;
|
||||||
|
let mut hasher = Sha1::new();
|
||||||
|
let mut buf = [0u8; 8192];
|
||||||
|
loop {
|
||||||
|
let n = f.read(&mut buf)?;
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hasher.update(&buf[..n]);
|
||||||
|
}
|
||||||
|
let actual = format!("{:x}", hasher.finalize());
|
||||||
|
Ok(actual == hex)
|
||||||
|
}
|
||||||
"md5" => {
|
"md5" => {
|
||||||
let mut ctx = md5::Context::new();
|
let mut ctx = md5::Context::new();
|
||||||
let mut f = fs::File::open(path)?;
|
let mut f = fs::File::open(path)?;
|
||||||
@@ -875,6 +890,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_file_hash_accepts_multiple_algorithms() {
|
fn verify_file_hash_accepts_multiple_algorithms() {
|
||||||
|
use sha1::Sha1;
|
||||||
use sha2::{Digest, Sha256, Sha512};
|
use sha2::{Digest, Sha256, Sha512};
|
||||||
|
|
||||||
let tmp = tempfile::NamedTempFile::new().unwrap();
|
let tmp = tempfile::NamedTempFile::new().unwrap();
|
||||||
@@ -890,6 +906,11 @@ mod tests {
|
|||||||
h.update(b"abc");
|
h.update(b"abc");
|
||||||
format!("{:x}", h.finalize())
|
format!("{:x}", h.finalize())
|
||||||
};
|
};
|
||||||
|
let sha1_hex = {
|
||||||
|
let mut h = Sha1::new();
|
||||||
|
h.update(b"abc");
|
||||||
|
format!("{:x}", h.finalize())
|
||||||
|
};
|
||||||
let md5_hex = format!("{:x}", md5::compute(b"abc"));
|
let md5_hex = format!("{:x}", md5::compute(b"abc"));
|
||||||
let b2_hex = b2sum_rust::Blake2bSum::new(64)
|
let b2_hex = b2sum_rust::Blake2bSum::new(64)
|
||||||
.read(tmp.path())
|
.read(tmp.path())
|
||||||
@@ -898,6 +919,7 @@ mod tests {
|
|||||||
assert!(verify_file_hash(tmp.path(), &sha256_hex).unwrap());
|
assert!(verify_file_hash(tmp.path(), &sha256_hex).unwrap());
|
||||||
assert!(verify_file_hash(tmp.path(), &format!("sha256:{}", sha256_hex)).unwrap());
|
assert!(verify_file_hash(tmp.path(), &format!("sha256:{}", sha256_hex)).unwrap());
|
||||||
assert!(verify_file_hash(tmp.path(), &format!("sha512:{}", sha512_hex)).unwrap());
|
assert!(verify_file_hash(tmp.path(), &format!("sha512:{}", sha512_hex)).unwrap());
|
||||||
|
assert!(verify_file_hash(tmp.path(), &format!("sha1:{}", sha1_hex)).unwrap());
|
||||||
assert!(verify_file_hash(tmp.path(), &format!("md5:{}", md5_hex)).unwrap());
|
assert!(verify_file_hash(tmp.path(), &format!("md5:{}", md5_hex)).unwrap());
|
||||||
assert!(verify_file_hash(tmp.path(), &format!("b2:{}", b2_hex)).unwrap());
|
assert!(verify_file_hash(tmp.path(), &format!("b2:{}", b2_hex)).unwrap());
|
||||||
assert!(verify_file_hash(tmp.path(), &format!("b2sum:{}", b2_hex)).unwrap());
|
assert!(verify_file_hash(tmp.path(), &format!("b2sum:{}", b2_hex)).unwrap());
|
||||||
|
|||||||
Reference in New Issue
Block a user