White Paper supply-chain-securityanomaly-detectionci-cdbyte-level-modelscompression-models

GRAFT: Byte-Level Anomaly Detection for Supply Chain Grafts

How GRAFT trains a per-repo compression baseline and scores PR-changed files for byte-level deviation, without signatures or labeled attacks.

October 6, 2026

The detection primitive

GRAFT is built on a single fact about lossless compression: a model that predicts the next byte well assigns it a short code, and a model that’s surprised assigns a long one. Train that predictor on a corpus and you get a numeric readout, bits-per-byte, of how well any new input matches what the model learned.

GRAFT applies this at the file level. Train the predictor on a repo’s own benign source, then run it over every file a PR touches. Files that compress the way the rest of the repo compresses score low BPB. Files carrying something the model never saw, obfuscated logic, encoded payloads, foreign binary content, score high.

This sidesteps the two usual weaknesses of supply chain scanning. There’s no signature database to keep current against novel payload formats. There’s no labeled set of “attack examples” to train a classifier on, which means no assumption about what tomorrow’s attack looks like syntactically. The only assumption is that foreign content introduced into a codebase tends to have a different byte distribution than the codebase itself. Base64 blobs, eval obfuscation chains, null-byte stuffed configs, and minified JS all satisfy that assumption without needing to be enumerated in advance.

Baseline construction

The baseline is built fresh per repo:

  1. git ls-files enumerates everything tracked at HEAD.
  2. Binaries and files under 64 bytes are dropped.
  3. What’s left is packed into a binary corpus in SUBSTRATE’s format.
  4. A MICRO-config model (~3.2M params, 512-byte context window, 64 chunk positions, 128 local / 256 global dim, 1/2/1 encoder/global/decoder layers) trains for train-steps (default 3000) on CPU, AdamW with cosine LR, finishing in roughly 3 minutes.
  5. A random 20% holdout of that corpus calibrates baseline_mean and baseline_std for the repo.
  6. The checkpoint is cached under a weekly key and retrained automatically on that cadence.

Because the model only ever sees this repo’s files, the resulting baseline is repo-shaped, not language-shaped. A Python/YAML repo and a pure Go repo produce different checkpoints and different thresholds, each calibrated to its own holdout.

Scoring and severity

At PR time, GRAFT scores each changed file that matches its scanned extension list, which spans common source languages, CI/build/infra files, and package manifests or lockfiles. Files under 64 bytes or over 512 KB, and anything binary, are skipped automatically, as is anything beyond max-files changed files per PR (default 50).

Severity is sigma above the calibrated baseline:

TierCondition
CRITICALBPB > baseline + 5σ
HIGHBPB > baseline + 3σ
MEDIUMBPB > baseline + 2σ
CLEANwithin 2σ

threshold-sigma (default 3.0) controls the flag line; lowering it raises sensitivity. On the reference run (mean 1.09, std 0.48, threshold 2.52), the four tested attack payloads landed between 9.04 and 13.33 max BPB, 8 to 26 sigma out, against normal Python and YAML sitting at 1.97 to 2.31 BPB. The separation is wide enough that threshold tuning is about false-positive control, not about catching those payload classes at all.

Results post as a ranked PR comment: a table of flagged files by severity, max BPB, mean BPB, sigma, and window count, followed by a count of files that scanned clean. fail-on-anomaly (default false) controls whether HIGH/CRITICAL findings block the check or just annotate it.

Where the model is blind

GRAFT’s detector has no semantic layer. It doesn’t parse the file, doesn’t know what an eval() call does, doesn’t track data flow. It only measures whether the byte sequence was predictable given the repo’s history. That gives it two failure directions.

False negatives come from attacks that mimic the surrounding file’s style closely enough, matching variable naming, whitespace, and import conventions, to stay inside the learned distribution. A backdoor written in the same hand as the rest of the file can score clean.

False positives come from legitimate files that are byte-unusual relative to hand-written source: generated protobuf output, minified vendored JS, binary test fixtures. These are addressed with .graftignore, max-files exclusion, or a raised threshold-sigma, rather than by the model learning to distinguish “generated” from “malicious,” which it has no basis to do.