Skip to content

Commit c65555c

Browse files
fix(stamps): an empty KAB_KEYSET_FILE must not digest the filesystem root
`KAB_KEYSET_FILE` set but empty reached `path_content_digest(Path::new("/"), "")`, which digested `/` itself. The stamp then depended on the entire filesystem: it would never match twice, and the cost of computing it was unbounded. Set-but- empty is now an error, which is what it always was in intent.
1 parent 413801e commit c65555c

1 file changed

Lines changed: 22 additions & 8 deletions

File tree

src/utils/stamps.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ pub struct StampOutputs {
148148
/// whatever input caused it, including inputs the host cannot see.
149149
#[serde(skip_serializing_if = "Option::is_none")]
150150
pub content_hash: Option<String>,
151+
/// Shell variables the step exported for later sections of the same build
152+
/// script, recorded so a skipped re-run can replay them.
153+
#[serde(skip_serializing_if = "Option::is_none")]
154+
pub exports: Option<std::collections::BTreeMap<String, String>>,
151155
}
152156

153157
/// A stamp representing successful completion of a command
@@ -1708,13 +1712,22 @@ pub fn compute_ext_image_input_hash(
17081712
== Some("kab");
17091713
if is_kab {
17101714
if let Ok(keyset) = std::env::var("KAB_KEYSET_FILE") {
1711-
let digest = crate::utils::overlay_preprocess::path_content_digest(
1712-
Path::new("/"),
1713-
keyset.trim_start_matches('/'),
1714-
)?
1715-
.ok_or_else(|| {
1716-
anyhow::anyhow!("KAB_KEYSET_FILE is set but `{keyset}` does not exist")
1717-
})?;
1715+
// Empty is an error, not "unset". `path_content_digest` would be
1716+
// handed an empty relative path and digest `/` itself, so the
1717+
// stamp would silently depend on the whole filesystem and read as
1718+
// a rotated key on any unrelated change. Same treatment as a
1719+
// declared file that does not exist.
1720+
let rel = keyset.trim().trim_start_matches('/');
1721+
if rel.is_empty() {
1722+
anyhow::bail!(
1723+
"KAB_KEYSET_FILE is set but empty — unset it, or point it at the keyset file"
1724+
);
1725+
}
1726+
let digest =
1727+
crate::utils::overlay_preprocess::path_content_digest(Path::new("/"), rel)?
1728+
.ok_or_else(|| {
1729+
anyhow::anyhow!("KAB_KEYSET_FILE is set but `{keyset}` does not exist")
1730+
})?;
17181731
hash_data.insert(key("kab_keyset"), serde_yaml::Value::String(digest));
17191732
}
17201733
}
@@ -2630,7 +2643,8 @@ mkdir -p "$(dirname "$_avocado_stamp")" || exit 1
26302643
cat > "$_avocado_stamp" << 'STAMP_EOF' || exit 1
26312644
{stamp_json}
26322645
STAMP_EOF
2633-
sed -i "s/\"{placeholder}\"/\"sha256:$AVOCADO_CONTENT_HASH\"/" "$AVOCADO_PREFIX/.stamps/{stamp_path}"
2646+
grep -q '"content_hash": "{placeholder}"' "$_avocado_stamp" || {{ echo "ERROR: {stamp_path} has no content_hash placeholder to substitute" >&2; exit 1; }}
2647+
sed -i 's|"content_hash": "{placeholder}"|"content_hash": "sha256:'"$AVOCADO_CONTENT_HASH"'"|' "$_avocado_stamp" || exit 1
26342648
"#,
26352649
placeholder = CONTENT_HASH_PLACEHOLDER,
26362650
))

0 commit comments

Comments
 (0)