{"id":49675537,"url":"https://github.com/qwexvf/aegis-cli","last_synced_at":"2026-05-16T14:02:04.830Z","repository":{"id":355336039,"uuid":"1227690490","full_name":"qwexvf/aegis-cli","owner":"qwexvf","description":"Supply-chain security CLI for npm/bun/yarn/pnpm — install gate + lockfile snapshots + AST risk scoring","archived":false,"fork":false,"pushed_at":"2026-05-11T03:39:51.000Z","size":1257,"stargazers_count":1,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T05:15:53.542Z","etag":null,"topics":["ast-analysis","cli","cve","dependency-scanner","devsecops","ghsa","golang","lockfile","malware-detection","npm-security","osv","python-security","sbom","sca","security","supply-chain-attack","supply-chain-security","tree-sitter","typosquatting","vulnerability-scanner"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qwexvf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-03T03:05:19.000Z","updated_at":"2026-05-11T03:39:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/qwexvf/aegis-cli","commit_stats":null,"previous_names":["qwexvf/aegis-cli"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/qwexvf/aegis-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Faegis-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Faegis-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Faegis-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Faegis-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qwexvf","download_url":"https://codeload.github.com/qwexvf/aegis-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Faegis-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33105712,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-16T04:41:52.686Z","status":"ssl_error","status_checked_at":"2026-05-16T04:41:52.009Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast-analysis","cli","cve","dependency-scanner","devsecops","ghsa","golang","lockfile","malware-detection","npm-security","osv","python-security","sbom","sca","security","supply-chain-attack","supply-chain-security","tree-sitter","typosquatting","vulnerability-scanner"],"created_at":"2026-05-07T02:03:24.930Z","updated_at":"2026-05-16T14:02:04.824Z","avatar_url":"https://github.com/qwexvf.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# aegis\n\n[![CI](https://github.com/qwexvf/aegis-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/qwexvf/aegis-cli/actions/workflows/ci.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/qwexvf/aegis-cli)](https://goreportcard.com/report/github.com/qwexvf/aegis-cli)\n[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)\n\nSupply-chain security scanner for 16 package ecosystems and GitHub Actions workflows. No account, no API key, no backend.\n\n![demo](docs/demo.gif)\n\n- **CVE / GHSA lookup** — batch query against [OSV.dev](https://osv.dev), 16 ecosystems in one shot; every finding carries `FixedIn` (the smallest version that resolves it)\n- **EPSS + KEV enrichment** — every CVE gets the FIRST.org Exploit Prediction Scoring System probability; `[KEV]` badge when the CVE is in CISA's Known Exploited Vulnerabilities catalog (actively exploited right now)\n- **AST capability scan** — tree-sitter walks every package source; surfaces `shell-spawn`, `net-egress`, `dynamic-eval`, `fs-write-outside-root` and more, even on packages with no advisory yet\n- **Taint analysis** — constant folding evaluates `String.fromCharCode([104,116,...])` arrays to detect obfuscated C2 hostnames; variable taint tracking catches `atob(x) → eval(x)` patterns that bypass simple pattern matching\n- **Hardcoded secrets in dep source** — detects AWS AKIA/ASIA keys, GitHub tokens, npm tokens, PEM private keys, Stripe / SendGrid / Twilio keys, Slack bot tokens, Bearer tokens ≥ 40 chars embedded in package source — no legitimate dep ships real credentials\n- **Behavior heuristics** — postinstall hooks doing `curl|sh`, obfuscated payloads, typosquat names (Levenshtein distance 2), maintainer hijack patterns, patch-version capability drift, git-SHA optional deps (worm propagation vector), unlisted large files (smuggled payloads), yanked-version detection\n- **Symbol-level reachability** — when OSV publishes `affected[].ecosystem_specific.functions` for an advisory, aegis cross-references against the user code's `UsedSymbols` and suppresses advisories where the vulnerable function is provably never called\n- **OpenVEX suppression** — `aegis ci --vex project.vex` loads a VEX document and clears `not_affected` advisories from verdict scoring (still shown greyed-out in output, omitted from JSON)\n- **License policy gate** — `aegis ci --deny-licenses GPL-3.0,AGPL-3.0` or `--allow-licenses MIT,Apache-2.0` enforce SPDX policy; unknown license blocks under allow-list mode\n- **Package health** — `deprecated` flag fetched from deps.dev for npm / PyPI / Cargo / Go / Maven / NuGet; `aegis ci --fail-on-deprecated` opt-in gate\n- **Guided remediation** — `aegis fix` picks the highest `FixedIn` per dep (the smallest single upgrade that clears every resolvable CVE) and emits ecosystem-appropriate upgrade commands; `--script` pipes straight to `sh`\n- **npm provenance** — fetches SLSA attestations from the npm registry during `snapshot enrich`; extracts source repo + git commit from SLSA v1 predicates; flags packages with no attestation as an informational risk signal\n- **Transitive deps included** — lockfile-based; every resolved package is scanned, not just direct deps\n- **Polyglot monorepo** — finds all lockfiles, merges into a single `aegis.lock`\n- **CycloneDX + SPDX SBOMs** — `aegis sbom` emits CycloneDX 1.5/1.6 or SPDX 2.3 JSON; `--include-vulns` attaches OSV advisories; package licenses populated from registries; `--attest` produces in-toto attestations via cosign\n- **GitHub Actions scanner** — `aegis actions scan` walks `.github/workflows/*.yml` (or fetches from a remote repo with `--repo owner/repo`); flags unpinned action refs, `pull_request_target` + checkout escalation, OIDC + npm publish worm vector, `actions/cache` poisoning, script injection, `curl|sh` in `run:` blocks, and `permissions: write-all`; outputs SARIF 2.1.0 with `--sarif` for GitHub Code Scanning\n- **Offline capable** — `AEGIS_NO_VULN_LOOKUP=1` for air-gapped use; self-hosted OSV mirror via `AEGIS_OSV_URL`\n\n## Ecosystems\n\n| Ecosystem     | Lockfiles                                                             | OSV | AST scanner  |\n|---------------|-----------------------------------------------------------------------|-----|--------------|\n| **npm**       | `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lock`       | ✅  | ✅ `js`      |\n| **PyPI**      | `poetry.lock`, `uv.lock`, `Pipfile.lock`, `requirements.txt`         | ✅  | ✅ `py`      |\n| **RubyGems**  | `Gemfile.lock`                                                        | ✅  | ✅ `ruby`    |\n| **crates.io** | `Cargo.lock`                                                          | ✅  | ✅ `rust`    |\n| **Go**        | `go.sum` / `go.mod`                                                   | ✅  | ✅ `golang`  |\n| **Maven**     | `pom.xml`, `gradle.lockfile`                                          | ✅  | ✅ `java`    |\n| **Packagist** | `composer.lock`                                                       | ✅  | ✅ `php`     |\n| **NuGet**     | `packages.lock.json`                                                  | ✅  | ✅ `csharp`  |\n| **Hex**       | `manifest.toml` (Gleam), `mix.lock` (Elixir)                         | ✅  | ✅ `gleam`   |\n| **Pub**       | `pubspec.lock`                                                        | ✅  | ✅           |\n| **SwiftURL**  | `Package.resolved`                                                    | ✅  | ✅           |\n| **CRAN**      | `renv.lock`                                                           | ✅  | ✅           |\n| **Hackage**   | `cabal.project.freeze`, `stack.yaml.lock`                             | ✅  | ✅           |\n| **CPAN**      | `cpanfile.snapshot`                                                   | ✅  | ✅           |\n| **CocoaPods** | `Podfile.lock`                                                        | ✅  | ✅           |\n\n## Install\n\n```sh\ngo install github.com/qwexvf/aegis-cli/cmd/aegis@latest\n```\n\nOne all-in-one binary — every PM wrapper and the AST scanner ship together.\nRequires Go 1.26+.\n\nPre-built linux/amd64 binary on [Releases](https://github.com/qwexvf/aegis-cli/releases),\ncosign-signed with SLSA build provenance:\n\n```sh\ncosign verify-blob \\\n  --certificate-identity-regexp 'https://github.com/qwexvf/aegis-cli/.github/workflows/release.yml.*' \\\n  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \\\n  --certificate checksums.txt.pem \\\n  --signature   checksums.txt.sig \\\n  checksums.txt\nsha256sum -c checksums.txt\n```\n\n## Usage\n\n```sh\n# snapshot the lockfile and scan\naegis snapshot save                  # parse lockfile → aegis.lock\naegis snapshot enrich                # AST scan + CVE lookup\naegis snapshot show                  # direct deps\naegis snapshot show --all            # + transitive\naegis snapshot diff baseline.lock    # drift between two snapshots\naegis snapshot rescan                # re-query OSV for new CVEs on saved deps\n\n# CI gate — exits 1 on findings ≥ threshold\naegis ci --fail-on=block\naegis ci --fail-on=prompt --json                          # machine-readable\naegis ci --deny-licenses=GPL-3.0,AGPL-3.0                 # license policy\naegis ci --vex project.vex                                # suppress not_affected CVEs\naegis ci --fail-on-deprecated                             # treat deprecated deps as findings\n\n# guided remediation — emit upgrade commands that clear every fixable CVE\naegis fix                                                 # human-readable plan\naegis fix --json                                          # tooling integration\naegis fix --script | sh                                   # apply directly\n\n# analyze a package ad hoc (fetches from registry)\naegis analyze lodash@4.17.21\naegis analyze --evidence ua-parser-js@0.7.29\n\n# analyze a local source tree (no registry fetch)\naegis analyze rubygems/rest-client@1.6.13 \\\n    --local examples/incidents/rubygems/rest-client-1.6.13/\n\n# SBOM export — CycloneDX or SPDX, with optional in-toto attestation\naegis sbom \u003e sbom.json\naegis sbom --format=spdx -o sbom.spdx.json\naegis sbom --include-vulns --pretty -o sbom.json\naegis sbom --format=cyclonedx -o sbom.json --attest      # in-toto via cosign\n\n# allowlist\naegis allowlist add lodash \\\n    --capability=dynamic-eval \\\n    --version='^4' \\\n    --reason='_.template uses Function() to compile templates'\naegis allowlist list\naegis allowlist test npm/lodash@4.17.21\naegis allowlist verify\n\n# GitHub Actions workflow scanner\naegis actions scan                   # scan .github/workflows/ in cwd\naegis actions scan --fail-on=high    # exit 1 on high/critical findings\naegis actions scan --json            # machine-readable output\n\n# shell completion\naegis completion bash \u003e /etc/bash_completion.d/aegis\naegis completion zsh  \u003e \"${fpath[1]}/_aegis\"\naegis completion fish \u003e ~/.config/fish/completions/aegis.fish\n```\n\n## How it works\n\n1. **Parse** — lockfile → every resolved `(name, version)`, direct and transitive\n2. **Fetch** — tarballs from the registry; cached under `~/.aegis/cache/sources/`\n3. **AST scan** — tree-sitter walks each file; emits `capability:file:line:snippet` evidence\n4. **Heuristics** — behavior-based detectors over the tarball and registry metadata (no network beyond step 2):\n   - install hooks doing `curl|sh`, `bun run \u003cfile\u003e \u0026\u0026 exit 1`, and similar download-execute patterns\n   - `optionalDependencies` pointing at git SHA commits (worm-propagation injection vector)\n   - VCS dependencies (`git+https://`, `git = \"...\"`, `:git =\u003e`) across PyPI, Cargo, RubyGems, Go, Composer, Gleam — bypasses registry immutability\n   - unlisted large code files (≥512 KB not in `files` field — smuggled payload shape)\n   - confirmed-malware IOC filenames (`router_init.js`, `router_runtime.js`, `tanstack_runner.js`)\n   - yanked versions: lockfile pinning a version removed from the registry flags users who installed during an incident window\n   - maintainer hijack: publisher change between consecutive releases, fresh publish on abandoned package\n   - tarball drift: files in the published tarball absent from the upstream git tag (requires GitHub access)\n   - typosquat: name within Levenshtein distance 2 of a top-1000 package\n5. **CVE lookup** — batch POST to OSV.dev; severity cached under `~/.aegis/cache/advisories/`\n6. **Allowlist** — builtin → `~/.aegis/allowlist.yaml` → `.aegis-allowlist.yaml`; specific beats wildcard\n7. **Verdict** — `max(ast, advisory)` vs `--fail-on`; Critical/High → `block`, Medium → `prompt`, Low → `review`\n\n## Allowlist\n\n```yaml\n# .aegis-allowlist.yaml — commit this for team-shared suppressions\nversion: 1\nrules:\n  - ecosystem: npm\n    name: lodash\n    version: \"^4\"\n    capability: dynamic-eval\n    reason: \"_.template uses Function() to compile templates\"\n```\n\nThree layers, in match order: builtin (~20 curated rules) → user (`~/.aegis/allowlist.yaml`) → project (`.aegis-allowlist.yaml`).\n\n## GitHub Actions Workflow Scanner\n\n```bash\n# Scan local workflows\naegis actions scan\n\n# Scan a remote repository (uses $GITHUB_TOKEN)\naegis actions scan --repo owner/repo\n\n# Emit SARIF for GitHub Code Scanning\naegis actions scan --sarif \u003e results.sarif\n```\n\nSuppress known-safe findings with `.aegis-actions-allowlist.yaml`:\n\n```yaml\nversion: 1\nrules:\n  - kind: unpinned_ref\n    file: .github/workflows/release.yml\n    reason: \"pinned via dependabot\"\n  - kind: write_all_permissions\n    reason: \"covered by network policy\"\n```\n\nUpload to GitHub Security tab:\n\n```yaml\n- run: aegis actions scan --sarif \u003e aegis.sarif\n- uses: github/codeql-action/upload-sarif@v3\n  with:\n    sarif_file: aegis.sarif\n```\n\n## CI\n\nDrop-in templates in [`examples/ci/`](examples/ci/) for GitHub Actions, GitLab CI, and generic shell.\n\n| Exit code | Meaning |\n|-----------|---------|\n| `0`       | clean — no findings ≥ `--fail-on` |\n| `1`       | findings ≥ `--fail-on` |\n| `2`       | verdict failed (config / network error) |\n\n**One-step full audit (packages + GitHub Actions workflows):**\n\n```yaml\n- name: aegis full audit\n  run: aegis ci --scan-actions --sarif \u003e aegis.sarif\n\n- name: Upload SARIF to GitHub Security\n  uses: github/codeql-action/upload-sarif@v3\n  with:\n    sarif_file: aegis.sarif\n  if: always()\n```\n\n| Flag | Description |\n|---|---|\n| `--scan-actions` | Also scan `.github/workflows/` (same checks as `aegis actions scan`) |\n| `--sarif` | Emit SARIF 2.1.0 for GitHub Code Scanning |\n| `--fail-on` | Threshold: `safe\\|review\\|prompt\\|block` (default: `block`) |\n| `--baseline` | Drift mode — only fail on newly-introduced findings |\n\n`aegis ci --json` output is stable for tooling — see [`examples/ci/README.md`](examples/ci/README.md).\n\n## Docs\n\nFull docs: **[qwexvf.github.io/aegis-cli](https://qwexvf.github.io/aegis-cli/)**\n\n- [Getting started](https://qwexvf.github.io/aegis-cli/getting-started/)\n- [Command reference](https://qwexvf.github.io/aegis-cli/reference/commands/)\n- [Cookbook](https://qwexvf.github.io/aegis-cli/guides/cookbook/)\n- [Architecture](https://qwexvf.github.io/aegis-cli/contributing/architecture/)\n- [CHANGELOG.md](CHANGELOG.md)\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md). Open an issue before a non-trivial PR.\nVulnerability reports: [GitHub Private Vulnerability Reporting](https://github.com/qwexvf/aegis-cli/security/advisories/new) — not public issues.\nMaintainers cutting a release: [RELEASING.md](RELEASING.md).\n\n## License\n\n[Apache-2.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqwexvf%2Faegis-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqwexvf%2Faegis-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqwexvf%2Faegis-cli/lists"}