argus

July 23, 2026 · View on GitHub

CI License: Apache-2.0

"100-eyed guardian." Static install-time scanner for eight package ecosystems, with opt-in Sigstore verification plumbing for npm provenance.

argus is a pre-release Rust CLI that inspects package artifacts from npm, PyPI, crates.io, Go modules, NuGet, Maven, RubyGems, and Composer/Packagist before package build or install hooks run. It combines artifact-integrity checks with ecosystem-specific static rules; neither a matching digest nor a clean static scan proves that an artifact is safe. See the matrix below and the "Status" section for the implemented capability snapshot.

Release automation and the repository-root GitHub Action are implemented but are not yet a public installation contract. Until an immutable release and the protected v1 branch are published, do not reference majiayu000/argus@v1 or assume that v0.1.0 assets exist. The operator sequence and verification boundary are documented in docs/releasing.md.

Ecosystem capability matrix

All rows describe code implemented on main, not a released binary contract.

EcosystemCLI commandIntegrity sourceArtifact and inspected surfacesExplicit limitations
npmfetchRegistry dist.integrity SRI digestTarball; lifecycle scripts, package metadata, text/binary content rules, and opt-in bounded metadata-anomaly checksStatic rules can miss obfuscated or dynamic behavior; npm search supplies candidates rather than complete publisher history; Sigstore plumbing is opt-in and real npm v0.2 bundles cannot yet reach a Verified verdict because of the documented upstream intoto/0.0.2 gap
PyPIpypi-fetchPyPI JSON digests.sha256sdist/wheel; setup.py, import-time Python surfaces, and package contentDoes not execute Python or prove runtime behavior
crates.iocrates-fetchcrates.io API SHA-256 checksum.crate; build.rs, Rust source, and proc-macro structureDoes not compile code or execute procedural macros
Go modulesgo-fetchGOPROXY .ziphash h1: directory hash when availableModule ZIP; init, package initializers, process and network callsMissing/unusable .ziphash is reported as go-integrity-unverified Info and can still allow; source detection is regex-based and sum.golang.org transparency is not verified
NuGetnuget-fetchCatalog SHA-512 packageHash when available.nupkg; PowerShell install hooks and MSBuild .targets/.propsDoes not verify .signature.p7s or inspect DLL bytecode; unavailable catalog hashes are reported explicitly
Mavenmaven-fetch.jar.sha256, falling back to weaker .jar.sha1JAR; pom.xml, manifests, resources, and embedded build scriptsDoes not inspect .class bytecode; SHA-1 fallback detects corruption but is not collision-resistant
RubyGemsgems-fetchRegistry SHA-256 sha.gem; gemspec, extconf.rb, and Ruby sourceStatic rules do not execute Ruby; internal archive checksums are not an independent trust anchor
Composer / Packagistcomposer-fetchPackagist dist.shasum SHA-1Dist ZIP; lifecycle hooks, autoload.files, and PHP sourceSHA-1 is weak, missing hashes are high-risk, VCS-only packages are unsupported, and dynamic PHP can evade regex rules

Decisions

  • block — at least one high-risk rule fired.
  • allow-with-approval — only approval-scoped evidence such as a known native-build pattern, bounded npm metadata anomaly, or weak-only lockfile integrity fired; require explicit approval.
  • allow — no rule fired.

Usage

# Scan one local package directory
cargo run -p argus-cli -- scan corpus/fixtures/lifecycle-curl-sh

# Fetch a real npm package: packument -> tarball -> SHA-512 verify -> safe
# extract -> scan. No lifecycle script ever runs.
cargo run -p argus-cli -- fetch chalk@5.3.0
cargo run -p argus-cli -- fetch '@types/node@20.10.0' --format json

# Opt in to bounded npm metadata-anomaly checks. The separate cache stores
# npm search responses for at most 15 minutes.
cargo run -p argus-cli -- fetch chalk@5.3.0 \
  --metadata-anomaly \
  --metadata-cache-dir ~/.cache/argus/npm-metadata

# Fetch a real PyPI package: JSON API -> sdist/wheel -> SHA-256 verify -> safe
# extract -> scan. setup.py never runs.
cargo run -p argus-cli -- pypi-fetch requests@2.31.0 --prefer wheel
cargo run -p argus-cli -- pypi-fetch django@5.0.0 --prefer both --format json

# Fetch a real crates.io crate: JSON API -> .crate -> SHA-256 verify -> safe
# extract -> scan. build.rs never runs.
cargo run -p argus-cli -- crates-fetch serde@1.0.228
cargo run -p argus-cli -- crates-fetch tokio --format json

# Fetch a Go module: GOPROXY zip -> h1 verify when .ziphash exists -> static scan
cargo run -p argus-cli -- go-fetch golang.org/x/text@v0.16.0

# Fetch a NuGet package: .nupkg -> catalog hash (when available) -> hook scan
cargo run -p argus-cli -- nuget-fetch Newtonsoft.Json@13.0.3

# Fetch a Maven artifact: JAR checksum -> POM/resource/build-script scan
cargo run -p argus-cli -- maven-fetch org.apache.commons:commons-lang3:3.14.0

# Fetch a RubyGem: registry SHA-256 -> nested .gem extraction -> Ruby scan
cargo run -p argus-cli -- gems-fetch rake@13.2.1

# Fetch a Composer package: Packagist dist ZIP -> lifecycle/PHP scan
cargo run -p argus-cli -- composer-fetch monolog/monolog@3.7.0

# Custom registry that serves tarballs from a separate CDN/host:
cargo run -p argus-cli -- fetch internal-tool@1.2.3 \
  --registry https://npm.corp.example \
  --allow-tarball-host cdn.corp.example \
  --allow-tarball-host objects.corp.example

# Run the full regression corpus (6 agent + 11 package + 1 lockfile cases)
cargo run -p argus-cli -- corpus test

# Machine-readable output
cargo run -p argus-cli -- scan path/to/pkg --format json

# SARIF 2.1.0 for code-scanning integrations
cargo run -p argus-cli -- scan path/to/pkg --format sarif > argus.sarif

# Lockfiles use basename + a closed structure/version signature.
cargo run -p argus-cli -- scan path/to/project/pnpm-lock.yaml --format json

# An explicit parser is validated together with the basename and signature.
# Extra source hosts are exact DNS names, not patterns.
cargo run -p argus-cli -- scan package-lock.json \
  --lockfile-format package-lock \
  --allow-registry-host packages.corp.example

# Query OSV for one exact package version. The cache directory is always
# explicit; online mode sends this coordinate to api.osv.dev.
cargo run -p argus-cli -- vulns package \
  --ecosystem npm --name lodash --version 4.17.20 \
  --cache-dir ~/.cache/argus/osv

# Query every complete external coordinate in one supported lockfile without
# network access. Offline mode requires a complete fresh cache snapshot.
cargo run -p argus-cli -- vulns lockfile Cargo.lock \
  --cache-dir ~/.cache/argus/osv --offline --format json

# Scan agent surfaces: MCP configs, skills, hooks, AGENTS.md / CLAUDE.md.
# Detects injection/override language (AGT-01), dangerous script
# capabilities like curl|sh or secret-read + network-egress (AGT-03),
# and high-risk config flags such as alwaysLoad: true (AGT-05).
cargo run -p argus-cli -- agent scan ~/.claude
cargo run -p argus-cli -- agent scan path/to/skill .mcp.json --format json

# Recompute the explicitly synthetic GH-58 fixture metrics.
cargo run -p argus-cli -- corpus eval --corpus corpus/agent --format json

The compiled binary is named argus and exits non-zero on block.

Lockfile source and integrity policy

argus scan statically normalizes nine lockfile families without starting a package manager, VCS command, shell, or network request:

LockfileAccepted closed versions/signatureIntegrity interpretation
package-lock.jsonnpm lockfile 2, 3registry/URL entries require valid SRI; root/link/workspace records are unavailable by format
yarn.lockClassic 1; Berry metadata 4, 6, 8Classic uses SRI or the resolved SHA-1 fragment; Berry npm/archive records require checksum
pnpm-lock.yamlcanonical 5.4, 6.0, 9.0registry/tarball records require SRI; link/workspace/file records are unavailable
poetry.lock1.1, 2.0, 2.1every listed registry artifact is retained and requires a valid hash
uv.lock1every listed registry/URL distribution is retained and requires a valid hash
Cargo.lock3, 4registry packages require SHA-256; path and git records do not treat a VCS revision as an artifact hash
go.sumstrict three-field grammarevery line requires a valid Go h1: digest; source host is unavailable by format
Gemfile.lockBundler major 2, 3, 4; CHECKSUMS only where supportedexact lock-name checksum association; an absent checksum section is unavailable, not verified
composer.lockschema-v1 structurenon-empty dist SHA-1 is weak evidence; missing dist shasum is optional-absent

Every normalized source is evaluated independently. Plain HTTP is always Critical/block. HTTPS, SSH, and scp-like git hosts must exact-match the format's documented public hosts or a repeated --allow-registry-host; user entries accept one IDNA-normalized DNS host and reject schemes, ports, paths, userinfo, wildcards, suffix patterns, and IP literals. Git refs are immutable only when they are a 40- or 64-character lowercase commit digest.

Strong SHA-256/384/512, SRI, and Go h1: evidence can allow; SHA-1/MD5-only evidence requires approval. Required missing evidence blocks at High, and unknown algorithms, malformed encodings/lengths, or conflicting values block at Critical. Legitimate unavailable-by-format records produce one format-scoped Info finding with a count and at most 20 stable locators; they do not change the decision.

Detection is fail-closed: unknown/ambiguous basenames or signatures, new versions, unsupported entries, coverage mismatch, parse failure, or any bound failure exits operationally with code 2, stderr, and empty stdout—no text, JSON, or SARIF report is emitted. Bounds are 64 MiB input, 100,000 records, 64 nesting levels, 1 MiB per scalar, 1,000,000 total scalars, and 64 MiB of RFC 8785 canonical finding/evidence JSON. Equality is accepted; plus one is rejected. This scan evaluates source and integrity metadata only: it does not claim vulnerability status, malicious-package status, or artifact safety.

Explicit OSV vulnerability queries

argus vulns is an opt-in known-vulnerability query. It accepts either one exact package coordinate or the normalized external coordinates from any of the nine lockfile families above:

argus vulns package \
  --ecosystem <npm|pypi|crates.io|go|nuget|maven|rubygems|packagist> \
  --name <name> --version <exact> --cache-dir <dir>

argus vulns lockfile <path> \
  [--lockfile-format <package-lock|yarn|pnpm|poetry|uv|cargo|go-sum|bundler|composer>] \
  --cache-dir <dir>

Both modes support --format text|json|sarif (default text), --max-age-seconds from 0 through 2592000 (default 86400), and optional --fail-on-severity low|medium|high|critical. Active advisories normally produce allow-with-approval and exit 2; a finding meeting the configured threshold produces block and exit 1; a complete no-match produces allow and exit 0.

--cache-dir is required in online and offline modes. Online queries use only the fixed https://api.osv.dev service and disclose the exact package coordinates being checked. --offline prohibits all OSV network access and requires every coordinate to have a complete fresh cache entry. --offline --allow-stale explicitly authorizes only a complete stale snapshot and emits visible vulnerability-data-stale approval evidence. Missing, corrupt, partial, future-dated, or unauthorized stale cache data is an operational error: exit 2, stderr, and empty stdout/no SARIF. Reports expose only the stable <argus-osv-cache> label, never the cache path.

These results are deliberately separate:

  • vulns reports known vulnerabilities for exact versions from OSV.
  • intel matches an explicitly imported offline known-malicious package snapshot.
  • provenance and lockfile integrity verify origin/digest evidence.
  • static package heuristics report suspicious install/runtime behavior.

One result family does not rewrite another. A no-match is not proof that a package is benign, correctly sourced, or safe. argus vulns never installs, upgrades, edits a manifest/lockfile, starts a package manager, or executes package code.

Opt-in npm metadata anomalies

fetch --metadata-anomaly enables policy npm-anomaly-v1; without this flag Argus makes no npm search request and emits no inferred metadata status. The policy produces two approval-only Medium findings:

  • version-shape-anomaly: the target has at least six earlier stable SemVer releases spanning at least 30 days, lands within 72 hours of its direct predecessor, jumps by at least two major versions or ten minor versions within the same major, and that jump class did not occur in the preceding five transitions.
  • rapid-publish-window: the target version's exact _npmUser.name appears on at least five distinct package names in the bounded npm search candidates published during the preceding 24 hours.

Insufficient valid history becomes the Info findings npm-version-shape-unassessed or npm-rapid-publish-unassessed; these do not change an otherwise-allow decision. Missing required target metadata, malformed/truncated responses, more than 250 search objects, bodies over 2 MiB, cache corruption, redirect-policy failures, and transport failures are operational errors: Argus exits 2 before emitting any report.

npm search is used only for candidate discovery and exposes current package versions, not a complete publisher activity ledger. Argus therefore exact matches publisher.username, never treats fewer than five observed packages as clean, and makes at most one search request per publisher per scan. The optional --metadata-cache-dir is keyed by the normalized full registry base URL (including base path), publisher, target publication time, and policy. A cache entry is reusable for 15 minutes only when it was fetched no earlier than the target publication time.

Offline known-malicious package intelligence

Argus can explicitly import a fixed revision of the OpenSSF malicious-packages OSV data set and use the verified local snapshot while scanning any of the eight supported registries:

REVISION="$(git -C /path/to/malicious-packages rev-parse HEAD)"

cargo run -p argus-cli -- intel import \
  --source https://github.com/ossf/malicious-packages \
  --revision "$REVISION" \
  --output ~/.cache/argus/malicious-packages.json

cargo run -p argus-cli -- intel status \
  --db ~/.cache/argus/malicious-packages.json

cargo run -p argus-cli -- fetch suspicious-package@1.2.3 \
  --malicious-db ~/.cache/argus/malicious-packages.json \
  --format json

Only intel import uses the network. It accepts the canonical GitHub source, a full pinned commit SHA, and the bounded GitHub-to-codeload archive redirect. Normal scans load and verify the local snapshot without making an intelligence request. Missing, corrupt, incompatible, or future-dated data is an operational error when --malicious-db is enabled; Argus does not silently continue as if there were no match.

A match emits known-malicious-package at Critical severity and blocks the package. A non-match means only that the exact coordinate was absent from the pinned snapshot—it is not evidence that the package is safe. Text, JSON, and SARIF output retain the snapshot source, revision, import time, age, and archive/records/snapshot digests even when there is no match.

This data set is malicious-package intelligence. It is deliberately separate from general CVE/advisory lookup, which remains tracked by GH-94.

SARIF and GitHub Code Scanning

--format sarif is available on package/lockfile scans, every ecosystem fetch command, and agent scan. The output preserves Argus rule IDs, severity, file/line evidence when present, package coordinates, and stable partial fingerprints. A finding without a line uses an artifact-level location; Argus does not invent line 1.

Generic SARIF consumers can read the generated file directly. A GitHub Actions job can upload it with the official action (currently v4):

permissions:
  contents: read
  security-events: write
steps:
  - uses: actions/checkout@v7
  - run: argus scan path/to/pkg --format sarif > argus.sarif
  - uses: github/codeql-action/upload-sarif@v4
    with:
      sarif_file: argus.sarif

Argus writes normal SARIF only after a complete scan report exists. Invalid input, parser failures, network failures, and agent-snapshot failures before inventory comparison write an error to stderr, exit 2, and leave stdout empty instead of emitting a clean run. An agent-snapshot failure after comparison retains its completed results in a partial SARIF run whose invocation has executionSuccessful=false and a sanitized error notification. A successful SARIF report retains the normal decision exit codes: allow = 0, block = 1, and allow-with-approval = 2.

Rule coverage (Milestone 0)

FamilyRules
lifecyclelifecycle-script, pre-scan-execution-marker
contentremote-download, shell-pipe-execution, credential-access, network-exfiltration, binary-execution, runtime-hook, wallet-interception, token-harvest, github-write-api, npm-publish
binarybinary-file
nametyposquatting, low-reputation, dependency-confusion, public-registry-internal-name, known-native-build-pattern
lockfilelockfile-http-resolved, untrusted-registry-host, lockfile-mutable-vcs-ref, lockfile-integrity-missing, lockfile-integrity-invalid, lockfile-integrity-weak, lockfile-integrity-unavailable
provenancemissing-provenance (info), provenance-verified-subject (info), provenance-subject-mismatch (block), provenance-fetch-blocked / provenance-fetch-failed / provenance-parse-failed (operational errors)
npm metadataversion-shape-anomaly, rapid-publish-window (approval); npm-version-shape-unassessed, npm-rapid-publish-unassessed (info)
ai-contextai-context-poisoning — writes to .cursorrules, CLAUDE.md, .claude/*, AGENTS.md, .aider.conf.yml, .continuerules, .codexrules, .windsurfrules. Pioneered at scale by the TrapDoor campaign (Socket.dev 2026-05-24).

Agent-surface rule coverage (GH-57)

argus agent scan statically scans agent supply-chain surfaces — MCP configs, skill definitions, hook scripts, and instruction files — without executing anything.

RuleSeverityDetects
AGT-01-injection-languagecritical → blockauthority-claim / instruction-override / concealment language (English + Chinese) in AGENTS.md, CLAUDE.md, SKILL.md, .claude/**/*.md, and MCP tool description fields
capability-manifestmedium → approvaldeclarative capability entries in JSON (capability, evidence, optional resolved_host) for network egress, unresolved hosts, sensitive reads, agent config writes, exec/eval, obfuscation, and persistence
AGT-03-remote-exechigh → blockremote download piped to a shell (curl … | sh, iwr … | iex) in hook/skill scripts
AGT-03-secret-exfilhigh → blockhigh-sensitivity credential access combined with network egress in the same script
capability-misfithigh → blockdeclared skill intent does not justify high-risk capability combinations such as credential exfiltration or agent config/hook writes
agent-config-writemedium → approval or high → blockscript writes .claude/settings*.json or hook paths; matching agent-config intent is declarative, mismatched intent blocks
hook-persistencehigh → blockscript persists or auto-approves an agent hook
credential-access / network-exfiltrationhigh → blockmanifest-backed evidence for credential reads and off-box network exfiltration
AGT-05-mcp-always-loadmedium → approvalmcpServers.<name>.alwaysLoad: true (permanent full trust)
AGT-05-enable-all-project-mcpmedium → approvalenableAllProjectMcpServers: true
AGT-05-enabled-mcpjson-serversmedium → approvalnon-empty enabledMcpjsonServers allowlist
AGT-05-posttooluse-output-rewritemedium → approvalPostToolUse hook rewriting updatedToolOutput for non-MCP tools
AGT-05-config-unparseableinfoagent config file is not valid JSON
AGT-02medium → approvalan already-approved MCP/skill description drifted from its recorded baseline hash (rug-pull detection; see below)
AGT-02-baseline-entry-missinginfoa baselined description is no longer present on the scanned surface
AGT-02-baseline-unreadableinfo--baseline file could not be read/parsed (scan continues; not treated as "no drift")
AGT-04-entry-addedmedium → approvala non-symlink high-context file or directory was added after approval
AGT-04-entry-removedmedium → approvala non-symlink high-context file or directory was removed after approval
AGT-04-entry-type-changedmedium → approvala high-context path changed between file and directory
AGT-04-content-modifiedmedium → approvalthe complete bytes of a high-context file changed
AGT-04-symlink-changedmedium → approvala symlink was added, removed, retargeted, or changed to/from another entry type

AGT-04 install-time high-context snapshot

AGT-04 provides an explicit before/after approval workflow for agent installations. Keep the snapshot outside the tree an installer can modify, preferably in separately protected version control:

# 1. Before installation: approve the complete high-context inventory.
argus agent scan path/to/agent \
  --update-snapshot /protected/argus/agent.snapshot.json

# 2. Run the installer using your normal, separately reviewed process.
your-installer path/to/agent

# 3. After installation: compare without approving the changes.
argus agent scan path/to/agent \
  --check-snapshot /protected/argus/agent.snapshot.json

# 4. Review every finding; approve a new state only with an explicit update.
argus agent scan path/to/agent \
  --update-snapshot /protected/argus/agent.snapshot.json

All five AGT-04 changes are Medium and therefore allow-with-approval unless another rule blocks. Check mode never records approval. Update mode records approval only after discovery, semantic rules, optional judge, and atomic persistence all succeed; it does not erase existing AGT-01/02/03/05/judge findings or force their exit code to zero. The normal report and snapshot written: N entries message are emitted only after the snapshot has been persisted.

Snapshot mode performs a complete, non-following walk without pruning .git or node_modules. Its canonical membership includes the existing instruction, MCP, hook, and skill-script surfaces; all .claude/** entries; and .cursorrules, .aider.conf.yml, .continuerules, .codexrules, and .windsurfrules. It inventories classified files, directories, and symlinks. New inventory-only shapes are hashed but skipped before text, UTF-8, size, binary, and semantic symlink validation; existing semantic surfaces retain their fail-closed validation.

Root-aware classification exists only in snapshot check/update mode. For example, scanning ~/.claude, ~/.claude/rules, or ~/.claude/settings.json preserves the .claude/ classification context while report and snapshot keys remain scan-root-relative. With no snapshot flag—including AGT-02-only check/update—the legacy root-relative classifier and .git/node_modules pruning remain unchanged; settings.json at a ~/.claude scan root is not silently promoted into a new semantic surface.

The snapshot target cannot hide a protected entry. If a target inside the scan root classifies as any supported surface, check and update reject it before exclusion, loading, rendering, or writing, even when the future update target does not exist. An unclassified target can be inside the root, but an external protected location is recommended.

The persisted schema is strict version 1 with sorted relative UTF-8 paths. Files store SHA-256 of all bytes; directories store no digest; symlinks store only SHA-256 of the raw link-target representation (Unix bytes, or Windows UTF-16 code units encoded little-endian), never target plaintext. Unknown fields, duplicate decoded JSON keys, invalid paths, invalid digests, traversal errors, and entries that change while being captured fail closed. Because symlink representation is platform-specific, approve and check such snapshots on the same platform.

Updates use a same-directory temporary file, write, flush, file sync, then atomic replace. A failed stage preserves the previous destination and cleans the temporary file. Argus does not coordinate concurrent snapshot writers; serialize approval operations and treat the snapshot as a trust artifact.

The persistence combinations are deliberately narrow:

FlagsAllowed
no baseline/snapshot flagyes; legacy multi-path behavior
--check-snapshotyes; exactly one scan path
--update-snapshotyes; exactly one scan path
--baseline + --check-snapshotyes; two read-only checks on one path
any update flag plus another baseline/snapshot flagno

Failures before inventory comparison produce the ordinary operational contract: stderr, exit 2, and empty stdout. Once inventory comparison is complete, any later collection/projection, semantic rule, AGT-02, judge, or snapshot-persistence failure retains completed findings in a blocked partial report: text starts with execution: incomplete, JSON uses the agent_scan_incomplete envelope, SARIF sets executionSuccessful=false, stderr is sanitized, and the exit code is 2.

Optional external semantic judge

The deterministic scanner remains the default and never starts a process or uses the network. To add an explicitly configured semantic layer, pass both --llm-judge and the path to an executable bridge:

argus agent scan path/to/skill \
  --llm-judge \
  --llm-judge-command ./my-llm-judge-bridge \
  --format json

Argus starts that exact executable without a shell or interpolated arguments, writes a versioned JSON request to stdin, and requires a strict JSON response containing schema_version, decision, and a non-empty rationale. The bridge can recommend allow, allow-with-approval, or block; its result becomes an additional llm-intent-judge finding, so it can escalate but never erase or downgrade deterministic findings.

The opt-in process is fail-closed: 30-second timeout, 4 MiB request limit, 1 MiB limits for stdout and stderr, and a 4096-byte rationale limit. Timeouts, non-zero exits, output overflow, invalid UTF-8/JSON, unknown response fields, or unsupported decisions make the scan return an operational error. The bridge owns any network/API configuration; Argus contains no provider URL or credential handling.

AGT-02 description-drift baseline (GH-64)

AGT-01/03/05 catch malicious agent surfaces at first sight, but they cannot catch a rug-pull: an MCP tool/server description or SKILL.md frontmatter that a human already approved and that is later silently mutated. AGT-02 closes that gap with an explicit, file-based baseline.

# 1. Approve the current descriptions — writes the baseline file.
cargo run -p argus-cli -- agent scan --update-baseline agt02.baseline.json ~/.claude
#    → prints "baseline written: N entries" to stderr, exits 0.

# 2. Later scans compare against the approved baseline.
cargo run -p argus-cli -- agent scan --baseline agt02.baseline.json ~/.claude
#    → any drifted description emits an AGT-02 finding (medium → allow-with-approval).

What is baselined: every MCP mcpServers.<name>.description and tools[].description field, plus SKILL.md frontmatter name / description. Each entry is keyed by "<relative-path>#<locator>" and stored as a SHA-256 hex hash of the description's UTF-8 bytes. Findings show only the first 12 hex chars of the old/new hashes — never the description plaintext, which may itself carry injection language. --baseline and --update-baseline are mutually exclusive.

Behavior: a changed hash → AGT-02 medium (re-approval, not a hard block — legitimate edits and rug-pulls are lexically indistinguishable; if the new text also trips AGT-01, the existing critical → block derivation still escalates). A baselined entry that disappeared → info. A brand-new description not in the baseline → no AGT-02 finding (AGT-01/03/05 cover first-time surface). With no --baseline/--update-baseline, AGT-02 is inert and behavior is identical to GH-57 (no baseline = no drift check, stated explicitly rather than faked).

Trust boundary

--update-baseline is the approval action: whoever runs it declares the current descriptions trusted. argus does not custody that trust — it only records and compares hashes. Treat the baseline file as a security artifact: commit it to your own version control and review its diffs, exactly as you would review the descriptions themselves. AGT-02 answers only "did an approved description change?"; whether the new content is malicious is still AGT-01's (lexical) and GH-59's (intent-misfit) job.

PyPI rule coverage (Milestone 1)

FamilyRules
sdist install-timesetup-py-execution, setup-subprocess, setup-remote-download, setup-eval
wheel + sdistimport-time-hook (rewriting sys.modules / __builtins__ at module load)
structuralpypi-sdist-no-manifest (info)
ported from npm rules (file-content scan)credential-access, ai-context-poisoning, runtime-hook, wallet-interception
nametyposquatting against 60+ Python package names

crates.io rule coverage (Milestone 1)

FamilyRules
build.rs compile-timebuild-rs-subprocess (shells / curl / wget / scripting interpreters only — plain Command::new("rustc") is allow-listed), build-rs-network, build-rs-include-bytes (binary blob + XOR loop), xor-decryption-loop
structuralbuild-rs-execution (info), proc-macro-crate (info), embedded-binary-blob (info)
ported from npm rules (file-content scan)credential-access, ai-context-poisoning, runtime-hook
nametyposquatting against 70+ crate names

Layout

  • crates/argus-core — data types (Decision, Finding, ScanReport).
  • crates/argus-rules — static detection rules.
  • crates/argus-fetch — npm registry client.
  • crates/argus-pypi — PyPI registry client (sdist + wheel).
  • crates/argus-crates — crates.io registry client (.crate + build.rs).
  • crates/argus-go — Go module proxy client (ZIP + h1: dirhash).
  • crates/argus-nuget — NuGet v3 client (.nupkg + MSBuild/PowerShell surfaces).
  • crates/argus-maven — Maven Central client (JAR + POM/build resources).
  • crates/argus-rubygems — RubyGems client (nested .gem archive + Ruby surfaces).
  • crates/argus-composer — Packagist/Composer client (dist ZIP + Composer/PHP surfaces).
  • crates/argus-lockfile — bounded nine-format lockfile normalization and source/integrity policy; no transport or process dependency.
  • crates/argus-cli — the argus binary.

Development

Enable the git hooks once per clone so cargo fmt drift can't reach CI:

uv tool install pre-commit        # or: pipx install pre-commit
pre-commit install                # pre-commit stage: cargo fmt + file hygiene
pre-commit install -t pre-push    # pre-push stage: cargo clippy -D warnings

CI is the authoritative gate (cargo fmt --check, clippy, cargo test, argus corpus test); the hooks just give faster local feedback. Run the full local set anytime with pre-commit run --all-files.

Status

Pre-release. Argus is not yet cut as a tagged release or published to any package registry. Build it from source against main; we treat main as the shipping branch and the CHANGELOG [Unreleased] section as the current ship-list.

Capability snapshot (as of 2026-07-18):

  • M0 — rule engine + regression corpus + CI (#4, #5).
  • M1 — npm tarball fetch + safe extraction + scan (#6); PyPI sdist/wheel (#23); crates.io .crate + build.rs analysis (#24); and the completed #22 long-tail umbrella: NuGet (#49), Maven (#50), RubyGems (#51), Composer/Packagist (#52), and Go modules (#53).
  • M2 plumbing — the DSSE, Fulcio-chain, Rekor-inclusion, and OIDC identity-policy path is opt-in behind the sigstore Cargo feature (#14). The current upstream verifier rejects real npm v0.2 intoto/0.0.2 bundles, so they produce provenance-signature-invalid rather than a green Verified verdict; see docs/design/sigstore-verification.md §10.

These entries mean implemented and covered by repository tests on main. Argus remains unreleased: there is no tagged binary distribution or package registry release yet, and normal installation still requires building from source.

Detection coverage is intentionally not claimed in headline numbers without benchmark evidence — see corpus/ for the regression set the project gates on and docs/supply-chain-attacks.md for the attack catalog argus is designed against.


The Agent Infra Stack

This project is one layer of an open-source stack for running coding agents (Claude Code, Codex) as serious infrastructure. Every piece works standalone; together they close the loop:

argus is the Trust layer at install time — scan what you pull from package registries before it ever runs. Its runtime counterpart is vibeguard.

LayerProjectWhat it does
Extendclaude-skill-registryDiscover and search community Claude Code skills
ExtendspellbookCross-runtime skills for Claude Code, Codex, and multi-agent workflows
Trustargus ◀ you are hereStatic install-time scanner for eight package ecosystems (npm, PyPI, crates.io, Go, NuGet, Maven, RubyGems, Composer)
TrustvibeguardRules, hooks, and guards against hallucinated or unverified agent changes
RememberrememLocal-first persistent memory for Claude Code and Codex sessions
OrchestrateharnessRust agent orchestration platform — rules, skills, GC, observability
Routelitellm-rsHigh-performance Rust AI gateway — 100+ LLM APIs via OpenAI format
KeepkeeplineSession command center — monitor, recover, never lose agent work