3lens/whitepaper

Whitepaper

Sections 1 and 4 are summarized from the README and have not been checked claim by claim against the code; the claims in sections 2, 3 and 5 each carry their own verification status and evidence.

1. What it is

Three-Lens (3lens) is a plugin for Antigravity CLI (agy) that provides evidence-backed, multi-stage security assurance and vulnerability verification. It has no external dependencies: the dependencies and devDependencies in package.json are empty, and hooks, skills and scripts contain no imports of non-built-in modules.

2. Design principles

Presumption of non-pass

Every authority claim (confirmation, dismissal, coverage completeness) defaults to not passing: a candidate’s default disposition is DEFERRED. Zero findings is a normal outcome, and a clean result only means coverage was completed within the declared scope, not a universal safety certification. Weaknesses found in testing are listed in section 3.

Conjunctive three lenses

Three independent views: Reachability, Defenses and Impact. Confirmation needs all three lenses to support with a complete evidence set; any one lens with a validated refutation dismisses the candidate. A majority vote does not decide the outcome.

3. Threat model and defences

There are four runtime layers: Layer 1 terminal sandbox (agy --sandbox), Layer 2 permission engine, Layer 3 shadow-context hook, Layer 4 path-containment and safe-read module. Each claim below stands on its own.

Presumption of non-pass

Verified

Asset protected
Trustworthiness of both “confirmed” and “dismissed” verdicts
Defence
A candidate’s default disposition is DEFERRED (needs manual review). To reach REPORTABLE, all three lenses (reachability, defenses, impact) must each cast one supporting vote, each vote must carry evidence that passes the structural check, the evidence set must include both a source and a sink, and the rigor score must be at least 0.60. Missing votes, missing lenses or a split panel always end in DEFERRED. A clean claim is impossible unless all seven gates pass (coverage, matrix, findings, context, execution, tool integrity, compact context).
Evidence
  • Code deriveFinalDisposition (skills/3lens/scripts/finalize-scan.mjs)
  • Code validateVoteEvidence (skills/3lens/scripts/finalize-scan.mjs)
  • Test SEC-INV-02, SEC-INV-15 (skills/3lens/scripts/check-release-invariants.mjs)
Residual risk
Dismissal is looser than confirmation. Any one lens that cites an existing file and a line within its length can dismiss a candidate, even when the other two lenses support it. Evidence validation is structural only and does not check that the line supports the argument: in a test, citing line 1 of README.md as a defense produced SUPPRESSED, while citing a missing file or a line past the end produced DEFERRED. Also, the README says the default is UNVERIFIED, but the code’s default disposition is DEFERRED; UNVERIFIED is a status or display label (for example the verification status when no delegation was observed, and the ACP adapter’s label for unconfirmed candidates), not a disposition.
Not protected
“Clean” only means coverage was completed within the declared scope and no verified finding remains. It is not proof that no vulnerability exists.

Secret tokenization

Verified

Asset protected
Credentials and keys in review input
Defence
When the review context is prepared, 6 pattern classes (AWS keys, GitHub tokens, Bearer tokens, PEM private-key blocks, key-value passwords and keys, JWTs) are replaced by line-preserving tokens, and a file is skipped if its line count changes. Only supported extensions and .env* are processed; dotfiles and dot-folders (except .env) never enter the context, and while an audit is active the hook refuses direct reads of paths that are not in it. User prompts get a separate prompt-side guard with a different set of 7 pattern classes.
Evidence
  • Code SECRET_PATTERNS, tokenizeSecretsForContext (skills/3lens/scripts/finalize-scan.mjs)
  • Code prepare-review-context.mjs
  • Code SECRET_PATTERNS (hooks/pre-invocation-secret-guard.mjs)
  • Test tokenizeSecretsForContext check (skills/3lens/scripts/check-release-invariants.mjs)
Residual risk
Pattern matching only recognizes listed formats. In a test with 15 synthetic samples, context tokenization caught 5: AWS keys, GitHub ghp_ tokens, JWTs, PEM RSA private keys and plain alphanumeric passwords of 16+ characters. It missed 10: Slack tokens, Google API keys, passwords with symbols, passwords shorter than 16 characters, database URLs with an inline password, client_secret, a bare token key, Azure AccountKey, and encrypted and PGP private-key blocks. The two pattern sets do not cover each other: only the prompt guard knows Slack and Google keys, and only context tokenization knows JWTs and passwords.
Not protected
Secrets in other formats are not covered. Content a user pastes into a prompt is covered only by the prompt guard’s 7 pattern classes.

Path containment and symlinks

Verified

Asset protected
Files outside the repository (reads and writes)
Defence
The Layer 3 hook requires both lexical and realpath checks; Layer 4 compares dev/ino of the opened descriptor with the realpath target.
Evidence
  • Code isWriteTargetRealPathContained (hooks/shadow-context-guard.mjs)
  • Code safeReadFileContained (skills/3lens/scripts/path-containment.mjs)
  • Test tests/test-containment-gaps.mjs
Residual risk
run_command can still create symlinks; hard links to outside files are detected by neither layer; filesystems reporting dev or ino as 0 are rejected; tested on Windows only; how hard the race windows are to win in practice was not measured.
Not protected
The real isolation boundary is Layer 1 (agy --sandbox). The Layer 3 hook fails open when no audit context is active or on an unhandled error.

4. Job modes

Six job contracts, each a specification file under skills/3lens/jobs/, executed by the coordinator agent (an LLM agent) according to the spec; code handles the deterministic parts such as directory accounting, validation, deduplication and finalization.

JobWhat it doesScripts the spec references
scanScans a whole repository or scoped directories; directory accounting must be complete, and zero findings is a valid outcome.build-inventory.mjsfinalize-scan.mjs
reviewReviews a PR, branch diff or working-tree diff, looking only at changed files with static source-to-sink analysis and no exploitation.—
validateIndependently validates an externally supplied candidate, dispatching the three lenses concurrently, without rescanning the repository.—
deepMulti-pass exploration (3 runs by default, at least 2), with candidates deduplicated by fingerprint and merged.finalize-scan.mjs
remediateGenerates a minimal patch in the isolated scratch/patches/ workspace and never applies it to the working copy directly; before proposing a patch it checks that the target file has not diverged from the audited commit.—
verify-fixVerifies a fix under regression semantics: format and path-safety checks, a clean-apply check (git apply --check), and baseline-staleness detection.validate-patch.mjs

The hard constraints in each job (for example that remediate must never apply a patch directly) are written in the specs; this site has not yet confirmed which of them are enforced in code.

5. Benchmarks

Benchmarks: what is measured, what is not

Verified

Asset protected
How readers interpret “100%”
Defence
The README labels each benchmark with a measurement type (MEASURED, RECORDED_SYNTHETIC, MODEL_OBSERVED). I re-ran four deterministic benchmarks locally and the numbers match the README: 50 cases 50/50 (8 groups), 20 paired cases 20/20, stability 10 lineages 10/10 (both synthetic and recorded). They measure whether decision logic and output handling follow the rules, using pre-written simulated candidates, not the model’s ability to find vulnerabilities.
Evidence
  • Doc README.md › Public Benchmark Claims Policy
  • Test npm run test:evals, test:semantic, test:stability, test:stability-recorded
  • Doc release-evidence/cross-provider-replication-report-2026-09-23.md
Residual risk
The model’s actual discovery numbers are in the release-evidence reports (quoted here, not re-run): recall on vulnerable holdout fixtures of 96.0% (24/25) and 96.7% (29/30), with only 3 passes per configuration; false positives on clean samples were 0/25 for one configuration and 8/18 (55.6% specificity) for the other. The sample is small, and the report itself rules the ground truth of one fixture defective. The README table only says “BASELINE ESTABLISHED” and does not show these numbers.
Not protected
These numbers do not describe performance on other codebases, other models or other prompts.