omni-line/omni-audit

🛡️ A fast, zero-config CLI to audit your projects for Dependency Confusion and supply chain risks across multiple registries. Backed by Omni Line.

Go

2

10 commits

updated Sep 30, 2026

See the code

See what people are saying

README

Omni Audit

License: MIT CI Powered by Omni Line

Omni Audit is a fast, zero-config CLI that scans your project for dependency confusion risk. It discovers NPM, Composer, PyPI, and Go manifests, checks whether each declared package name exists on the public registry, and reports names that are still unclaimed — names an attacker could publish.

Distributed as a standalone Go binary. No Node or PHP runtime required.

The problem

If your team uses private packages alongside public registries (npmjs.com, Packagist, PyPI, proxy.golang.org), a build can resolve a malicious public package that reuses an internal name. Omni Audit flags unclaimed public names before they are hijacked.

Auditing is the first step. Omni Line is the durable fix: a self-hosted registry that routes internal packages correctly across ecosystems.

Install

Prebuilt binaries

Download the latest release from GitHub Releases.

From source

Requires Go 1.20+:

go install github.com/omni-line/omni-audit/cmd/omni-audit@latest

Or clone and build:

git clone https://github.com/omni-line/omni-audit.git
cd omni-audit
make build
./bin/omni-audit --help

Quick start

# Scan the current directory
omni-audit

# Scan a path
omni-audit ./apps/api

# Scan a single manifest
omni-audit ./services/billing/requirements.txt

# JSON for CI
omni-audit --format json --no-marketing

# SARIF for GitHub code scanning / security dashboards
omni-audit --format sarif > omni-audit.sarif

# Skip names you own and fixture directories
omni-audit --safe-namespace '@acme/*,acme/*' --exclude testdata,fixtures

Example text output (colors when the terminal supports them):

omni-audit v0.3.0 — Dependency confusion audit
Backed by Omni Line — one registry for every package your team ships

âś— 2 unclaimed package names found

  ECOSYSTEM  PACKAGE               VERSION  LOCATION              SECTION
  composer   acme/internal-sdk     ^1.0     composer.json:7       require
  npm        @acme/internal-utils  1.0.0    package.json:5        dependencies

Anyone can publish these names on the public registry. If a build resolves
them there instead of your private source, it installs the publisher's code.

How to fix
  composer  Register the vendor name on Packagist so nobody else can publish under it, ...
  npm       Claim the name (or its @scope as an npm organization) on npmjs.com, ...

Scanned 2 manifests · 8 packages · 2 findings · 0 skipped · 0 errors in 412ms

───
Unclaimed names can be published by anyone on the public registry.
Prevent confusion at install time with Omni Line — a self-hosted package
registry for npm, Composer, Docker, PyPI, Go, Cargo, Maven, and more.
One UI, one API, your infrastructure.
https://omniline.app  ·  docs: https://omniline.app/docs

How it works

  1. Walks the tree for package.json, composer.json, requirements*.txt, requirements/*.txt, pyproject.toml, and go.mod (skips node_modules, vendor, .venv, venv, __pycache__, .git, dist, build, and other dependency/cache dirs, plus anything matched by --exclude)
  2. Collects declared dependencies with their section and line number:
    • npm: dependencies / devDependencies / optionalDependencies / peerDependencies. Local and VCS specs (file:, workspace:, link:, git URLs, user/repo) are skipped; aliases (npm:real-pkg@^1) are checked under the real name.
    • Composer: require / require-dev, skipping platform packages (php, ext-*, lib-*, composer-plugin-api, …).
    • PyPI: requirements files (comments, markers, extras, line continuations) and pyproject.toml PEP 621 [project] dependencies / optional-dependencies plus PEP 735 [dependency-groups]. Names are compared using PEP 503 normalization.
    • Go: require directives in go.mod (including // indirect). Paths must look like public module paths (first element contains a .). Checked via proxy.golang.org.
  3. Checks each distinct name once per ecosystem against the public npm registry, Packagist, PyPI, and the Go module proxy using lightweight HEAD requests, with retries and backoff for rate limits and transient errors
  4. Reports names that return 404 as reason=unclaimed; checks that fail are reported as warnings, never as clean

Security properties

  • Only package names are sent, and only to the public registries above (HTTPS, TLS 1.2+, no HTTPS→HTTP redirects). Names that are not valid for the registry are never sent.
  • Manifests are read with a 10 MiB cap; FIFOs/devices and symlinks that resolve outside the scan root are skipped.
  • Values from scanned files are escaped before being printed, so a crafted manifest cannot inject terminal escape sequences.
  • HTTPS_PROXY / NO_PROXY are honored for locked-down networks.

Exit codes

CodeMeaning
0No findings (or --fail-on none)
1One or more findings (--fail-on any, default)
2Usage or runtime error, or an incomplete scan with --strict

Without --strict, registry failures and unreadable manifests are reported as warnings and do not change the exit code. Use --strict in CI when an unverified package should block the pipeline.

Flags

FlagDescription
--format text|json|sarifOutput format (default text)
--safe-namespaceGlobs for namespaces you own; matches are skipped (repeatable / comma-separated)
--ignoreSkip package name globs
--excludeSkip paths: directory/file names or globs relative to the scan root
--strictExit 2 if any manifest or package could not be verified
--timeoutPer-request timeout (default 10s)
--retriesRetries for 429 / 5xx / network errors (default 2, max 10)
--concurrencyParallel checks (default 16, max 256)
--fail-on any|noneWhether findings fail the process
-q / --quietFindings table only; no banner, warnings, summary, or marketing
--no-marketingHide Omni Line CTA / JSON sponsor
--marketingForce marketing even when non-TTY
--color auto|always|neverANSI colors (default auto on TTY)
-v / --verboseShow package URLs and all warnings
--versionPrint version

JSON output

{
  "schema_version": 1,
  "version": "0.3.0",
  "complete": true,
  "findings": [
    {
      "ecosystem": "npm",
      "package": "@acme/internal-utils",
      "version": "1.0.0",
      "manifest": "package.json",
      "line": 5,
      "group": "dependencies",
      "reason": "unclaimed",
      "registry": "registry.npmjs.org",
      "url": "https://www.npmjs.com/package/@acme/internal-utils",
      "remediation": "Claim the name (or its @scope as an npm organization) ..."
    }
  ],
  "stats": { "manifests": 1, "packages": 4, "unique_packages": 4, "findings": 1, "skipped": 0, "errors": 0, "duration_ms": 412 }
}

complete is false when any warning was raised (see warnings[], each with a kind of walk, manifest, or registry). New fields may be added; breaking changes bump schema_version.

Environment:

  • OMNI_AUDIT_NO_MARKETING=1 — same as --no-marketing (handy in CI)
  • NO_COLOR=1 — disable colors (no-color.org)
  • FORCE_COLOR=1 — enable colors when not a TTY

JSON includes an optional top-level sponsor object by default. Use --no-marketing or -q for a silent machine payload.

CI example

- name: Dependency confusion audit
  run: |
    curl -sL https://github.com/omni-line/omni-audit/releases/latest/download/omni-audit_Linux_x86_64.tar.gz | tar xz
    ./omni-audit --format json --no-marketing --strict --safe-namespace '@your-org/*'

GitHub code scanning (findings appear as alerts with file/line annotations):

- name: Dependency confusion audit
  run: ./omni-audit --format sarif --fail-on none > omni-audit.sarif
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: omni-audit.sarif

Run from the repository root so SARIF paths are repository-relative.

Roadmap

  • Additional ecosystems (Maven, Cargo)
  • Lockfile / .npmrc / auth.json / pip.conf / GOPRIVATE policy analysis
  • Optional Omni Line registry URL to verify private existence
  • Poetry/Pipfile table-style dependency maps (requirements + PEP 621 covered today)

Contributing

See CONTRIBUTING.md, MAINTAINERS.md, and CODE_OF_CONDUCT.md. Security reports: SECURITY.md.

License

MIT · Copyright Omni Line and contributors · See NOTICE.


Omni Line

Omni Audit is built and maintained by Omni Line — one self-hosted registry for every package your team ships.

cli
composer
dependency-confusion
golang
npm
security
supply-chain

omni-line/omni-audit

🛡️ A fast, zero-config CLI to audit your projects for Dependency Confusion and supply chain risks across multiple registries. Backed by Omni Line.

Go

2

10 commits

updated Sep 30, 2026

See the code

See what people are saying

README

Omni Audit

License: MIT CI Powered by Omni Line

Omni Audit is a fast, zero-config CLI that scans your project for dependency confusion risk. It discovers NPM, Composer, PyPI, and Go manifests, checks whether each declared package name exists on the public registry, and reports names that are still unclaimed — names an attacker could publish.

Distributed as a standalone Go binary. No Node or PHP runtime required.

The problem

If your team uses private packages alongside public registries (npmjs.com, Packagist, PyPI, proxy.golang.org), a build can resolve a malicious public package that reuses an internal name. Omni Audit flags unclaimed public names before they are hijacked.

Auditing is the first step. Omni Line is the durable fix: a self-hosted registry that routes internal packages correctly across ecosystems.

Install

Prebuilt binaries

Download the latest release from GitHub Releases.

From source

Requires Go 1.20+:

go install github.com/omni-line/omni-audit/cmd/omni-audit@latest

Or clone and build:

git clone https://github.com/omni-line/omni-audit.git
cd omni-audit
make build
./bin/omni-audit --help

Quick start

# Scan the current directory
omni-audit

# Scan a path
omni-audit ./apps/api

# Scan a single manifest
omni-audit ./services/billing/requirements.txt

# JSON for CI
omni-audit --format json --no-marketing

# SARIF for GitHub code scanning / security dashboards
omni-audit --format sarif > omni-audit.sarif

# Skip names you own and fixture directories
omni-audit --safe-namespace '@acme/*,acme/*' --exclude testdata,fixtures

Example text output (colors when the terminal supports them):

omni-audit v0.3.0 — Dependency confusion audit
Backed by Omni Line — one registry for every package your team ships

âś— 2 unclaimed package names found

  ECOSYSTEM  PACKAGE               VERSION  LOCATION              SECTION
  composer   acme/internal-sdk     ^1.0     composer.json:7       require
  npm        @acme/internal-utils  1.0.0    package.json:5        dependencies

Anyone can publish these names on the public registry. If a build resolves
them there instead of your private source, it installs the publisher's code.

How to fix
  composer  Register the vendor name on Packagist so nobody else can publish under it, ...
  npm       Claim the name (or its @scope as an npm organization) on npmjs.com, ...

Scanned 2 manifests · 8 packages · 2 findings · 0 skipped · 0 errors in 412ms

───
Unclaimed names can be published by anyone on the public registry.
Prevent confusion at install time with Omni Line — a self-hosted package
registry for npm, Composer, Docker, PyPI, Go, Cargo, Maven, and more.
One UI, one API, your infrastructure.
https://omniline.app  ·  docs: https://omniline.app/docs

How it works

  1. Walks the tree for package.json, composer.json, requirements*.txt, requirements/*.txt, pyproject.toml, and go.mod (skips node_modules, vendor, .venv, venv, __pycache__, .git, dist, build, and other dependency/cache dirs, plus anything matched by --exclude)
  2. Collects declared dependencies with their section and line number:
    • npm: dependencies / devDependencies / optionalDependencies / peerDependencies. Local and VCS specs (file:, workspace:, link:, git URLs, user/repo) are skipped; aliases (npm:real-pkg@^1) are checked under the real name.
    • Composer: require / require-dev, skipping platform packages (php, ext-*, lib-*, composer-plugin-api, …).
    • PyPI: requirements files (comments, markers, extras, line continuations) and pyproject.toml PEP 621 [project] dependencies / optional-dependencies plus PEP 735 [dependency-groups]. Names are compared using PEP 503 normalization.
    • Go: require directives in go.mod (including // indirect). Paths must look like public module paths (first element contains a .). Checked via proxy.golang.org.
  3. Checks each distinct name once per ecosystem against the public npm registry, Packagist, PyPI, and the Go module proxy using lightweight HEAD requests, with retries and backoff for rate limits and transient errors
  4. Reports names that return 404 as reason=unclaimed; checks that fail are reported as warnings, never as clean

Security properties

  • Only package names are sent, and only to the public registries above (HTTPS, TLS 1.2+, no HTTPS→HTTP redirects). Names that are not valid for the registry are never sent.
  • Manifests are read with a 10 MiB cap; FIFOs/devices and symlinks that resolve outside the scan root are skipped.
  • Values from scanned files are escaped before being printed, so a crafted manifest cannot inject terminal escape sequences.
  • HTTPS_PROXY / NO_PROXY are honored for locked-down networks.

Exit codes

CodeMeaning
0No findings (or --fail-on none)
1One or more findings (--fail-on any, default)
2Usage or runtime error, or an incomplete scan with --strict

Without --strict, registry failures and unreadable manifests are reported as warnings and do not change the exit code. Use --strict in CI when an unverified package should block the pipeline.

Flags

FlagDescription
--format text|json|sarifOutput format (default text)
--safe-namespaceGlobs for namespaces you own; matches are skipped (repeatable / comma-separated)
--ignoreSkip package name globs
--excludeSkip paths: directory/file names or globs relative to the scan root
--strictExit 2 if any manifest or package could not be verified
--timeoutPer-request timeout (default 10s)
--retriesRetries for 429 / 5xx / network errors (default 2, max 10)
--concurrencyParallel checks (default 16, max 256)
--fail-on any|noneWhether findings fail the process
-q / --quietFindings table only; no banner, warnings, summary, or marketing
--no-marketingHide Omni Line CTA / JSON sponsor
--marketingForce marketing even when non-TTY
--color auto|always|neverANSI colors (default auto on TTY)
-v / --verboseShow package URLs and all warnings
--versionPrint version

JSON output

{
  "schema_version": 1,
  "version": "0.3.0",
  "complete": true,
  "findings": [
    {
      "ecosystem": "npm",
      "package": "@acme/internal-utils",
      "version": "1.0.0",
      "manifest": "package.json",
      "line": 5,
      "group": "dependencies",
      "reason": "unclaimed",
      "registry": "registry.npmjs.org",
      "url": "https://www.npmjs.com/package/@acme/internal-utils",
      "remediation": "Claim the name (or its @scope as an npm organization) ..."
    }
  ],
  "stats": { "manifests": 1, "packages": 4, "unique_packages": 4, "findings": 1, "skipped": 0, "errors": 0, "duration_ms": 412 }
}

complete is false when any warning was raised (see warnings[], each with a kind of walk, manifest, or registry). New fields may be added; breaking changes bump schema_version.

Environment:

  • OMNI_AUDIT_NO_MARKETING=1 — same as --no-marketing (handy in CI)
  • NO_COLOR=1 — disable colors (no-color.org)
  • FORCE_COLOR=1 — enable colors when not a TTY

JSON includes an optional top-level sponsor object by default. Use --no-marketing or -q for a silent machine payload.

CI example

- name: Dependency confusion audit
  run: |
    curl -sL https://github.com/omni-line/omni-audit/releases/latest/download/omni-audit_Linux_x86_64.tar.gz | tar xz
    ./omni-audit --format json --no-marketing --strict --safe-namespace '@your-org/*'

GitHub code scanning (findings appear as alerts with file/line annotations):

- name: Dependency confusion audit
  run: ./omni-audit --format sarif --fail-on none > omni-audit.sarif
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: omni-audit.sarif

Run from the repository root so SARIF paths are repository-relative.

Roadmap

  • Additional ecosystems (Maven, Cargo)
  • Lockfile / .npmrc / auth.json / pip.conf / GOPRIVATE policy analysis
  • Optional Omni Line registry URL to verify private existence
  • Poetry/Pipfile table-style dependency maps (requirements + PEP 621 covered today)

Contributing

See CONTRIBUTING.md, MAINTAINERS.md, and CODE_OF_CONDUCT.md. Security reports: SECURITY.md.

License

MIT · Copyright Omni Line and contributors · See NOTICE.


Omni Line

Omni Audit is built and maintained by Omni Line — one self-hosted registry for every package your team ships.

cli
composer
dependency-confusion
golang
npm
security
supply-chain

Languages

Go

99.4%