Package Scanner
The Package Scanner is a cross-platform CLI for scanning dependency manifests and uploading package inventory to ShieldedStack. It is useful for seeding existing repositories, scanning projects that do not restore through the proxy, and adding scheduled CI inventory checks.
Scanner uploads are sent to the package proxy at <proxy-url>/scans. The proxy URL has no default: set it to your deployed proxy, or the scanner refuses to upload.
Install
Section titled “Install”Binaries are published for five platforms, always at the same URL for the current release:
| Platform | File |
|---|---|
| Linux (x64) | package-scanner-linux-amd64 |
| Linux (ARM64) | package-scanner-linux-arm64 |
| macOS (Intel) | package-scanner-darwin-amd64 |
| macOS (Apple Silicon) | package-scanner-darwin-arm64 |
| Windows (x64) | package-scanner-windows-amd64.exe |
Each is served from https://tools.shieldedstack.com/u/<file>, and SHA256 checksums for all five are
published together at https://tools.shieldedstack.com/r/checksums.txt.
Verify the download before you run it. The steps below do that, substituting the file for your platform.
curl -LO https://tools.shieldedstack.com/u/package-scanner-linux-amd64curl -L -o checksums.txt https://tools.shieldedstack.com/r/checksums.txtgrep package-scanner-linux-amd64 checksums.txt | sha256sum -c -sha256sum prints OK when the file matches. If it does not, stop and do not run the binary.
Then put it on your PATH:
install -m 755 package-scanner-linux-amd64 ~/.local/bin/package-scannerUse package-scanner-linux-arm64 on ARM64 machines.
curl -LO https://tools.shieldedstack.com/u/package-scanner-darwin-arm64curl -L -o checksums.txt https://tools.shieldedstack.com/r/checksums.txtgrep package-scanner-darwin-arm64 checksums.txt | shasum -a 256 -c - \ && install -m 755 package-scanner-darwin-arm64 ~/.local/bin/package-scannerUse package-scanner-darwin-amd64 on Intel machines.
Invoke-WebRequest https://tools.shieldedstack.com/u/package-scanner-windows-amd64.exe -OutFile package-scanner.exe(Get-FileHash .\package-scanner.exe -Algorithm SHA256).Hash.ToLower()Compare the printed hash against the package-scanner-windows-amd64.exe line in
https://tools.shieldedstack.com/r/checksums.txt, then move the executable somewhere on your PATH.
Pinning a build
Section titled “Pinning a build”The download URLs always serve the current release, so an unpinned CI job picks up a new binary whenever one ships. Record the checksum you verified and check it on every run, as the GitHub Actions example does. The job then fails loudly when the binary changes rather than quietly running a different one.
Deployment URL
Section titled “Deployment URL”<proxy-url> is the base URL of the package proxy, without a trailing slash. Pass it with
--proxy-url <proxy-url>, or set SHIELDEDSTACK_PROXY_URL in the environment.
Features
Section titled “Features”- Multi-ecosystem support: npm, NuGet, pip, Go, Maven, Cargo, RubyGems, Dart, Composer, Hex, Swift.
- Manifest-only scanning: Fast scans without dependency resolution.
- Concurrent processing: Parallel manifest parsing.
- Batch uploads: Efficient bulk submission to ShieldedStack.
- Retry logic: Automatic retries with exponential backoff.
- Rate limiting: Respects API rate limits.
- Dry-run mode: Preview findings without uploading.
- CI/CD ready: Non-interactive execution with clear exit codes.
Supported Manifests
Section titled “Supported Manifests”package-lock.jsonv2/v3. Scoped names such as@babel/coreare kept; workspace packages are skipped.yarn.lock, both the Yarn 1 format and the Yarn 2+ format. Only registry packages are recorded; workspace, link, file, git, and patch entries are skipped, and npm aliases are recorded under the real package name.pnpm-lock.yaml, lockfile versions 5, 6, and 9. Workspace projects, git and directory dependencies, and tarball URLs are skipped.package.jsonfallback, used only when there is nopackage-lock.json,yarn.lock, orpnpm-lock.yamlbeside it. Only exact versions such as4.19.2are recorded; ranges like^4.19.0, tags, andfile:, git, orworkspace:specifiers are skipped because they do not say which version is installed. Commit a lockfile for complete coverage.
packages.lock.json.*.csproj/*.fsproj/*.vbprojusing PackageReference, with the version as an attribute, a child element, orVersionOverride. Under central package management the version comes from the nearestDirectory.Packages.props. Version ranges and floating versions such as2.*are skipped;packages.lock.jsonrecords the restored version.packages.config.paket.lock. Every package in a NUGET section is recorded, in all groups. GITHUB, GIT, and HTTP entries pin files rather than packages and are skipped.
requirements.txt. Only exact pins (==or===) are recorded; ranges, URL, editable, and VCS requirements are skipped. Environment markers, extras, and--hashlines are handled.Pipfile.lock.poetry.lock. Git, directory, file, and URL sources are skipped; private indexes are included.uv.lock. Packages from PyPI or a private index are included; git, path, URL, and the project’s own workspace members are skipped.
Python package names are recorded in their normalized form (Flask_Login becomes flask-login), matching how the proxy records them.
go.mod.
pom.xml. A dependency without a version takes it from the same file’s<dependencyManagement>; versions that come from an imported BOM or a parent POM are not resolved, and those dependencies are skipped.gradle.lockfile, plusbuildscript-gradle.lockfileandsettings-gradle.lockfile. Gradle writes these only when dependency locking is enabled; build scripts are never evaluated.
Cargo.lock. Registry crates only; workspace members, path, and git dependencies are skipped.
RubyGems
Section titled “RubyGems”Gemfile.lock. Gems from theGEMsection only;GITandPATHgems are skipped.
pubspec.lock.
Composer
Section titled “Composer”composer.lock, coveringpackagesandpackages-dev.
mix.lock: Hex registry dependencies are included; Git and path dependencies are skipped. The scanner parses the lockfile’s literal Erlang-term subset and rejects executable or unsupported terms rather than evaluating them.rebar.lock: Hexpkgdependencies in the current Rebar3 lockfile shape are included; Git and path dependencies are skipped. Other lockfile shapes are not supported.
Package.resolved(format versions 1, 2, and 3): source-control dependencies are named by repository path, such asgithub.com/apple/swift-nio, so they match OSV and GitHub advisories. Registry dependencies keep their registry identity. Branch, revision, and local pins are skipped.Package.swiftis never evaluated. Swift is scanning-only; see Swift.
Basic Usage
Section titled “Basic Usage”package-scanner --api-key YOUR_API_KEY --proxy-url <proxy-url> /path/to/projectEnvironment Variables
Section titled “Environment Variables”export SHIELDEDSTACK_API_KEY=your_api_keyexport SHIELDEDSTACK_PROXY_URL=<proxy-url>package-scanner /path/to/projectWith the default HTTP endpoint, this looks like:
Use HTTPS in production. The API key is a credential and must not be sent over an unencrypted connection.
export SHIELDEDSTACK_PROXY_URL=http://<host>:3003package-scanner --api-key YOUR_API_KEY .Dry Run
Section titled “Dry Run”Preview findings without uploading:
package-scanner --dry-run --api-key YOUR_API_KEY --proxy-url <proxy-url> .Exclude Directories
Section titled “Exclude Directories”package-scanner --api-key YOUR_API_KEY --proxy-url <proxy-url> --exclude "test,examples" .CI/CD Integration
Section titled “CI/CD Integration”name: Scan Dependencies
on: schedule: - cron: '0 0 * * *' push: branches: [main]
jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Download Package Scanner env: SCANNER_SHA256: ${{ vars.SCANNER_SHA256 }} run: | curl -L https://tools.shieldedstack.com/u/package-scanner-linux-amd64 -o scanner echo "${SCANNER_SHA256} scanner" | sha256sum -c - chmod +x scanner
- name: Scan Dependencies env: SHIELDEDSTACK_API_KEY: ${{ secrets.SHIELDEDSTACK_API_KEY }} SHIELDEDSTACK_PROXY_URL: ${{ vars.SHIELDEDSTACK_PROXY_URL }} run: ./scanner --no-color .trigger: - main
pool: vmImage: 'ubuntu-latest'
steps:- script: | curl -L https://tools.shieldedstack.com/u/package-scanner-linux-amd64 -o scanner chmod +x scanner displayName: 'Download Package Scanner'
- script: ./scanner --no-color . env: SHIELDEDSTACK_API_KEY: $(ShieldedStackApiKey) SHIELDEDSTACK_PROXY_URL: $(ShieldedStackProxyUrl) displayName: 'Scan Dependencies'scan-dependencies: image: alpine:latest before_script: - apk add --no-cache curl - curl -L https://tools.shieldedstack.com/u/package-scanner-linux-amd64 -o scanner - chmod +x scanner script: - ./scanner --no-color . variables: SHIELDEDSTACK_PROXY_URL: "<proxy-url>" only: - mainStore SHIELDEDSTACK_API_KEY as a protected CI secret. Store SHIELDEDSTACK_PROXY_URL as a variable or secret depending on your internal policy.
Flags Reference
Section titled “Flags Reference”| Flag | Environment Variable | Description |
|---|---|---|
--api-key |
SHIELDEDSTACK_API_KEY |
ShieldedStack API key. Required for uploads. |
--proxy-url |
SHIELDEDSTACK_PROXY_URL |
Package proxy base URL. Required unless --dry-run is set. |
--include |
- | Only scan manifests matching these glob patterns, matched against the path relative to the target directory or the file name. * does not cross /. |
--exclude |
- | Additional exclude patterns. |
--dry-run |
- | Print findings without uploading. |
--output-json |
- | Write results to JSON file. |
--format |
- | Output format: text or json. |
--no-color |
- | Disable colored output. |
--concurrency |
- | Worker pool size. |
Do not rely on the scanner binary’s built-in proxy URL default for on-prem deployments. Set SHIELDEDSTACK_PROXY_URL or pass --proxy-url explicitly.
Default Excludes
Section titled “Default Excludes”The scanner automatically excludes these directories:
node_modulesbinobj.venv/venv.git__pycache__.pytest_cache.mypy_cache
Exit Codes
Section titled “Exit Codes”0: Success; all packages uploaded.1: Upload errors such as network, auth, or rate-limit failures.2: Scan failures such as manifest parsing errors.
Monorepo Support
Section titled “Monorepo Support”For monorepos with multiple projects or workspaces, run the scanner multiple times with different API keys and target directories:
# Frontend projectpackage-scanner --api-key FRONTEND_KEY --proxy-url <proxy-url> ./frontend
# Backend projectpackage-scanner --api-key BACKEND_KEY --proxy-url <proxy-url> ./backendTroubleshooting
Section titled “Troubleshooting”Authentication Errors
Section titled “Authentication Errors”Verify your API key is correct, has not expired, and belongs to the workspace you expect:
package-scanner --dry-run --api-key YOUR_API_KEY --proxy-url <proxy-url> .Then run without --dry-run to test upload authentication.
Uploads Go to the Wrong Host
Section titled “Uploads Go to the Wrong Host”Confirm the active proxy URL:
printf '%s\n' "$SHIELDEDSTACK_PROXY_URL"The scanner posts inventory to <proxy-url>/scans.
Rate Limiting
Section titled “Rate Limiting”If you hit rate limits with 429 responses, the scanner automatically retries with backoff. Current limit: 5,000 packages/hour per API key.
Parsing Errors
Section titled “Parsing Errors”Use JSON output to see which manifests fail to parse:
package-scanner --format json --api-key YOUR_API_KEY --proxy-url <proxy-url> . 2>&1 | tee scan.log