> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scanoss.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Approve components with scanoss.json

> Declare the open source components your project knowingly uses in scanoss.json, so they stop failing your checks, and handle false matches with ignore, remove, and replace rules.

## Goal

Keep a reviewed list of the open source components your project is allowed to contain, in a
`scanoss.json` file in the repository. scanoss-cli marks the components on the list as approved
in every scan, so your checks fail only on something new.

## When to use it

* Your [pull request check](/en/latest/developer-tools/recipes/scan-pull-requests) fails on
  components you already know about and accept.
* A scan reports a match you have reviewed and know to be wrong.
* You want approvals to go through code review, with a history in Git.

## Prerequisites

* scanoss-cli and `SCANOSS_API_KEY` (see [Recipes](/en/latest/developer-tools/recipes#before-you-use-a-recipe))
* `jq`, to bootstrap the file from an existing scan

## How scanoss-cli finds scanoss.json

scanoss-cli looks for `scanoss.json` (then `settings.json`) in the directory you scan. To use a
file somewhere else, pass `--settings <path>`. scanoss-cli applies the rules after the results
come back from the API, so they work the same for `scan`, `scan wfp`, and `results`.

## Step 1: Bootstrap the approved list

Scan the project once, and turn every component it detected into an approval rule:

```bash theme={null}
scanoss-cli scan . --output scanoss-results.json

jq '{
  bom: {
    identify: ([.components[]
                | select((.scope // "detected") == "detected")
                | {purl}] | unique)
  }
}' scanoss-results.json > scanoss.bootstrap.json
```

`scanoss.bootstrap.json` now contains one rule per component:

```json theme={null}
{
  "bom": {
    "identify": [
      { "purl": "pkg:github/madler/zlib" },
      { "purl": "pkg:npm/lodash" }
    ]
  }
}
```

Review the list before you use it. Every component in it counts as approved from now on. Then save it as
`scanoss.json`. If you already have a `scanoss.json` (for example, with skip settings), copy the
`bom` section into it instead of overwriting the file.

Commit `scanoss.json` to the repository. Require a review from your open source owner for changes
to it, using your code hosting's review rules. That review is your approval process.

## Step 2: Approve new components as they appear

When a check reports a new component and you accept it, add a rule in the same pull request:

```json theme={null}
{
  "bom": {
    "identify": [
      { "purl": "pkg:github/madler/zlib" },
      { "purl": "pkg:npm/lodash" },
      { "purl": "pkg:github/nlohmann/json", "path": "third_party/json/" }
    ]
  }
}
```

The third rule has a `path`. It approves the component only in files under `third_party/json/`.
If the same code turns up somewhere else, the scan still reports it.

<Warning>
  scanoss-cli matches rules by PURL without the version. On its own, a rule for
  `pkg:npm/vue@2.6.14` marks every version of `vue` as identified, including a later one with a
  different licence or a known vulnerability. The gate below closes that gap. When a rule names a
  version, the gate treats only that version as approved. Leave the version out only when you mean to
  approve every version.
</Warning>

## Step 3: Handle matches that are wrong

Not every match is a component you use. Four rule types cover the common cases:

| Rule | Effect | Use it when |
| - | - | - |
| `bom.identify` (also spelled `bom.include`) | Marks the component as approved (`"identified": true`) and puts it first in each matching file's matches. Protects it from `ignore`, `remove`, and the ranking threshold. | You use the component and accept it. |
| `bom.ignore` (also spelled `bom.exclude`) | Drops that component from each file's matches. Other matches for the same file remain. | A file matches the wrong project, for example a common upstream that many projects copy. |
| `bom.remove` | Clears all matches for the files where the component matched, so those files report no match. | A file is your own code, or code you already account for elsewhere, and should not report a match at all. |
| `bom.replace` | Changes the matched component to the one in `replace_with`. | The match points at a fork or mirror, and you want the original project reported. |

```json theme={null}
{
  "bom": {
    "identify": [
      { "purl": "pkg:github/madler/zlib" }
    ],
    "ignore": [
      { "purl": "pkg:github/some-mirror/zlib-copy", "path": "third_party/zlib/" }
    ],
    "remove": [
      { "purl": "pkg:github/example/our-old-public-repo" }
    ],
    "replace": [
      { "purl": "pkg:github/someone/lodash-fork", "replace_with": "pkg:npm/lodash@4.17.21" }
    ]
  }
}
```

Paths in rules are relative to the scanned directory:

* `dir/` matches every file under `dir`, at any depth.
* A glob, such as `dir/*.c`, matches with standard glob rules. `*` does not cross a `/`.
* Any other value must match the file path exactly.

Every rule needs a `purl`. The rules run in this order: ignore, identify, the optional
`--ranking-threshold` filter, remove, and replace.

## Approve a baseline without editing scanoss.json

To approve everything in an earlier result for one run, pass it with `-i, --identify`. Each
component in the file becomes an approval rule without a path:

```bash theme={null}
scanoss-cli scan . --identify baseline-results.json --output scanoss-results.json
```

`--identify` accepts a scanoss-cli raw result, a CycloneDX or SPDX file, or a component list
(`{"components":[{"purl":"..."}]}`). `-n, --ignore <file>` works the same way for ignore rules.
Both can be combined with `scanoss.json`.

Use this to adopt SCANOSS on a large existing codebase. Keep `baseline-results.json` as a build
artefact and fail only on what appears after it. For a reviewed, long-term record, prefer
`scanoss.json`.

## What the output means

In the raw result, an approved component carries `"identified": true`, both on the component and
on the file evidence the rule covered:

```json theme={null}
{
  "purl": "pkg:github/nlohmann/json",
  "identified": true,
  "evidence": [
    { "path": "third_party/json/json.hpp", "match_type": "file", "identified": true }
  ]
}
```

A component approved only for some paths still shows `"identified": true` at component level.
Check the `identified` value on each `evidence` entry to see which files the rule covered. In
CycloneDX output, the component-level flag appears as the `scanoss:identified` property.

## How it fails the pipeline

`scanoss.json` changes what the result contains, not the exit code. Your gate decides what fails.

Save this filter in your repository as `scanoss/unapproved.jq`. The other recipes use it too. It
prints each detected component that `scanoss.json` doesn't approve:

```jq title="scanoss/unapproved.jq" theme={null}
# Detected components that scanoss.json does not approve.
# A bom.identify rule that pins a version approves only that version.
# A rule without a version approves every version of the component.
def base: sub("@[^@/]+$"; "");
def ver: (capture("@(?<v>[^@/]+)$") | .v) // null;
(($rules[0] // {}) | .bom | (.identify // .include // [])) as $r
| [ $r[] | select(.purl) | {b: (.purl | base), v: (.purl | ver)} ] as $pins
| .components[]
| select((.scope // "detected") == "detected")
| . as $c
| ($pins | map(select(.b == ($c.purl | base)))) as $m
| select(
    ($c.identified | not)
    or (($m | length) > 0
        and ($m | all(.v != null))
        and (($m | map(.v) | index($c.version)) == null))
  )
```

Run it after the scan. It fails with exit code `2` when it prints anything:

```bash theme={null}
rules=scanoss.json; [ -f "$rules" ] || rules=/dev/null
unapproved=$(jq -r --slurpfile rules "$rules" -f scanoss/unapproved.jq scanoss-results.json \
  | jq -rs '.[] | "\(.purl)@\(.version // "unknown")"')
if [ -n "$unapproved" ]; then
  echo "Components not approved in scanoss.json:"
  echo "$unapproved"
  exit 2
fi
```

With the rules from Step 2, every version of `zlib`, `lodash`, and `json` passes. Change the
`lodash` rule to `pkg:npm/lodash@4.17.21` and only that version passes. An upgrade then fails the
gate until someone approves the new version. To bootstrap pinned rules instead, use
`{purl: (if .version then .purl + "@" + .version else .purl end)}` in place of `{purl}` in Step 1.

## Related settings in scanoss.json

The same file also controls which files scanoss-cli scans (`settings.skip`), and Crypto Finder
reads its skip patterns too. See
[Scanning your project](/en/latest/cli/scanoss-go/scanning-your-project#skipping-files).

<Warning>
  The `settings.file_snippet` keys `skip_headers`, `skip_headers_limit`, and `ranking_threshold`
  in `scanoss.json` override the matching command-line flags. If a flag seems to have no effect,
  check for these keys.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.