airlock sits between a finished torrent and the folder you were going to open. It is built for
one specific failure: the window between "download complete" and "you double-clicked it", on
a machine where nothing else may be watching.
Within that window it:
- keeps the payload out of the destination until a decision is reached, so there is no moment where a finished-looking file sits somewhere convenient and unexamined
- marks every file with the mark-of-the-web, which no mainstream BitTorrent client does — so SmartScreen, Protected View and the other zone-aware defences fire on torrent downloads
- denies execution on every quarantined file, and on everything extracted from it
- expands zip, 7z, tar and gzip archives under bounds, in quarantine, refusing entry names that traverse, are absolute, name a stream or a device, or end in a dot or space, and refusing link entries outright
- stops zip bombs on expansion ratio rather than on size, so tolerating a large download does not mean tolerating a bomb
- scans the payload and the extracted contents as files on disk, with Microsoft Defender
- fails closed: a scanner that is missing, broken, or timed out holds the payload exactly as a detection does, and a failure to mark or to deny execution is a reason to hold, not a log line
- leaves a written record of every decision, and a notice in the folder when it holds — even for a run that died before it could decide, which the next run explains on its behalf
Stated plainly, because a security tool that overstates its reach is worse than none.
- It is not real-time protection. The payload is examined once, at completion. Nothing watches the files afterwards. If a released file is later modified, or if something arrives by any route other than this hook, this program never sees it.
- The deny-execute ACE is a guard rail, not a boundary. It stops a double-click and a stray script. The owner of a file can rewrite its DACL, and the ACE is not claimed to survive an adversary who is already running code as you.
- One scanner, one opinion. Defender on-demand, and nothing else — no second engine, no AMSI pass, no reputation lookup. If Defender says clean, this program treats it as clean.
- A third-party antivirus is detected, never driven. Where Bitdefender or similar has taken
over and Defender has stood down, airlock says so and holds; it cannot ask the other product
for a verdict and does not report on it.
--allow-unscannedon such a machine means released without airlock's scan — quarantine, mark-of-the-web, deny-execute and archive inspection still run, and whatever real-time protection the other product provides is outside this program's knowledge. This path is reasoned from Microsoft's documentation and unit-tested branch by branch, but has never been run on a machine with a third-party antivirus installed. - Defender exclusions make the scan blind, and airlock cannot see them. If the downloads
folder is excluded, the on-demand scan returns clean for everything in it. The exclusion list
is readable only by administrators, and the hook runs as you, so this is a stated
requirement rather than a detected condition: check with an elevated
Get-MpPreference | Select-Object -ExpandProperty ExclusionPath. - ISO, CAB, xz, zstd and bzip2 are not opened. They are identified and held by default.
With
--allow-unsupported-archivesthey are released on the strength of the scan alone; airlock's own checks on their contents never run, and what you extract from them by hand is not marked. - RAR is opened with stated limits. Both format generations, stored and compressed, solid
and multi-volume, are read by a container parser in this tree and decoders from
compcol(pure Rust,forbid(unsafe_code)), and every member is checked against the archive's own CRC. An encrypted archive holds. A RAR using the ARM executable filter, RAR 1.x/2.x or RAR 7 compression, a dictionary over the in-memory bound, or a member of undeclared size is reported unexamined by name and held unless--allow-unsupported-archives. A compressed member over 512 MiB (--max-rar-member-bytes), or a solid RAR 5 group whose members add up to more, stops extraction as too large, because compressed RAR members are decoded in memory;--allow-oversized-archiveswaives that. One known decoder defect (a RAR 3.x low-distance table reset, libarchive's regression archive) produces wrong bytes that the CRC check catches: the member is withdrawn and the payload holds, so the defect cannot pass bad content off as the archive's. - A destination that cannot hold ACLs or alternate data streams cannot be protected. On FAT32 or exFAT every payload is held, and the notice says why. NTFS is required.
- A compromised torrent client is upstream of everything here. The client chooses what to invoke this with. If it is compromised or misconfigured, this program is downstream of that and cannot correct it.
- Releasing is trusting. Once a payload is released, this program has no further involvement with it.
- Seeding is interrupted. The payload moves into quarantine while it is checked and back when it passes; a client seeding it sees "files missing" for those seconds and must be resumed or rechecked. This is a usability cost, not a security one, but it is the reason some users should wait for the in-place mode on the roadmap.
--allow-unscanned,--allow-unsupported-archives,--allow-oversized-archivesand--forceare real reductions in safety. They exist so that lowering the bar is a deliberate, visible act. They are not defaults and should not be made into defaults casually. None of them releases a detection, and none of them releases a scan that ran and failed.
Treated as hostile — anything originating in torrent metadata or archive contents:
- archive entry names, and link entries inside archives
- declared sizes and compression ratios inside archives
- the payload name, wherever it is used to build a path
Treated as trusted — the local configuration: the destination directory, the client's own invocation, and the scanner's answer.
See ARCHITECTURE.md for why each boundary sits where it does.
No network calls, of any kind. Nothing about a payload leaves the machine — no hash
lookups, no reputation services, no telemetry. A SHA-256 is not anonymous: it is an exact
fingerprint of a specific file, and sending one to a third party discloses precisely what was
downloaded. This program computes no hashes at all. Reports, notices, and logs stay on the
machine — reports and notices in the vault beside the destination, the log under
%LOCALAPPDATA%\airlock; the report records the torrent's infohash and name only because the
client passed them in, and only on your disk.
For anything you would rather not describe in public, use GitHub's private vulnerability reporting: the Security tab on the repository → Report a vulnerability. It opens a report only you and the maintainer can read, and it is the preferred route for anything in the two categories below. For everything else — a crash, a wrong decision you can describe safely, a format that should be opened — an ordinary issue is fine and easier to discuss.
There is no published email address; private reporting covers the confidential case without putting one on a page that gets scraped.
Two things are especially worth reporting:
- any path that releases a payload the policy should have held — this is the failure mode the whole design is arranged against, and the one bug class here that is genuinely dangerous
- anything written outside the extraction root, under any archive format
Testing this involves deliberately hostile files. The EICAR test string is the safe way to exercise the detection path and is what the test suite uses; it is stored byte-shifted in the test sources so a machine with real-time protection does not quarantine the build.
If you test with a real sample, do it on a machine you are prepared to lose, and note that
--force will release it. Nothing in this program deletes anything on its own: a held payload
stays held until told otherwise, because quarantine that empties itself is quarantine you
cannot audit.