Merge pull request #2961 from Hamza0101/encrypted-pdf-note

Exports: say that an encrypted PDF still differs, and test the clock fallback
This commit is contained in:
BFlat
2026-10-19 23:12:28 +07:00
committed by GitHub
3 changed files with 31 additions and 5 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+24 -1
View File
@@ -90,7 +90,13 @@ impl DocMetadata {
/// The raw clock stays in [`now_unix`], which is what a heartbeat is judged by and what seeds
/// encryption: neither may be pinned by the environment.
pub fn output_time() -> Option<i64> {
stamp_from_env(|n| std::env::var(n).ok()).or_else(now_unix)
pinned_or(|n| std::env::var(n).ok(), now_unix)
}
/// [`output_time`] with the environment and the clock supplied, so a test can show that the
/// variable is preferred and that the clock answers whenever it does not.
fn pinned_or(get: impl Fn(&str) -> Option<String>, clock: impl Fn() -> Option<i64>) -> Option<i64> {
stamp_from_env(get).or_else(clock)
}
/// `SOURCE_DATE_EPOCH` as Unix seconds: the reproducible-builds variable
@@ -184,4 +190,21 @@ mod tests {
assert_eq!(stamp_from_env(|_| Some("1791208980".to_owned())), Some(1_791_208_980));
assert_eq!(stamp_from_env(|n: &str| (n == "TZ").then(|| "1791208980".to_owned())), None);
}
/// What a file records: the variable when it is usable, the clock whenever it is not. The
/// environment and the clock are passed in, so no test changes this process's environment --
/// that is `unsafe` under edition 2024 and would reach every other test running beside it.
#[test]
fn a_pinned_epoch_is_preferred_and_the_clock_answers_otherwise() {
let env = |v: Option<&'static str>| move |_: &str| v.map(str::to_owned);
let clock = || Some(1_000_000);
assert_eq!(pinned_or(env(Some("1791208980")), clock), Some(1_791_208_980), "the variable wins over the clock");
assert_eq!(pinned_or(env(Some("0")), clock), Some(0), "including the epoch itself");
for ignored in [None, Some(""), Some("-1"), Some("later")] {
assert_eq!(pinned_or(env(ignored), clock), Some(1_000_000), "{ignored:?} leaves it to the clock");
}
// A platform with no clock and no variable has no date to record.
assert_eq!(pinned_or(env(None), || None), None);
assert_eq!(pinned_or(env(Some("1791208980")), || None), Some(1_791_208_980), "the variable needs no clock");
}
}
+6 -3
View File
@@ -836,9 +836,12 @@ SOURCE_DATE_EPOCH=1791208980 vectorcraft-cli run --cmd file.new --params '{}' --
A command that takes its own date (`file.new {created}`, a save's `modified`) still wins over the
variable. A blank, negative or non-numeric value is ignored and the clock decides, so an export is
never refused over the environment. The variable does not reach the recovery heartbeat or the
random seed PDF encryption draws on, which must follow the real clock. SVG output carries no time
and was already reproducible.
never refused over the environment. SVG output carries no time and was already reproducible.
Two things stay on the real clock and are not pinned by the variable: the recovery heartbeat, and
the random seed PDF encryption draws on. **A PDF exported with a password therefore still differs
between runs** whatever `SOURCE_DATE_EPOCH` says, because that seed supplies its AES
initialisation vectors and its file `/ID`. Reproducible output means an unencrypted export.
### Parameter keys