fix(tvix/cli): handle SRI hashes in outputHash

Instead of being called with `md5`, `sha1`, `sha256` or `sha512`,
`fetchurl.nix` (from corepkgs / `<nix`) can also be called with a `hash`
attribute, being an SRI hash.

In that case, `builtin.derivation` is called with `outputHashAlgo` being
an empty string, and `outputHash` being an SRI hash string.

In other cases, an SRI hash is passed as outputHash, but outputHashAlgo
is set too.

Nix does modify these values in (single, fixed) output specification it
serializes to ATerm, but keeps it unharmed in `env`.

Move this into a construct_output_hash helper function, that can be
tested better in isolation.

Change-Id: Id9d716a119664c44ea7747540399966752e20187
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7933
Reviewed-by: tazjin <tazjin@tvl.su>
Autosubmit: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
This commit is contained in:
Florian Klink 2023-01-26 14:18:12 +01:00 committed by clbot
parent bda5fc58d0
commit a94a1434cc
5 changed files with 573 additions and 38 deletions

129
tvix/Cargo.lock generated
View file

@ -171,6 +171,15 @@ dependencies = [
"nix 0.23.1",
]
[[package]]
name = "base64"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
dependencies = [
"byteorder",
]
[[package]]
name = "base64"
version = "0.13.1"
@ -215,17 +224,38 @@ dependencies = [
"cc",
"cfg-if",
"constant_time_eq",
"digest",
"digest 0.10.6",
"rayon",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array 0.12.4",
]
[[package]]
name = "block-buffer"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
dependencies = [
"generic-array",
"generic-array 0.14.6",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
@ -234,6 +264,12 @@ version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -495,7 +531,7 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"generic-array 0.14.6",
"typenum",
]
@ -521,13 +557,22 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array 0.12.4",
]
[[package]]
name = "digest"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"block-buffer 0.10.3",
"crypto-common",
"subtle",
]
@ -616,6 +661,12 @@ dependencies = [
"str-buf",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fastcdc"
version = "2.0.0"
@ -768,6 +819,15 @@ dependencies = [
"byteorder",
]
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.14.6"
@ -856,6 +916,12 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
[[package]]
name = "http"
version = "0.2.8"
@ -1294,6 +1360,12 @@ version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
@ -1911,6 +1983,30 @@ dependencies = [
"serde",
]
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "sha2"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "sha2"
version = "0.10.6"
@ -1919,7 +2015,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
"digest 0.10.6",
]
[[package]]
@ -1982,6 +2078,21 @@ dependencies = [
"winapi",
]
[[package]]
name = "ssri"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9cec0d388f39fbe79d7aa600e8d38053bf97b1bc8d350da7c0ba800d0f423f2"
dependencies = [
"base64 0.10.1",
"digest 0.8.1",
"hex",
"serde",
"sha-1",
"sha2 0.8.2",
"thiserror",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
@ -2302,7 +2413,7 @@ dependencies = [
"async-stream",
"async-trait",
"axum",
"base64",
"base64 0.13.1",
"bytes",
"futures-core",
"futures-util",
@ -2498,11 +2609,15 @@ version = "0.1.0"
dependencies = [
"aho-corasick",
"clap 4.0.32",
"data-encoding",
"dirs",
"rustyline",
"smol_str",
"ssri",
"test-case",
"tvix-derivation",
"tvix-eval",
"tvix-store-bin",
]
[[package]]
@ -2514,7 +2629,7 @@ dependencies = [
"glob",
"serde",
"serde_json",
"sha2",
"sha2 0.10.6",
"test-case",
"test-generator",
"thiserror",

View file

@ -635,7 +635,24 @@ rec {
];
};
"base64" = rec {
"base64 0.10.1" = rec {
crateName = "base64";
version = "0.10.1";
edition = "2015";
sha256 = "13k6bvd3n6dm7jqn9x918w65dd9xhx454bqphbnv0bkd6n9dj98b";
authors = [
"Alice Maz <alice@alicemaz.com>"
"Marshall Pierce <marshall@mpierce.org>"
];
dependencies = [
{
name = "byteorder";
packageId = "byteorder";
}
];
};
"base64 0.13.1" = rec {
crateName = "base64";
version = "0.13.1";
edition = "2018";
@ -743,7 +760,7 @@ rec {
}
{
name = "digest";
packageId = "digest";
packageId = "digest 0.10.6";
optional = true;
features = [ "mac" ];
}
@ -768,7 +785,7 @@ rec {
};
resolvedDefaultFeatures = [ "default" "digest" "rayon" "std" ];
};
"block-buffer" = rec {
"block-buffer 0.10.3" = rec {
crateName = "block-buffer";
version = "0.10.3";
edition = "2018";
@ -779,7 +796,52 @@ rec {
dependencies = [
{
name = "generic-array";
packageId = "generic-array";
packageId = "generic-array 0.14.6";
}
];
};
"block-buffer 0.7.3" = rec {
crateName = "block-buffer";
version = "0.7.3";
edition = "2015";
sha256 = "12v8wizynqin0hqf140kmp9s38q223mp1b0hkqk8j5pk8720v560";
authors = [
"RustCrypto Developers"
];
dependencies = [
{
name = "block-padding";
packageId = "block-padding";
}
{
name = "byte-tools";
packageId = "byte-tools";
}
{
name = "byteorder";
packageId = "byteorder";
usesDefaultFeatures = false;
}
{
name = "generic-array";
packageId = "generic-array 0.12.4";
}
];
};
"block-padding" = rec {
crateName = "block-padding";
version = "0.1.5";
edition = "2015";
sha256 = "1xbkmysiz23vimd17rnsjpw9bgjxipwfslwyygqlkx4in3dxwygs";
authors = [
"RustCrypto Developers"
];
dependencies = [
{
name = "byte-tools";
packageId = "byte-tools";
}
];
@ -795,6 +857,16 @@ rec {
features = { };
resolvedDefaultFeatures = [ "default" ];
};
"byte-tools" = rec {
crateName = "byte-tools";
version = "0.3.1";
edition = "2015";
sha256 = "1mqi29wsm8njpl51pfwr31wmpzs5ahlcb40wsjyd92l90ixcmdg3";
authors = [
"RustCrypto Developers"
];
};
"byteorder" = rec {
crateName = "byteorder";
version = "1.4.3";
@ -1503,7 +1575,7 @@ rec {
dependencies = [
{
name = "generic-array";
packageId = "generic-array";
packageId = "generic-array 0.14.6";
features = [ "more_lengths" ];
}
{
@ -1564,7 +1636,7 @@ rec {
];
};
"digest" = rec {
"digest 0.10.6" = rec {
crateName = "digest";
version = "0.10.6";
edition = "2018";
@ -1575,7 +1647,7 @@ rec {
dependencies = [
{
name = "block-buffer";
packageId = "block-buffer";
packageId = "block-buffer 0.10.3";
optional = true;
}
{
@ -1604,6 +1676,26 @@ rec {
};
resolvedDefaultFeatures = [ "alloc" "block-buffer" "core-api" "default" "mac" "std" "subtle" ];
};
"digest 0.8.1" = rec {
crateName = "digest";
version = "0.8.1";
edition = "2015";
sha256 = "1madjl27f3kj5ql7kwgvb9c8b7yb7bv7yfgx7rqzj4i3fp4cil7k";
authors = [
"RustCrypto Developers"
];
dependencies = [
{
name = "generic-array";
packageId = "generic-array 0.12.4";
}
];
features = {
"blobby" = [ "dep:blobby" ];
"dev" = [ "blobby" ];
};
resolvedDefaultFeatures = [ "std" ];
};
"dirs" = rec {
crateName = "dirs";
version = "4.0.0";
@ -1806,6 +1898,16 @@ rec {
];
features = { };
};
"fake-simd" = rec {
crateName = "fake-simd";
version = "0.1.2";
edition = "2015";
sha256 = "1vfylvk4va2ivqx85603lyqqp0zk52cgbs4n5nfbbbqx577qm2p8";
authors = [
"The Rust-Crypto Project Developers"
];
};
"fastcdc" = rec {
crateName = "fastcdc";
version = "2.0.0";
@ -2214,7 +2316,27 @@ rec {
];
};
"generic-array" = rec {
"generic-array 0.12.4" = rec {
crateName = "generic-array";
version = "0.12.4";
edition = "2015";
sha256 = "1gfpay78vijl9vrwl1k9v7fbvbhkhcmnrk4kfg9l6x24y4s9zpzz";
libName = "generic_array";
authors = [
"Bartłomiej Kamiński <fizyk20@gmail.com>"
"Aaron Trent <novacrazy@gmail.com>"
];
dependencies = [
{
name = "typenum";
packageId = "typenum";
}
];
features = {
"serde" = [ "dep:serde" ];
};
};
"generic-array 0.14.6" = rec {
crateName = "generic-array";
version = "0.14.6";
edition = "2015";
@ -2478,6 +2600,16 @@ rec {
};
resolvedDefaultFeatures = [ "default" ];
};
"hex" = rec {
crateName = "hex";
version = "0.3.2";
edition = "2015";
sha256 = "0xsdcjiik5j750j67zk42qdnmm4ahirk3gmkmcqgq7qls2jjcl40";
authors = [
"KokaKiwi <kokakiwi@kokakiwi.net>"
];
features = { };
};
"http" = rec {
crateName = "http";
version = "0.2.8";
@ -3748,6 +3880,16 @@ rec {
"Simon Heath <icefox@dreamquest.io>"
];
};
"opaque-debug" = rec {
crateName = "opaque-debug";
version = "0.2.3";
edition = "2015";
sha256 = "172j6bs8ndclqxa2m64qc0y1772rr73g4l9fg2svscgicnbfff98";
authors = [
"RustCrypto Developers"
];
};
"os_str_bytes" = rec {
crateName = "os_str_bytes";
@ -5514,7 +5656,51 @@ rec {
};
resolvedDefaultFeatures = [ "serde" ];
};
"sha2" = rec {
"sha-1" = rec {
crateName = "sha-1";
version = "0.8.2";
edition = "2015";
sha256 = "1pv387q0r7llk2cqzyq0nivzvkgqgzsiygqzlv7b68z9xl5lvngp";
libName = "sha1";
authors = [
"RustCrypto Developers"
];
dependencies = [
{
name = "block-buffer";
packageId = "block-buffer 0.7.3";
}
{
name = "digest";
packageId = "digest 0.8.1";
}
{
name = "fake-simd";
packageId = "fake-simd";
}
{
name = "opaque-debug";
packageId = "opaque-debug";
}
];
devDependencies = [
{
name = "digest";
packageId = "digest 0.8.1";
features = [ "dev" ];
}
];
features = {
"asm" = [ "sha1-asm" ];
"asm-aarch64" = [ "asm" "libc" ];
"default" = [ "std" ];
"libc" = [ "dep:libc" ];
"sha1-asm" = [ "dep:sha1-asm" ];
"std" = [ "digest/std" ];
};
resolvedDefaultFeatures = [ "default" "std" ];
};
"sha2 0.10.6" = rec {
crateName = "sha2";
version = "0.10.6";
edition = "2018";
@ -5534,13 +5720,13 @@ rec {
}
{
name = "digest";
packageId = "digest";
packageId = "digest 0.10.6";
}
];
devDependencies = [
{
name = "digest";
packageId = "digest";
packageId = "digest 0.10.6";
features = [ "dev" ];
}
];
@ -5554,6 +5740,49 @@ rec {
};
resolvedDefaultFeatures = [ "default" "std" ];
};
"sha2 0.8.2" = rec {
crateName = "sha2";
version = "0.8.2";
edition = "2015";
sha256 = "0s9yddvyg6anaikdl86wmwfim25c0d4m0xq0y2ghs34alxpg8mm2";
authors = [
"RustCrypto Developers"
];
dependencies = [
{
name = "block-buffer";
packageId = "block-buffer 0.7.3";
}
{
name = "digest";
packageId = "digest 0.8.1";
}
{
name = "fake-simd";
packageId = "fake-simd";
}
{
name = "opaque-debug";
packageId = "opaque-debug";
}
];
devDependencies = [
{
name = "digest";
packageId = "digest 0.8.1";
features = [ "dev" ];
}
];
features = {
"asm" = [ "sha2-asm" ];
"asm-aarch64" = [ "asm" "libc" ];
"default" = [ "std" ];
"libc" = [ "dep:libc" ];
"sha2-asm" = [ "dep:sha2-asm" ];
"std" = [ "digest/std" ];
};
resolvedDefaultFeatures = [ "default" "std" ];
};
"sharded-slab" = rec {
crateName = "sharded-slab";
version = "0.1.4";
@ -5730,6 +5959,51 @@ rec {
features = { };
resolvedDefaultFeatures = [ "all" ];
};
"ssri" = rec {
crateName = "ssri";
version = "7.0.0";
edition = "2018";
sha256 = "1wi3yk801a0bgkd51ly83dxzjfq5726hwq5asxwvx7zki39w1km9";
authors = [
"Kat Marchán <kzm@zkat.tech>"
];
dependencies = [
{
name = "base64";
packageId = "base64 0.10.1";
}
{
name = "digest";
packageId = "digest 0.8.1";
}
{
name = "hex";
packageId = "hex";
}
{
name = "serde";
packageId = "serde";
optional = true;
}
{
name = "sha-1";
packageId = "sha-1";
}
{
name = "sha2";
packageId = "sha2 0.8.2";
}
{
name = "thiserror";
packageId = "thiserror";
}
];
features = {
"default" = [ "serde" ];
"serde" = [ "dep:serde" ];
};
resolvedDefaultFeatures = [ "default" "serde" ];
};
"static_assertions" = rec {
crateName = "static_assertions";
version = "1.1.0";
@ -6635,7 +6909,7 @@ rec {
}
{
name = "base64";
packageId = "base64";
packageId = "base64 0.13.1";
}
{
name = "bytes";
@ -7439,6 +7713,10 @@ rec {
packageId = "clap 4.0.32";
features = [ "derive" "env" ];
}
{
name = "data-encoding";
packageId = "data-encoding";
}
{
name = "dirs";
packageId = "dirs";
@ -7451,6 +7729,10 @@ rec {
name = "smol_str";
packageId = "smol_str";
}
{
name = "ssri";
packageId = "ssri";
}
{
name = "tvix-derivation";
packageId = "tvix-derivation";
@ -7459,6 +7741,16 @@ rec {
name = "tvix-eval";
packageId = "tvix-eval";
}
{
name = "tvix-store-bin";
packageId = "tvix-store-bin";
}
];
devDependencies = [
{
name = "test-case";
packageId = "test-case";
}
];
};
@ -7492,7 +7784,7 @@ rec {
}
{
name = "sha2";
packageId = "sha2";
packageId = "sha2 0.10.6";
}
{
name = "thiserror";

View file

@ -12,6 +12,12 @@ tvix-eval = { path = "../eval" }
tvix-derivation = { path = "../derivation" }
rustyline = "10.0.0"
clap = { version = "4.0", features = ["derive", "env"] }
tvix-store-bin = { path = "../store" }
dirs = "4.0.0"
smol_str = "0.1"
aho-corasick = "0.7"
ssri = "7.0.0"
data-encoding = "2.3.3"
[dev-dependencies]
test-case = "2.2.2"

View file

@ -1,5 +1,6 @@
//! Implements `builtins.derivation`, the core of what makes Nix build packages.
use data_encoding::BASE64;
use std::cell::RefCell;
use std::collections::{btree_map, BTreeSet};
use std::rc::Rc;
@ -79,6 +80,82 @@ fn populate_inputs<I: IntoIterator<Item = String>>(
}
}
/// Due to the support for SRI hashes, and how these are passed along to
/// builtins.derivation, outputHash and outputHashAlgo can have values which
/// need to be further modified before constructing the Derivation struct.
///
/// If outputHashAlgo is an SRI hash, outputHashAlgo must either be an empty
/// string, or the hash algorithm as specified in the (single) SRI (entry).
/// SRI strings with multiple hash algorithms are not supported.
///
/// In case an SRI string was used, the (single) fixed output is populated
/// with the hash algo name, and the hash digest is populated with the
/// (lowercase) hex encoding of the digest.
///
/// These values are only rewritten for the outputs, not what's passed to env.
fn construct_output_hash(digest: &str, algo: &str, hash_mode: Option<&str>) -> Result<Hash, Error> {
let sri_parsed = digest.parse::<ssri::Integrity>();
// SRI strings can embed multiple hashes with different algos, but that's probably not supported
let (digest, algo): (String, String) = match sri_parsed {
Err(e) => {
// unable to parse as SRI, but algo not set
if algo.is_empty() {
// InvalidSRIString doesn't implement PartialEq, but our error does
return Err(Error::InvalidSRIString(e.to_string()));
}
// algo is set. Assume the digest is set to some nixbase32.
// TODO: more validation here
(digest.to_string(), algo.to_string())
}
Ok(sri_parsed) => {
// We don't support more than one SRI hash
if sri_parsed.hashes.len() != 1 {
return Err(Error::UnsupportedSRIMultiple(sri_parsed.hashes.len()).into());
}
// grab the first (and only hash)
let sri_parsed_hash = &sri_parsed.hashes[0];
// ensure the algorithm in the SRI is supported
if !(sri_parsed_hash.algorithm == ssri::Algorithm::Sha1
|| sri_parsed_hash.algorithm == ssri::Algorithm::Sha256
|| sri_parsed_hash.algorithm == ssri::Algorithm::Sha512)
{
Error::UnsupportedSRIAlgo(sri_parsed_hash.algorithm.to_string());
}
// if algo is set, it needs to match what the SRI says
if !algo.is_empty() && algo != sri_parsed_hash.algorithm.to_string() {
return Err(Error::ConflictingSRIHashAlgo(
algo.to_string(),
sri_parsed_hash.algorithm.to_string(),
));
}
// the digest comes base64-encoded. We need to decode, and re-encode as hexlower.
match BASE64.decode(sri_parsed_hash.digest.as_bytes()) {
Err(e) => return Err(Error::InvalidSRIDigest(e).into()),
Ok(sri_digest) => (
data_encoding::HEXLOWER.encode(&sri_digest),
sri_parsed_hash.algorithm.to_string(),
),
}
}
};
// mutate the algo string a bit more, depending on hashMode
let algo = match hash_mode {
None | Some("flat") => algo,
Some("recursive") => format!("r:{}", algo),
Some(other) => return Err(Error::InvalidOutputHashMode(other.to_string()).into()),
};
Ok(Hash { algo, digest })
}
/// Populate the output configuration of a derivation based on the
/// parameters passed to the call, flipping the required
/// parameters for a fixed-output derivation if necessary.
@ -102,6 +179,12 @@ fn populate_output_configuration(
.as_str()
.to_string();
let digest_str = hash
.force(vm)?
.coerce_to_string(CoercionKind::Strong, vm)?
.as_str()
.to_string();
let hash_mode = match hash_mode {
None => None,
Some(mode) => Some(
@ -112,23 +195,12 @@ fn populate_output_configuration(
),
};
let algo = match hash_mode.as_deref() {
None | Some("flat") => algo,
Some("recursive") => format!("r:{}", algo),
Some(other) => {
return Err(Error::InvalidOutputHashMode(other.to_string()).into())
}
};
out.hash = Some(Hash {
algo,
digest: hash
.force(vm)?
.coerce_to_string(CoercionKind::Strong, vm)?
.as_str()
.to_string(),
});
// construct out.hash
out.hash = Some(construct_output_hash(
&digest_str,
&algo,
hash_mode.as_deref(),
)?);
}
},
@ -371,6 +443,7 @@ pub use derivation_builtins::builtins as derivation_builtins;
#[cfg(test)]
mod tests {
use super::*;
use test_case::test_case;
use tvix_eval::observer::NoOpObserver;
static mut OBSERVER: NoOpObserver = NoOpObserver {};
@ -576,4 +649,23 @@ mod tests {
vec!["--foo".to_string(), "42".to_string(), "--bar".to_string()]
);
}
#[test_case(
"sha256-swapHA/ZO8QoDPwumMt6s5gf91oYe+oyk4EfRSyJqMg=", "sha256", Some("flat"),
Ok(Hash { algo: "sha256".to_string(), digest: "b306a91c0fd93bc4280cfc2e98cb7ab3981ff75a187bea3293811f452c89a8c8".to_string() });
"sha256 and SRI"
)]
#[test_case(
"sha256-s6JN6XqP28g1uYMxaVAQMLiXcDG8tUs7OsE3QPhGqzA=", "", Some("flat"),
Ok(Hash { algo: "sha256".to_string(), digest: "b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30".to_string() });
"SRI only"
)]
fn test_construct_output_hash(
digest: &str,
algo: &str,
hash_mode: Option<&str>,
result: Result<Hash, Error>,
) {
assert_eq!(construct_output_hash(digest, algo, hash_mode), result);
}
}

View file

@ -1,7 +1,7 @@
use std::{error, fmt::Display, rc::Rc};
use tvix_derivation::DerivationError;
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum Error {
// Errors related to derivation construction
DuplicateOutput(String),
@ -10,6 +10,11 @@ pub enum Error {
ShadowedOutput(String),
InvalidDerivation(DerivationError),
InvalidOutputHashMode(String),
UnsupportedSRIAlgo(String),
UnsupportedSRIMultiple(usize),
InvalidSRIDigest(data_encoding::DecodeError),
InvalidSRIString(String),
ConflictingSRIHashAlgo(String, String),
}
impl Display for Error {
@ -38,6 +43,31 @@ impl Display for Error {
f,
"invalid output hash mode: '{mode}', only 'recursive' and 'flat` are supported"
),
Error::UnsupportedSRIAlgo(algo) => {
write!(
f,
"unsupported sri algorithm: {algo}, only sha1, sha256 or sha512 is supported"
)
}
Error::UnsupportedSRIMultiple(n) => {
write!(
f,
"invalid number of sri hashes in string ({n}), only one hash is supported"
)
}
Error::InvalidSRIDigest(err) => {
write!(f, "invalid sri digest: {}", err)
}
Error::InvalidSRIString(err) => {
write!(f, "failed to parse SRI string: {}", err)
}
Error::ConflictingSRIHashAlgo(algo, sri_algo) => {
write!(
f,
"outputHashAlgo is set to {}, but outputHash contains SRI with algo {}",
algo, sri_algo
)
}
}
}
}