Skip to content

Commit f37ff68

Browse files
pmaxhoganclaude
andauthored
fix(core): exempt Zone.Identifier from the ads_skipped warning (#288)
`local.ads_skipped` fires for every file carrying a named NTFS stream, because named streams are not backed up. But the overwhelmingly common named stream is `Zone.Identifier` - the mark-of-the-web tag Windows attaches to every downloaded file. It is transient browser provenance metadata, not user data, and on a Documents tree full of downloads it produced ~3,500 warnings per scan (14,098 of the 14,174 lines in the 2026-08-14 incident-day log), burying the useful signal. This exempts it at the detection probe: a file whose **only** named stream is `Zone.Identifier` is no longer flagged; any other named stream still warns (with or without a Zone.Identifier alongside - the exemption cannot mask real data). The check is free: `FindFirstStreamW`/`FindNextStreamW` already return the stream names in the enumeration the probe runs; this adds one ASCII case-insensitive comparison, zero extra syscalls. The chaos `ads-alternate-data-stream` scenario (`foo.txt:hidden`) is unaffected and still asserts the warning for real streams. New Windows-only unit test covers: no-stream and Zone-only files don't flag, the exemption is case-insensitive (NTFS stream names are), a real named stream flags, and Zone.Identifier next to another stream still flags. Skips gracefully on a non-NTFS temp volume. README checked - no changes needed (it does not enumerate per-file warnings). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01A7q3CvJzL4zZmDA9CbXyQQ Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent a377492 commit f37ff68

1 file changed

Lines changed: 91 additions & 7 deletions

File tree

crates/driven-core/src/scanner.rs

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,26 @@ fn should_skip_placeholder(file_attributes: u32, policy: PlaceholderPolicy) -> b
107107
&& file_attributes & FILE_ATTRIBUTE_RECALL_ON_OPEN != 0
108108
}
109109

110-
/// Whether `path` carries one or more NTFS Alternate Data Streams beyond its
111-
/// main unnamed `::$DATA` stream (DESIGN s5.2.1, STRESS_HARNESS s3.5
112-
/// `ads-alternate-data-stream`).
110+
/// Whether `path` carries one or more WARN-WORTHY NTFS Alternate Data Streams
111+
/// beyond its main unnamed `::$DATA` stream (DESIGN s5.2.1, STRESS_HARNESS
112+
/// s3.5 `ads-alternate-data-stream`).
113113
///
114114
/// Driven backs up the main stream only; a file with named streams (e.g.
115115
/// `foo.txt:secret`) silently loses those streams. The scanner detects them
116116
/// so the orchestrator can surface a one-per-file `local.ads_skipped` warning
117117
/// (SPEC s24) rather than dropping them silently - silent data loss in a
118118
/// backup tool.
119119
///
120+
/// EXEMPTION: `Zone.Identifier` - the mark-of-the-web tag Windows attaches to
121+
/// every downloaded file - is deliberately NOT warn-worthy. It is transient
122+
/// browser provenance metadata, not user data; a Documents tree full of
123+
/// downloads carries it on thousands of files, and warning on each buried the
124+
/// real signal in the 2026-08-14 incident's logs (14,098 of that day's 14,174
125+
/// lines were `ads_skipped`). A file whose ONLY named stream is
126+
/// `Zone.Identifier` returns `false`; any other named stream still warns,
127+
/// Zone.Identifier alongside it or not. Zero extra I/O either way: the stream
128+
/// NAMES arrive in the same enumeration this probe already runs.
129+
///
120130
/// Windows enumerates streams via `FindFirstStreamW` / `FindNextStreamW`
121131
/// (`STREAM_INFO_LEVELS::FindStreamInfoStandard`). The main stream reports as
122132
/// `::$DATA`; any other `:<name>:$DATA` entry is an ADS. Non-Windows targets
@@ -149,12 +159,29 @@ fn has_alternate_data_streams(path: &Path) -> bool {
149159
fn FindClose(handle: isize) -> i32;
150160
}
151161

162+
// The stream name up to its NUL terminator.
163+
fn stream_name(name: &[u16]) -> &[u16] {
164+
let len = name.iter().position(|&c| c == 0).unwrap_or(name.len());
165+
&name[..len]
166+
}
167+
152168
// The unnamed main stream's name, as FindFirstStreamW reports it.
153169
fn is_main_stream(name: &[u16]) -> bool {
154-
// Compare against the literal "::$DATA" up to the first NUL.
155170
let main: Vec<u16> = "::$DATA".encode_utf16().collect();
156-
let len = name.iter().position(|&c| c == 0).unwrap_or(name.len());
157-
name[..len] == main[..]
171+
stream_name(name) == main
172+
}
173+
174+
// The mark-of-the-web tag (see the fn doc's EXEMPTION). NTFS stream names
175+
// are case-insensitive and this one is pure ASCII, so an ASCII
176+
// case-insensitive compare is exact.
177+
fn is_zone_identifier_stream(name: &[u16]) -> bool {
178+
const ZONE: &str = ":Zone.Identifier:$DATA";
179+
let name = stream_name(name);
180+
name.len() == ZONE.len()
181+
&& name
182+
.iter()
183+
.zip(ZONE.bytes())
184+
.all(|(&c, e)| c < 128 && (c as u8).eq_ignore_ascii_case(&e))
158185
}
159186

160187
let wide: Vec<u16> = path
@@ -180,7 +207,7 @@ fn has_alternate_data_streams(path: &Path) -> bool {
180207
}
181208
let mut found_ads = false;
182209
loop {
183-
if !is_main_stream(&data.stream_name) {
210+
if !is_main_stream(&data.stream_name) && !is_zone_identifier_stream(&data.stream_name) {
184211
found_ads = true;
185212
break;
186213
}
@@ -1354,6 +1381,63 @@ mod tests {
13541381
use async_trait::async_trait;
13551382

13561383
use super::*;
1384+
1385+
/// `Zone.Identifier` alone must NOT flag a file (log-noise follow-up to
1386+
/// the 2026-08-14 incident, where it accounted for ~all `ads_skipped`
1387+
/// warnings); any OTHER named stream still must, with or without a
1388+
/// Zone.Identifier alongside. Skips gracefully when the temp volume
1389+
/// cannot hold ADS (non-NTFS).
1390+
#[cfg(windows)]
1391+
#[test]
1392+
fn ads_probe_exempts_zone_identifier_only_files() {
1393+
let dir = tempfile::tempdir().unwrap();
1394+
1395+
let plain = dir.path().join("plain.txt");
1396+
fs::write(&plain, b"body").unwrap();
1397+
assert!(!has_alternate_data_streams(&plain), "no streams, no flag");
1398+
1399+
let zoned = dir.path().join("zoned.txt");
1400+
fs::write(&zoned, b"body").unwrap();
1401+
if fs::write(
1402+
format!("{}:Zone.Identifier", zoned.display()),
1403+
b"[ZoneTransfer]\r\nZoneId=3\r\n",
1404+
)
1405+
.is_err()
1406+
{
1407+
eprintln!("temp volume does not support ADS; skipping");
1408+
return;
1409+
}
1410+
assert!(
1411+
!has_alternate_data_streams(&zoned),
1412+
"Zone.Identifier alone is exempt (mark-of-the-web metadata)"
1413+
);
1414+
1415+
// NTFS stream names are case-insensitive; the exemption must be too.
1416+
let cased = dir.path().join("cased.txt");
1417+
fs::write(&cased, b"body").unwrap();
1418+
fs::write(format!("{}:zone.identifier", cased.display()), b"x").unwrap();
1419+
assert!(
1420+
!has_alternate_data_streams(&cased),
1421+
"the exemption is case-insensitive"
1422+
);
1423+
1424+
let secret = dir.path().join("secret.txt");
1425+
fs::write(&secret, b"body").unwrap();
1426+
fs::write(format!("{}:secret", secret.display()), b"hidden").unwrap();
1427+
assert!(
1428+
has_alternate_data_streams(&secret),
1429+
"a real named stream still flags"
1430+
);
1431+
1432+
let both = dir.path().join("both.txt");
1433+
fs::write(&both, b"body").unwrap();
1434+
fs::write(format!("{}:Zone.Identifier", both.display()), b"x").unwrap();
1435+
fs::write(format!("{}:extra", both.display()), b"y").unwrap();
1436+
assert!(
1437+
has_alternate_data_streams(&both),
1438+
"Zone.Identifier must not mask another named stream"
1439+
);
1440+
}
13571441
use crate::state::{
13581442
AccountRow, ActivityFilter, ActivityPage, FileSearchHit, FileStateRow, NewActivity,
13591443
NewPendingOp, PageRequest, PendingOpRow,

0 commit comments

Comments
 (0)