CASCADE seal 74a18ebf30d05515 · measured, then frozen
Appendix B · Screening Security and data handling ← Back to the findings

The lists come down. Nothing goes up.

A matte aluminium padlock; its shackle, closed, is the same deep ruby as the tool's accents.

Screening handles the most sensitive strings a bank holds: the names it checks. This page says what touches the network, what never does, and how both are enforced by tests rather than promised by sentences.

One file may touch the network, and a test walks all the others

Exactly one module is allowed to reach the network: the downloader of the public sanctions lists. The permission is a named allowlist with its reason, and a structural test walks every source file for network sites (fetch, node:http and kin, WebSockets) and fails the suite if any other module grows one. The detector carries its own witness: it first proves it can see a planted call, so its zero means looked and found none.

What the one allowed file does is one-directional: the list comes down, nothing goes up. No name you screen exists yet at that point; the downloader moves public documents only.

Verify the frontier

Run the suite. The walk fails if a network site appears outside the allowlist.

run it yourself
npm testfails if any module but the downloader touches the network

Where it livessrc/frontiere.test.ts:20 · src/frontiere.test.ts:45

The lists are fingerprinted, and a changed file is refused

npm run listes -- --fetch downloads OFAC SDN and the UN consolidated list, and writes a committed manifest: source, URL, date, sha256, byte and entry counts. The data itself stays out of git. When the tool later reads a list from disk, it recomputes the fingerprint and refuses a file that no longer matches the manifest, naming both fingerprints: screening against a list that is not the one recorded certifies nothing.

OFAC announces its own record count inside the file, and the parser is checked against it on every fetch; the UN address is the one the Security Council page publishes. Without the flag, npm run listes reports what is on disk and touches nothing.

Verify without the network

The no-flag form reads the disk against the manifest and says so.

run it yourself
npm run listesreports date, fingerprint state and entry counts; no network touched

Where it livessrc/listes.ts:238 · src/listes.ts:262

The EU list, honestly unavailable today

The EU consolidated financial sanctions list is served through a token-gated endpoint. On the day this record was built, every file endpoint answered an error with the generic public token the Commission's own RSS still advertises, so the manifest records the fact with its date instead of hiding the source: which list, which URL, which error, and the way out.

The way out is one environment variable: a personal EU Login token through CASCADE_EU_TOKEN, same URL, fetch again. Until then the tool screens against OFAC and the UN and the manifest says exactly that; a report that cited three lists while holding two would be the kind of sentence this suite exists to prevent.

Verify the manifest entry

The committed manifest carries the EU line, its error and its issue.

run it yourself
python3 -c "import json; l=[x for x in json.load(open('listes-manifest.json'))['listes'] if x['source']=='EU'][0]; print(l['disponible'], l.get('erreur',''), l.get('issue','')[:40])"prints the recorded unavailability and the way out

Where it livessrc/listes.ts:70 · src/listes.ts:319

Offline, everything still runs, and says so

CASCADE_OFFLINE=1 cuts the network for the whole tool. The suite passes under it, the measure runs under it, and the one command that needs the network refuses under it by naming the flag and the way out rather than failing into a stack trace. A refusal you can read is the difference between a tool that is off and a tool that is broken.

Verify offline

Cut the network and run everything.

run it yourself
CASCADE_OFFLINE=1 npm testthe whole suite, network cutCASCADE_OFFLINE=1 npm run listes -- --fetchrefuses, names the flag, names the way out, writes nothing

Where it livessrc/listes.ts:319

Never a screened name in an output

When you measure on your own alert history, the outputs are written next to your file and nowhere else: a report and a sealed record holding counts, rates, intervals and verdicts by your alert id. Never a screened name, never a list entry you matched, never a path with your username. Your identifiers do survive as verdict keys, so choose them opaque: an id that is an account number stays an account number.

The screening itself runs on your machine against local lists; there is no callback, no telemetry, no account. The evaluation needs nothing from us but the repository.

Verify on a decoy

Measure a file with an invented name, then search the outputs for it.

run it yourself
grep -r "the name you invented" your-export-measured.md your-export-measured.jsonfinds nothing; the outputs carry verdicts, not names

Where it livessrc/your-alerts.ts:24 · src/your-alerts.ts:395