use std::path::Path;
use crate::error::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Mark {
Carried,
Noted,
AlreadyMarked,
Recorded,
Silent,
}
pub fn carry(from: &Path, to: &Path) -> Result<Mark> {
match platform::carry(from, to) {
Err(_) if platform::carries_a_mark(to) => Ok(if platform::note_origin(from, to) {
Mark::Recorded
} else {
Mark::AlreadyMarked
}),
other => Ok(other?),
}
}
#[must_use]
pub fn arrived_from_elsewhere(path: &Path) -> bool {
platform::arrived_from_elsewhere(path)
}
#[cfg(target_os = "macos")]
mod platform {
use super::{Mark, Path};
use std::io;
const QUARANTINE: &str = "com.apple.quarantine";
const ORIGIN_NOTE: &str = "com.excelano.slipcase.origin";
#[cfg(test)]
thread_local! {
pub(super) static DENY_QUARANTINE_WRITE: std::cell::Cell<bool> =
const { std::cell::Cell::new(false) };
}
fn set_quarantine(to: &Path, value: &[u8]) -> io::Result<()> {
#[cfg(test)]
if DENY_QUARANTINE_WRITE.with(std::cell::Cell::get) {
return Err(io::Error::from(io::ErrorKind::PermissionDenied));
}
xattr::set(to, QUARANTINE, value)
}
pub fn carry(from: &Path, to: &Path) -> io::Result<Mark> {
match xattr::get(from, QUARANTINE)? {
Some(value) => {
set_quarantine(to, &value)?;
Ok(Mark::Carried)
}
None => Ok(Mark::Silent),
}
}
pub fn note_origin(from: &Path, to: &Path) -> bool {
let Ok(Some(value)) = xattr::get(from, QUARANTINE) else {
return false;
};
xattr::set(to, ORIGIN_NOTE, &value).is_ok()
}
fn note_of(path: &Path) -> Option<Vec<u8>> {
xattr::get(path, ORIGIN_NOTE).ok().flatten()
}
pub fn carries_a_mark(path: &Path) -> bool {
value_of(path).is_some()
}
pub fn arrived_from_elsewhere(path: &Path) -> bool {
if note_of(path).is_some() {
return true;
}
match value_of(path) {
Some(value) => !this_process_wrote(&value),
None => false,
}
}
fn value_of(path: &Path) -> Option<Vec<u8>> {
xattr::get(path, QUARANTINE).ok().flatten()
}
fn this_process_wrote(value: &[u8]) -> bool {
use std::os::unix::ffi::OsStrExt;
let Some(agent) = value.split(|b| *b == b';').nth(2) else {
return false;
};
let Ok(us) = std::env::current_exe() else {
return false;
};
us.file_name().is_some_and(|name| name.as_bytes() == agent)
}
}
#[cfg(target_os = "windows")]
mod platform {
use super::{Mark, Path};
use std::ffi::OsString;
use std::io;
const ZONE: &str = ":Zone.Identifier";
const SECTION: &[u8] = b"[zonetransfer]";
const ZONE_ID: &[u8] = b"zoneid";
const GATED_FROM: u32 = 3;
fn stream_of(path: &Path) -> OsString {
let mut named = path.as_os_str().to_os_string();
named.push(ZONE);
named
}
pub fn note_origin(_from: &Path, _to: &Path) -> bool {
false
}
pub fn carry(from: &Path, to: &Path) -> io::Result<Mark> {
let zone = match std::fs::read(stream_of(from)) {
Ok(bytes) => bytes,
Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(Mark::Silent),
Err(e) => return Err(e),
};
std::fs::write(stream_of(to), zone)?;
Ok(Mark::Carried)
}
pub fn carries_a_mark(path: &Path) -> bool {
let stream = std::fs::read(stream_of(path)).unwrap_or_default();
zone_id(&stream).is_some_and(|zone| zone >= GATED_FROM)
}
fn zone_id(stream: &[u8]) -> Option<u32> {
let mut reading = false;
let mut zone = None;
for line in stream.split(|b| matches!(b, b'\r' | b'\n')) {
let line = line.trim_ascii();
if line.starts_with(b"[") {
reading = line.eq_ignore_ascii_case(SECTION);
continue;
}
if !reading {
continue;
}
let mut halves = line.splitn(2, |b| *b == b'=');
let (Some(key), Some(value)) = (halves.next(), halves.next()) else {
continue;
};
if key.trim_ascii().eq_ignore_ascii_case(ZONE_ID) {
zone = std::str::from_utf8(value.trim_ascii())
.ok()
.and_then(|value| value.parse().ok());
}
}
zone
}
pub fn arrived_from_elsewhere(path: &Path) -> bool {
std::fs::metadata(stream_of(path)).is_ok()
}
}
#[cfg(target_os = "linux")]
mod platform {
use super::{Mark, Path};
use std::io;
const ORIGIN: [&str; 2] = ["user.xdg.origin.url", "user.xdg.referrer.url"];
#[allow(clippy::unnecessary_wraps)]
pub fn carry(from: &Path, to: &Path) -> io::Result<Mark> {
let mut carried = false;
for name in ORIGIN {
if let Ok(Some(value)) = xattr::get(from, name) {
if xattr::set(to, name, &value).is_ok() {
carried = true;
}
}
}
Ok(if carried { Mark::Noted } else { Mark::Silent })
}
pub fn note_origin(_from: &Path, _to: &Path) -> bool {
false
}
pub fn carries_a_mark(path: &Path) -> bool {
arrived_from_elsewhere(path)
}
pub fn arrived_from_elsewhere(path: &Path) -> bool {
ORIGIN
.iter()
.any(|name| matches!(xattr::get(path, name), Ok(Some(_))))
}
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
mod platform {
use super::{Mark, Path};
use std::io;
#[allow(clippy::unnecessary_wraps)]
pub fn carry(_from: &Path, _to: &Path) -> io::Result<Mark> {
Ok(Mark::Silent)
}
pub fn carries_a_mark(_path: &Path) -> bool {
false
}
pub fn note_origin(_from: &Path, _to: &Path) -> bool {
false
}
pub fn arrived_from_elsewhere(_path: &Path) -> bool {
false
}
}
#[cfg(all(test, target_os = "linux"))]
mod tests {
use super::{carry, Mark};
#[test]
fn a_container_from_nowhere_marks_nothing() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("plain.slpc");
let to = dir.path().join("payload.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
assert_eq!(carry(&from, &to).expect("carrying"), Mark::Silent);
assert!(xattr::get(&to, "user.xdg.origin.url")
.expect("reading")
.is_none());
}
#[test]
fn a_downloaded_container_puts_its_origin_on_the_payload() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("payload.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
xattr::set(
&from,
"user.xdg.origin.url",
b"https://example.invalid/a.slpc",
)
.expect("marking the source");
assert_eq!(carry(&from, &to).expect("carrying"), Mark::Noted);
assert_eq!(
xattr::get(&to, "user.xdg.origin.url").expect("reading"),
Some(b"https://example.invalid/a.slpc".to_vec()),
);
}
#[test]
fn the_referrer_is_carried_as_well_as_the_origin() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("payload.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
xattr::set(
&from,
"user.xdg.origin.url",
b"https://example.invalid/a.slpc",
)
.expect("marking the origin");
xattr::set(
&from,
"user.xdg.referrer.url",
b"https://example.invalid/page",
)
.expect("marking the referrer");
assert_eq!(carry(&from, &to).expect("carrying"), Mark::Noted);
assert_eq!(
xattr::get(&to, "user.xdg.referrer.url").expect("reading"),
Some(b"https://example.invalid/page".to_vec()),
);
}
#[test]
fn an_origin_already_on_the_copy_is_replaced() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("payload.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
xattr::set(&from, "user.xdg.origin.url", b"https://example.invalid/new")
.expect("marking the source");
xattr::set(&to, "user.xdg.origin.url", b"https://example.invalid/stale")
.expect("marking the destination");
carry(&from, &to).expect("carrying");
assert_eq!(
xattr::get(&to, "user.xdg.origin.url").expect("reading"),
Some(b"https://example.invalid/new".to_vec()),
);
}
}
#[cfg(all(test, target_os = "macos"))]
mod macos_tests {
use super::platform::carries_a_mark;
use super::{arrived_from_elsewhere, carry, Mark};
const ORIGIN_NOTE: &str = "com.excelano.slipcase.origin";
fn with_quarantine_denied<T>(f: impl FnOnce() -> T) -> T {
super::platform::DENY_QUARANTINE_WRITE.with(|d| d.set(true));
let out = f();
super::platform::DENY_QUARANTINE_WRITE.with(|d| d.set(false));
out
}
const QUARANTINE: &str = "com.apple.quarantine";
const FROM_SAFARI: &[u8] = b"0083;6a8dbb61;Safari;B8AC643B-5609-41D4-A666-ACC147704C79";
const FROM_US: &[u8] = b"0082;6a8dc724;some-other-application;";
fn unwritable(path: &std::path::Path) {
let mut mode = std::fs::metadata(path).expect("the file").permissions();
std::os::unix::fs::PermissionsExt::set_mode(&mut mode, 0o444);
std::fs::set_permissions(path, mode).expect("making it unwritable");
}
#[test]
fn a_copy_the_platform_marked_first_is_not_a_failure() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("report.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
xattr::set(&from, QUARANTINE, FROM_SAFARI).expect("marking the source");
xattr::set(&to, QUARANTINE, FROM_US).expect("marking the copy");
unwritable(&to);
assert_eq!(
carry(&from, &to).expect("a marked copy is not a failure"),
Mark::AlreadyMarked
);
}
#[test]
fn a_copy_with_no_mark_at_all_is_still_a_failure() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("report.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
xattr::set(&from, QUARANTINE, FROM_SAFARI).expect("marking the source");
unwritable(&to);
assert!(
carry(&from, &to).is_err(),
"an unmarked copy was accepted, which is the laundering this module exists to prevent"
);
}
fn our_own_mark() -> Vec<u8> {
use std::os::unix::ffi::OsStrExt;
let us = std::env::current_exe().expect("this process has a path");
let mut value = b"0082;6a8dc724;".to_vec();
value.extend_from_slice(us.file_name().expect("and a filename").as_bytes());
value.push(b';');
value
}
#[test]
fn a_mark_this_process_wrote_is_not_provenance() {
let dir = tempfile::tempdir().expect("a temporary directory");
let saved = dir.path().join("saved-here.slpc");
std::fs::write(&saved, b"container").expect("the container");
xattr::set(&saved, QUARANTINE, &our_own_mark()).expect("marking it as we would");
assert!(
!super::arrived_from_elsewhere(&saved),
"a container the calling process saved is being reported as downloaded"
);
}
#[test]
fn a_mark_anything_else_wrote_still_is() {
let dir = tempfile::tempdir().expect("a temporary directory");
let downloaded = dir.path().join("downloaded.slpc");
std::fs::write(&downloaded, b"container").expect("the container");
xattr::set(&downloaded, QUARANTINE, FROM_SAFARI).expect("marking the source");
assert!(super::arrived_from_elsewhere(&downloaded));
}
#[test]
fn a_mark_that_cannot_be_read_is_reported() {
let dir = tempfile::tempdir().expect("a temporary directory");
let odd = dir.path().join("odd.slpc");
std::fs::write(&odd, b"container").expect("the container");
xattr::set(&odd, QUARANTINE, b"0082").expect("marking it oddly");
assert!(super::arrived_from_elsewhere(&odd));
}
#[test]
fn a_copy_this_process_marked_still_counts_as_gated() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("report.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
xattr::set(&from, QUARANTINE, FROM_SAFARI).expect("marking the source");
xattr::set(&to, QUARANTINE, &our_own_mark()).expect("as the platform would");
unwritable(&to);
assert_eq!(
carry(&from, &to).expect("a marked copy is not a failure"),
Mark::AlreadyMarked
);
assert!(
!super::arrived_from_elsewhere(&to),
"and the same file does not claim to have come from anywhere"
);
}
#[test]
fn a_container_from_nowhere_marks_nothing() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("plain.slpc");
let to = dir.path().join("report.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
assert_eq!(carry(&from, &to).expect("carrying"), Mark::Silent);
assert!(xattr::get(&to, QUARANTINE).expect("reading").is_none());
}
#[test]
fn a_downloaded_container_puts_its_quarantine_on_the_payload() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("report.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
xattr::set(&from, QUARANTINE, FROM_SAFARI).expect("marking the source");
assert_eq!(carry(&from, &to).expect("carrying"), Mark::Carried);
assert_eq!(
xattr::get(&to, QUARANTINE).expect("reading"),
Some(FROM_SAFARI.to_vec()),
"the copy does not carry the value the container carried"
);
}
#[test]
fn a_mark_that_cannot_be_carried_is_recorded_beside_the_one_that_stands() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("rewritten.slpc");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"rewrite").expect("the rewrite");
xattr::set(&from, QUARANTINE, FROM_SAFARI).expect("marking the source");
xattr::set(&to, QUARANTINE, FROM_US).expect("the platform marking the copy");
assert_eq!(
with_quarantine_denied(|| carry(&from, &to)).expect("not a failure"),
Mark::Recorded
);
assert_eq!(
xattr::get(&to, ORIGIN_NOTE).expect("reading the note"),
Some(FROM_SAFARI.to_vec()),
"the source's own value, verbatim, agent and event identifier included"
);
assert!(
arrived_from_elsewhere(&to),
"the whole point: the copy still says where it came from"
);
}
#[test]
fn a_note_answers_where_it_came_from_and_never_whether_it_is_gated() {
let dir = tempfile::tempdir().expect("a temporary directory");
let noted = dir.path().join("noted.slpc");
std::fs::write(¬ed, b"container").expect("the container");
xattr::set(¬ed, ORIGIN_NOTE, FROM_SAFARI).expect("noting the origin");
assert!(arrived_from_elsewhere(¬ed), "the card's question");
assert!(!carries_a_mark(¬ed), "the gate's question");
}
#[test]
fn nothing_is_recorded_when_the_source_said_nothing() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("local.slpc");
let to = dir.path().join("rewritten.slpc");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"rewrite").expect("the rewrite");
xattr::set(&to, QUARANTINE, FROM_US).expect("the platform marking the copy");
assert_eq!(
with_quarantine_denied(|| carry(&from, &to)).expect("not a failure"),
Mark::Silent
);
assert_eq!(xattr::get(&to, ORIGIN_NOTE).expect("reading"), None);
}
#[test]
fn a_note_already_on_the_copy_is_not_left_to_go_stale() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("rewritten.slpc");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"rewrite").expect("the rewrite");
xattr::set(&from, QUARANTINE, FROM_SAFARI).expect("marking the source");
xattr::set(&to, QUARANTINE, FROM_US).expect("the platform marking the copy");
xattr::set(&to, ORIGIN_NOTE, b"0083;1;SomethingElse;stale").expect("a stale note");
with_quarantine_denied(|| carry(&from, &to)).expect("not a failure");
assert_eq!(
xattr::get(&to, ORIGIN_NOTE).expect("reading the note"),
Some(FROM_SAFARI.to_vec()),
"replaced rather than left saying where some earlier file came from"
);
}
}
#[cfg(all(test, target_os = "windows"))]
mod windows_tests {
use super::{carry, Mark};
use std::path::Path;
const FROM_THE_INTERNET: &[u8] =
b"[ZoneTransfer]\r\nZoneId=3\r\nHostUrl=https://example.invalid/a.slpc\r\n";
const A_WRITE_THAT_FAILED_PARTWAY: &[u8] = b"[ZoneTransfer]\r\n";
fn stream_of(path: &Path) -> std::ffi::OsString {
let mut named = path.as_os_str().to_os_string();
named.push(":Zone.Identifier");
named
}
fn mark(path: &Path, zone: &[u8]) {
std::fs::write(stream_of(path), zone).expect("marking");
}
fn zone_on(path: &Path) -> Option<Vec<u8>> {
std::fs::read(stream_of(path)).ok()
}
fn unwritable(path: &Path) {
let mut mode = std::fs::metadata(path).expect("the file").permissions();
mode.set_readonly(true);
std::fs::set_permissions(path, mode).expect("making it unwritable");
}
#[allow(clippy::permissions_set_readonly_false)]
fn writable(path: &Path) {
let mut mode = std::fs::metadata(path).expect("the file").permissions();
mode.set_readonly(false);
std::fs::set_permissions(path, mode).expect("putting it back");
}
#[test]
fn a_stream_that_does_not_gate_is_not_an_excuse_for_a_failed_write() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("report.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
mark(&from, FROM_THE_INTERNET);
mark(&to, A_WRITE_THAT_FAILED_PARTWAY);
unwritable(&to);
let outcome = carry(&from, &to);
writable(&to);
assert!(
outcome.is_err(),
"a copy the shell will not gate was accepted as already marked, \
which is the laundering this module exists to prevent"
);
}
#[test]
fn a_copy_the_shell_would_gate_is_not_a_failure() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("report.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
mark(&from, FROM_THE_INTERNET);
mark(&to, b"[ZoneTransfer]\r\nZoneId=4\r\n");
unwritable(&to);
let outcome = carry(&from, &to);
writable(&to);
assert_eq!(
outcome.expect("a gated copy is not a failure"),
Mark::AlreadyMarked
);
}
#[test]
fn only_the_zones_the_shell_gates_count_as_a_mark() {
let dir = tempfile::tempdir().expect("a temporary directory");
for (zone, gates) in [(0, false), (1, false), (2, false), (3, true), (4, true)] {
let file = dir.path().join(format!("zone-{zone}.pdf"));
std::fs::write(&file, b"payload").expect("the payload");
mark(
&file,
format!("[ZoneTransfer]\r\nZoneId={zone}\r\n").as_bytes(),
);
assert_eq!(
super::platform::carries_a_mark(&file),
gates,
"zone {zone} was not read as the measurement says the shell reads it"
);
}
}
#[test]
fn a_stream_that_gates_nothing_is_still_reported_as_provenance() {
let dir = tempfile::tempdir().expect("a temporary directory");
let odd = dir.path().join("odd.slpc");
std::fs::write(&odd, b"container").expect("the container");
mark(&odd, A_WRITE_THAT_FAILED_PARTWAY);
assert!(super::arrived_from_elsewhere(&odd));
}
#[test]
fn a_container_from_nowhere_marks_nothing() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("plain.slpc");
let to = dir.path().join("report.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
assert_eq!(carry(&from, &to).expect("carrying"), Mark::Silent);
assert!(zone_on(&to).is_none());
}
#[test]
fn a_downloaded_container_puts_its_zone_on_the_payload() {
let dir = tempfile::tempdir().expect("a temporary directory");
let from = dir.path().join("downloaded.slpc");
let to = dir.path().join("report.pdf");
std::fs::write(&from, b"container").expect("the container");
std::fs::write(&to, b"payload").expect("the payload");
mark(&from, FROM_THE_INTERNET);
assert_eq!(carry(&from, &to).expect("carrying"), Mark::Carried);
assert_eq!(
zone_on(&to).as_deref(),
Some(FROM_THE_INTERNET),
"the copy does not carry the zone the container carried"
);
}
}