Skip to content

Repository files navigation

SCANOSS CLI — Go Implementation

Command-line tool and Go SDK for scanning source code and querying the SCANOSS platform. It fingerprints a project with WFP (Winnowing FingerPrint), uploads the fingerprints to the SCANOSS v3 API, and can decorate results with vulnerabilities, licenses, cryptography, geoprovenance, copyright, dependency, and component data.

Important

This CLI is not compatible with OSSKB.org.

See CHANGELOG.md for release notes and the releases page for the latest version.

Architecture

  • cmd/ — the CLI (Cobra); cmd/scanoss-cli is the go install entrypoint.
  • pkg/ — the reusable Go SDK: scan and decoration services, fingerprinting, file filtering, SBOM read/write, and the low-level API client.
  • internal/ — private helpers (CLI config, build config, logging, output, version).
  • libscanoss/ — C shared library with Python and Node.js wrappers.

OpenAPI types come from the published SDK github.com/scanoss/scanoss.api-sdk (imported as scanossapi); there is no local codegen step.

Installation

go install

go install github.com/scanoss/scanoss.go/cmd/scanoss-cli@latest

This installs the CLI as scanoss-cli (Go names the binary after its package directory) — matching the examples below and avoiding a clash with the SCANOSS scan engine (also scanoss) on your PATH.

Prebuilt binary

Download the archive for your platform from the releases page, extract it, and move the scanoss-cli binary onto your PATH:

# Linux (amd64) — adjust the archive for your OS/arch
tar xzf scanoss-cli-linux-amd64.tar.gz
sudo mv scanoss-cli /usr/local/bin/

On Windows, unzip the .zip and add the folder to your PATH. On macOS, an unsigned direct download may be quarantined by Gatekeeper — clear it with xattr -d com.apple.quarantine ./scanoss-cli. Verify a download against checksums.txt with sha256sum -c --ignore-missing checksums.txt.

Docker

Multi-arch images (linux/amd64, linux/arm64) are published to GHCR on each release. Mount the code to scan and pass the CLI arguments:

docker run --rm -v "$PWD:/src" ghcr.io/scanoss/scanoss:latest \
  scan /src --api-key "$SCANOSS_API_KEY" > results.json

Use :latest or a version tag (e.g. :0.1.0).

The image runs as a non-root user, so writing output into the mounted folder (--output /src/results.json) can fail with a permission error. Either redirect stdout on the host as above, or run the container as your own user so writes are owned by you:

docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/src" \
  ghcr.io/scanoss/scanoss:latest scan /src --api-key "$SCANOSS_API_KEY" --output /src/results.json

Build from source

git clone https://github.com/scanoss/scanoss.go.git
cd scanoss.go
make build          # or: go build -o scanoss-cli ./cmd/scanoss-cli

Quick start

# Scan a project and save JSON results (default endpoint needs an API key)
scanoss-cli scan ./my-project --api-key "$SCANOSS_API_KEY" --output results.json

# Generate fingerprints only
scanoss-cli wfp ./my-project > project.wfp

# Refresh vulnerabilities/licenses on an existing inventory (no re-scan)
scanoss-cli enrich results.json --include vulns,licenses --api-key "$SCANOSS_API_KEY" > enriched.json

Add -v / --verbose to any command for structured debug logging on stderr — the scan flow, each API request (method/URL/status/duration), and fingerprinting. Stdout stays reserved for results, so logs never corrupt --output or piped JSON.

Commands

Command Purpose
scan <path> Fingerprint a folder/file, scan against the SCANOSS v3 API, and output results (--format raw/spdx/cyclonedx; opt into dependency/vuln/license/crypto/geo layers with --include).
scan wfp <wfp> Scan a pre-generated WFP file (no fingerprinting).
wfp <path> Generate WFP fingerprints only (no upload).
results <scan-id> Resume or poll a scan by its id.
sbom <input> Produce an SBOM from a raw inventory, or convert between formats, offline (cyclonedx/spdx).
enrich <input> Add purl-keyed layers (vulns/licenses/crypto/geo) to a raw or SBOM file.
dependencies [path] Extract local dependencies, or query direct/transitive deps for a PURL.
vulnerabilities Known vulnerabilities / CPEs for components.
cryptography Algorithms, library hints, and version ranges.
licenses Declared licenses, attribution files, per-file evidence.
geoprovenance Component origin and contributor countries.
copyright Copyright evidence and holders.
components Search, versions, and lifecycle status.
config Store settings in ~/.scanoss/settings.json (see Configuration).

The default endpoint (https://api.scanoss.com) requires an API key; a custom API URL (e.g. an on-prem deployment) may run keyless.

See CLIENT_HELP.md for full usage — every command and subcommand with flags, examples, the scanoss.json reference (BOM + skip rules), SBOM output formats, and default values.

Configuration

Store your credentials once instead of passing --api-key on every command:

scanoss-cli config set api-key SC_abc123def456
scanoss-cli scan ./my-project --output results.json

Settings use the same names as the flags: api-key, api-url, proxy and ca-cert. They live in ~/.scanoss/settings.json:

{
  "api_key": "SC_abc123def456",
  "api_url": "https://api.scanoss.com",
  "proxy": "http://proxy.example.com:8080",
  "ca_cert": "/etc/ssl/corp-ca.pem"
}

The command line always uses the dashed names; snake_case is the file's format, not a second way to type a key.

Precedence

Every setting resolves the same way, and each has a matching flag:

--flag  >  environment variable  >  ~/.scanoss/settings.json  >  built-in default

The environment variable is the setting name in upper case with a SCANOSS_ prefix: SCANOSS_API_KEY, SCANOSS_API_URL, SCANOSS_PROXY, SCANOSS_CA_CERT. An empty value from the environment or the file is treated as unset and falls through to the next source.

scanoss-cli config set api-url https://scanoss.internal.example.com

# 1. the stored value is used
scanoss-cli scan .

# 2. the environment overrides the file
SCANOSS_API_URL=https://scanoss.staging.example.com scanoss-cli scan .

# 3. the flag overrides both
SCANOSS_API_URL=https://scanoss.staging.example.com \
  scanoss-cli scan . --api-url https://api.scanoss.com

--verbose reports which source won for each setting (the source only, never the key's value).

Inspecting

config list shows the value each command will actually use, and where it came from:

$ scanoss-cli config list
api-key  ********                              (env: SCANOSS_API_KEY)
api-url  https://scanoss.internal.example.com  (config file)

Config file: /Users/you/.scanoss/settings.json

The API key is never printed. list and get always render it as ******** — there is no flag that reveals it, so it cannot land in your shell history or a CI log. config get api-key therefore only tells you whether it is set (exit code 0 or 1). Scripts that need the value should use $SCANOSS_API_KEY; to read your own file, open it directly:

cat "$(scanoss-cli config path)"

Non-secret values print normally, so config get composes:

$ scanoss-cli config get api-url
https://scanoss.internal.example.com

On-prem endpoint

A custom API URL may run keyless, so pointing the CLI at an internal deployment is one command:

scanoss-cli config set api-url https://scanoss.internal.example.com
scanoss-cli scan .

Proxy and custom CA

HTTP_PROXY, HTTPS_PROXY and NO_PROXY are honoured with no flags. --proxy overrides them for one run, and --ca-cert trusts a CA the system pool does not have — an internal endpoint, or a proxy that intercepts TLS:

scanoss-cli scan . --proxy http://proxy.example.com:8080
scanoss-cli scan . --ca-cert /etc/ssl/corp-ca.pem

The CA is added to the system pool, so the public API keeps working, and verification stays on — unlike --ignore-cert-errors. Both flags work on every command that reaches the API. Proxy auto-configuration (PAC) is not supported: read the proxy out of the PAC and pass it with --proxy.

Both can be stored, so neither flag has to be repeated:

scanoss-cli config set proxy http://proxy.example.com:8080
scanoss-cli config set ca-cert /etc/ssl/corp-ca.pem
scanoss-cli scan .

A stored proxy takes precedence over HTTP_PROXY/HTTPS_PROXY. --ignore-cert-errors is not storable — turning off verification stays a per-run choice.

CI

Use the environment instead of a config file — no config set, and no key on the command line where it would land in build logs:

- name: SCANOSS scan
  env:
    SCANOSS_API_KEY: ${{ secrets.SCANOSS_API_KEY }}
  run: scanoss-cli scan . --output results.json

Rotating and removing

scanoss-cli config set api-key SC_newkey789   # overwrite in place
scanoss-cli config unset api-key              # remove the key
scanoss-cli config path                       # print the file location

Hand-editing the file is supported, and keys this version does not recognize are left untouched by config set.

Go SDK — Decoration Pipeline

Beyond the CLI, pkg/scanoss is a Go SDK for the SCANOSS services. The pipeline runs a configurable set of decoration services over the same PURLs in parallel, reports per-service progress, and returns one object keyed by service. Chunking and the worker pool are handled internally.

import "github.com/scanoss/scanoss.go/pkg/scanoss"

client, err := scanoss.New(scanoss.Config{
    APIKey:    os.Getenv("SCANOSS_API_KEY"),
    ChunkSize: 20, // PURLs per request
    Workers:   10, // max concurrent requests
})
if err != nil {
    return err
}

comps := scanoss.Components("pkg:github/scanoss/engine")

pipe := client.DecorationPipeline(
    scanoss.ServiceVulnerabilities,
    scanoss.ServiceLicenses,
)
pipe.Add(scanoss.ServiceCryptographyAlgorithms, scanoss.ServiceGeoprovenanceOrigin)

res, err := pipe.Run(context.Background(), comps)
if err != nil {
    log.Fatal(err) // only if every service failed
}
fmt.Println(res.String())

for svc, e := range res.Errors { // per-service failures are recorded, not fatal
    log.Printf("%s failed: %v", svc, e)
}

Per-service progress

Implement DecorationReporter and hand it to the call. Every update carries the service that produced it, so one receiver renders them all — services run concurrently, so it must be safe for concurrent use:

type bars struct{ mu sync.Mutex }

func (b *bars) Decorating(service string, done, total int) {
    b.mu.Lock()
    defer b.mu.Unlock()
    fmt.Printf("%-26s %d/%d purls\n", service, done, total)
}

res, err := pipe.Run(ctx, comps, scanoss.WithDecorationReporter(&bars{}))

The per-service methods take it too:

res, err := client.Vulnerabilities.Components(ctx, comps, scanoss.WithDecorationReporter(&bars{}))

Logging

The SDK writes nothing until you ask it to — it will not put lines in your program's output uninvited. One call covers every package it is built from, not just the client:

scanoss.SetLogger(slog.Default())                    // fold into your own stream
scanoss.SetLogger(slog.New(myHandler))               // keep them apart, or drop them
scanoss.SetLogger(nil)                               // back to silence

At Debug this also explains file selection — which rules a collection applied, and which rule excluded each file:

level=DEBUG msg="filters applied"  builtinFolderRules=true gitignore=true matchers=51
level=DEBUG msg="file excluded"    path=CHANGELOG.md rule=ext:.md
level=DEBUG msg="directory pruned" path=node_modules rule=dir:node_modules

Call it during initialisation: it is process-wide, so changing it while calls are in flight can split a run's output across two destinations.

Version requirements

scanoss.Components(...) produces entries with no version. When a version matters, build the components directly:

comps := []scanoss.Component{
    {Purl: "pkg:github/scanoss/engine", Requirement: "4.17.21"},
    {Purl: "pkg:github/scanoss/engine", Requirement: "5.4.7"},
}

A single service (without the pipeline)

Each decoration service is a grouped handle on the client:

res, err := client.Vulnerabilities.Components(ctx, comps) // *scanossapi.VulnerabilitiesResponse
// also: client.Licenses.Attribution, client.Cryptography.Algorithms,
//       client.Geoprovenance.Origin, client.Copyright.Evidence, ...

Scanning from the SDK

client, err := scanoss.New(scanoss.Config{APIKey: os.Getenv("SCANOSS_API_KEY")})
result, err := client.Scan.Folder(ctx, "./my-project")
// resume by id: client.Scan.Wait(ctx, scanID)

Proxy and custom CA from the SDK

client, err := scanoss.New(scanoss.Config{
    APIKey:     key,
    Proxy:      "http://proxy.example.com:8080", // empty honours HTTP(S)_PROXY
    CACertFile: "/etc/ssl/corp-ca.pem",          // added to the system pool
})
if err != nil {
    return err // an unreadable CA file or a schemeless proxy fails here
}

Development

make build         # build the CLI
make test          # unit tests
make test-race     # tests with the race detector
make lint          # golangci-lint
make check         # fmt-check + vet + lint + test (run before committing)

License

See LICENSE.

About

Command-line tool and Go SDK for scanning source code and querying the SCANOSS platform

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages