@fossity/probe-cli
v0.1.4
Published
Captures fingerprints and dependency data from your code, without shipping your code.
Maintainers
Readme
Probe CLI
A command-line and terminal-UI code auditing probe. It fingerprints a codebase, reads its declared dependencies from manifests and lockfiles, counts files, optionally obfuscates paths, and writes a single encrypted package for an auditor.
It succeeds the Fossity Probe desktop application, which is discontinued.
Your source code never leaves the machine: the package contains winnowing fingerprints, package URLs and counts, not file contents.
$ probe-cli scan .
✔ Indexing files (1/1)
✔ Fingerprinting source (1/5)
✔ Analyzing dependencies (2/5)
✔ Counting files (3/5)
✔ Obfuscating paths (4/5)
✔ Attaching files (5/5)
Scan complete
files seen 1284
fingerprinted 902
filtered out 382
dependency files 11 (1043 purls)
from lockfiles 806 purls npm:612 pypi:98 cargo:96
→ /home/you/acme-app.fossity (612.4 KB)Install
Pick whichever is easiest — none of them needs a checkout.
Standalone binary (no Node required)
# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/fossity/probe-cli/main/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/fossity/probe-cli/main/install.ps1 | iexAlready have Node 18+?
npx fossity@latest scan . # runs without installing anything
npm install -g @fossity/probe-cli # or install it permanentlyfossity publishes the same build as @fossity/probe-cli, under a shorter name.
npx resolves the newest version from the registry on each run, so the bare form is current too;
@latest only makes that explicit. Two cases do reuse an older copy: asking for one by version
(npx [email protected] keeps using 0.1.0, as it should), and running offline, where npx falls back
to its cache. rm -rf ~/.npm/_npx clears it if you ever need to.
An installed copy is different: neither the binary nor a global npm install updates itself. Check
with probe-cli --version against the latest release,
and re-run the install command to update.
Or download a binary directly from the releases page
— one file per platform, gunzip and run. Verify it against the published SHA256SUMS.
Unsigned binaries
The binaries carry no Developer ID or Authenticode certificate. What that means in practice:
- The install scripts above are unaffected. macOS attaches its quarantine flag in the browser,
not in
curl, and Windows marks downloads the same way, so neither one-liner triggers a warning. - Downloading from the releases page in a browser does trigger one. On macOS, right-click the
file and choose Open, or run
xattr -d com.apple.quarantine <file>. On Windows, choose More info then Run anyway. npxis unaffected — no operating-system gatekeeping applies to it at all.
Check the published SHA256SUMS if you want to verify what you downloaded; it is a stronger check
than a code signature, since it is produced by the same public build that produced the binary.
Use
Run it with no arguments for the guided flow: project information, obfuscation, then a summary to confirm before anything is written.
probe-cliOr drive it non-interactively, which is what you want in CI:
probe-cli scan . --email [email protected] --license MIT -o audit.fossityCommands
| Command | What it does |
| ------------------------- | ---------------------------------------------------------- |
| probe-cli | guided terminal wizard |
| probe-cli scan [folder] | fingerprint a folder and write the package |
| probe-cli deps [folder] | list the dependencies found, write nothing |
| probe-cli formats | list every manifest/lockfile format this build understands |
| probe-cli workspace | print the working directory used for intermediate files |
Useful flags
| Flag | Effect |
| ---------------------- | ------------------------------------------------------------------------------------ |
| -e, --email <email> | contact email — the auditor requires it |
| -o, --output <file> | where the package goes |
| -l, --license <spdx> | default license of your own code |
| --obfuscate <words> | comma-separated words to strip from every path in the package |
| --sbom <file> | attach a known-components SBOM |
| --raw | write a plain .zip instead of an encrypted package, to review what you are sending |
| --json | machine-readable result, for CI |
| --verbose | mirror the scan log to stderr |
Reviewing what you send
Every scan leaves the package contents in the clear next to the package itself:
audit.fossity the encrypted package, for the auditor
audit.fossity.contents/ the same files, readableThe package is encrypted to the auditor's public key, so you cannot open it after it is written.
That directory is how you check what leaves your machine — it is byte for byte what the package
contains, and a test enforces that. Read dependencies.json and winnowing.wfp before you send
anything.
Fingerprints cannot be reversed to the code: they are winnowing hashes, from which the source cannot be reconstructed.
Paths inside the package always use forward slashes, whichever platform produced it, so a scan of the same tree yields the same record on Windows, macOS and Linux.
What the package contains
| File | Contents |
| ---------------------- | --------------------------------------------- |
| winnowing.wfp | winnowing fingerprints (hashes, never source) |
| dependencies.json | package URLs from manifests and lockfiles |
| file_count.csv | file counts by extension |
| projectMetadata.json | the contact details and license you supplied |
Dependency coverage
Dependency discovery runs two parsing engines. The scanoss SDK covers package.json,
package-lock.json, yarn.lock, pom.xml, build.gradle, requirements.txt, pyproject.toml,
Gemfile, Gemfile.lock, go.mod, go.sum, *.csproj and packages.config. This program adds
the lockfile formats it does not:
| Ecosystem | Added formats |
| ------------------ | -------------------------------------------------------------------------------- |
| npm | pnpm-lock.yaml, npm-shrinkwrap.json, bun.lock |
| Python | poetry.lock, Pipfile, Pipfile.lock, requirements*.txt, environment.yml |
| Rust / C++ | Cargo.lock, Cargo.toml, conan.lock, vcpkg.json |
| PHP | composer.json, composer.lock |
| JVM | gradle.lockfile |
| .NET | packages.lock.json, paket.lock, *.fsproj, *.vbproj |
| Apple | Podfile.lock, Package.resolved, Cartfile.resolved |
| Dart / Elixir / Go | pubspec.yaml, pubspec.lock, mix.lock, Gopkg.lock |
Detection is not configurable: every scan runs both engines over every file, vendored directories
included, because a checked-in node_modules is part of what ships. There is no flag to narrow it.
probe-cli formats prints the live list. Two deliberate behaviours:
- Local packages are not reported.
workspace:*,file:,link:, path and git specifiers name things that exist in no registry, so no purl is emitted for them. - Ranges stay ranges. A manifest constraint like
^4.18.2goes inrequirement; the purl only carries a version when the file actually pins one. Lockfiles pin, so their purls carry versions.
bun.lockb is binary: it is detected and reported as a gap rather than silently ignored.
Privacy
Lockfiles routinely name internal packages and private registry hosts, so any URL-shaped field is dropped from the dependency records before they are written, and package names go through the obfuscation dictionary when one is in use.
The obfuscation dictionary (obfuscationMapper.json) is written to the working directory and is
not included in the package — it is the key that maps obfuscated paths back to real ones, and it
stays with you. Keep it if you need to interpret the auditor's findings; pass --keep-workspace to
retain it.
Rebranding
Everything brand-specific lives in brand.config.json. npm run brand:check fails the build if a
vendor name is hardcoded anywhere in src/ or scripts/ — attribution notices excepted, since the
GPL requires those — so the list below is the whole job.
Fork and edit
brand.config.json:{ "productName": "Acme Audit Probe", "binaryName": "acme-probe", "shortName": "Acme Probe", "vendor": "Acme", "tagline": "Captures fingerprints from your code, without shipping your code.", "packageExtension": ".acme", "versionFooterTag": "ACME_VERSION", "obfuscationKeyPrefix": "ACME", "workspaceDirName": "acme-workspace", "configDirName": "acme-probe", "websiteUrl": "https://acme.example", "uploadUrl": "https://acme.example/upload", "supportEmail": "[email protected]", "issuesUrl": "https://github.com/acme/probe-cli/issues", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n…your key…\n-----END PUBLIC KEY-----\n", "accentColor": "magenta" }publicKeyPemis the RSA public key your back end holds the private half of. Everything else is cosmetic except the three format fields called out below.Apply and verify:
npm run brand:apply # updates package.json name/bin/homepage and the install scripts npm run brand:check # fails if any brand string is hardcoded in the source npm test npm run build:binaryPoint the installers at your repository. Set
"repository": "acme/probe-cli"inbrand.config.jsonand re-runnpm run brand:apply, which rewrites the defaults ininstall.shandinstall.ps1. Update.github/workflows/release.ymland the artwork by hand.
Three fields are wire format, not decoration
packageExtension, versionFooterTag and publicKeyPem are read by whatever ingests the package.
Change them only together with the receiving end:
publicKeyPem— the package body is AES-128-CBC, its key and IV sealed with this RSA key. The wrong key makes the package undecryptable.versionFooterTag— the last 100 bytes of the file are<TAG>:<version>padded with NULs. An ingest that greps for the old tag will reject the file.packageExtension— upload forms usually filter on it.
Keep the originals if you are talking to an existing back end, and rebrand only the surface.
Try a rebrand without rebuilding
PROBE_BRAND_CONFIG points the binary at a different brand file at runtime — useful for testing
before you commit to a fork:
PROBE_BRAND_CONFIG=/tmp/acme-brand.json probe-cli scan . --email [email protected]Development
npm install
npm run dev -- scan ./some-folder --email [email protected] # run from source
npm test # 110 tests: parsers, pipeline, package format, CLI, end to end
npm run typecheck
npm run verify # brand check, types, formatting and tests, as CI runs them
npm run build # dist/probe.cjs, a single file of about 4.8 MB
npm run build:binary # bin/probe-<version>-<os>-<arch>, 99 MB (33 MB compressed)Layout, data flow and the reasoning behind the design are in ARCHITECTURE.md.
src/core holds the scanning pipeline and is free of any terminal or CLI concern, so it can be
driven as a library; src/cli is the only place that reads arguments or writes to the terminal.
Adding a lockfile format takes one entry in LOCKFILE_DEFINITIONS and one parser function; see the
same-named section of ARCHITECTURE.md.
Licensing
Copyright (C) 2021-2026 Fossity LLC (https://fossity.com).
This program is free software licensed under the GNU General Public License, version 2 only
(SPDX-License-Identifier: GPL-2.0-only). The full text is in LICENSE.
Parts of src/core derive, in modified form, from the discontinued Fossity Probe desktop
application, which was itself based on the
SCANOSS Audit Workbench. Both are GPL-2.0.
NOTICE records which parts are derived and which were written for this program; every
source file carries an SPDX header, and .reuse/dep5 covers the files that cannot.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
What this means if you fork it
- A rebranded fork is still a derivative work: it must remain GPL-2.0, keep the copyright notices and the LICENSE file, and offer source to anyone it distributes binaries to.
- Rebranding changes the name, not the licence. Replacing the vendor name everywhere is expected and supported (see Rebranding); removing the attribution is not permitted.
- Contributions are accepted under the same licence.
Third-party components
Dependencies keep their own licences; package.json lists them and npm ls resolves the tree. The
notable one is scanoss (MIT), which provides the winnowing
fingerprint engine and the package manifest parsers this program builds on. A standalone binary
embeds the Node runtime, which is MIT licensed.
