Build tooling and editor autorun
Capability, not signature: evaluate a string, open a socket, spawn a process, name a chain RPC, or append a tail past the config. Reads raw text, with no comment or string masking.
[ SUPPLY-CHAIN DEFENSE ]STELLAR / SOROBAN0.0.1 / UNRELEASED
A scanner for the shapes that have actually compromised Stellar and Soroban repositories. It reads the tracked tree, judges a change against the policy that existed before the change, and fails closed on every file it did not read.
Five public Soroban repositories shipped the same copy-pasted scan step. A review of that step found seven ways to commit anything you like past it.
On 2026-10-27 all seven were run against the real script. Every one exited 0, which the workflow reads as a pass. The shapes include an artifact dropped before any rule sees it, a payload line that exempts itself with a comment, and a decode-then-evaluate line in a build config that scores one warning where only HIGH affects the exit code.
Airlock is the rewrite those campaigns should have had. It fails closed on anything it did not read, judges a change by the policy that existed before it, and keeps detection rules as data with a fixture behind every one.
It is a scanner for a specific attack class, not a general-purpose antivirus. A clean report means nothing matched the rules that ship, which is a narrower claim than "no compromise found".
Capability, not signature: evaluate a string, open a socket, spawn a process, name a chain RPC, or append a tail past the config. Reads raw text, with no comment or string masking.
A committed Wasm file is trusted only when a hash-keyed manifest in the trusted base names its bytes. Unlisted, swapped, moved and stale artifacts are reported, and an archive is reported as unread.
Invisible and direction-flipping characters, a decoder feeding an evaluator, paste and tunnel endpoints, obfuscator identifier grammar. The roles each rule reads are stated in the rule data, not in detector code.
Workflows, package manifests, install hooks and editor settings get roles of their own. Pairing rules catch a secret interpolated into a display command, and a privileged trigger checking out a pull request head.
Every skip carries a rule id, a severity and a path. There is no default directory skip, and no inline marker that hides a line from every rule. A scan that could not run never reports success.
A change cannot relax the rules it is judged by.
In pr and push modes, policy, suppressions and the Wasm manifest are read from the trusted base: the pull request base commit, or the commit a push started at. Neither is read from HEAD. A base that cannot be resolved is exit 2, not a scan against HEAD.
| Mode | Policy comes from | Use for |
|---|---|---|
| pr | The pull request base commit | Gating a contribution |
| push | github.event.before, where the push started | Gating a merge |
| full | The working tree, announced with an INFO | A local look at your own repository |
$ git clone https://github.com/posture-sec/airlock.git $ cd airlock $ python3 -m venv .venv && . .venv/bin/activate $ pip install . $ airlock scan --mode full # exit 0 when nothing reached fail_on, 1 when something did
Python 3.11 or newer. There is no package index release yet, so install from a checkout; the PyPI name is not Airlock's to promise.
The composite Action is task 4.1 and does not exist. Until it ships, make the CLI the step in your workflow and keep the checkout at fetch-depth: 0 so a pull request base is reachable.