---
url: /en/security/config-file.md
description: >
  Detailed guide to the code_scan_config.yml configuration file, including file
  location, top-level structure, include splitting and merge rules, and validate
  self-checks.
---
This chapter describes the overall structure, file location, and how to split configuration with `include`.

## File location

| File | Location | Required |
|---|---|---|
| `code_scan_config.yml` | `<repo-root>/.cnb/security/code_scan_config.yml` | No |
| `.scanignore` | Any path in the repository, explicitly declared in the configuration | No |
| `include` subfile | Any path in the repository | No |

When `code_scan_config.yml` is not provided, the platform uses the built-in default configuration and scanning
continues as usual.

## Top-level structure

```yaml title=".cnb/security/code_scan_config.yml"
# 0~N subfiles, merged in order
include:
  - <relative path>
  - path: <relative path> # object form, tolerate missing
    ignoreError: true

# Capability blocks (any subset; undeclared capabilities use platform defaults)
secrets:
  # ...
software-composition-analysis:
  # ...
```

Each capability block has the same shape:

```yaml
<capability>:
  scan: # scan scope
    ignoreFrom: <path>
    paths:
      - <gitignore pattern>
  rules: # finding handling rules (Beta)
    - name: <id>
      when:
        # ...
      then:
        # ...
```

> **Important**: common top-level keys are `include`, `secrets`, and `software-composition-analysis`. Unknown keys are
> treated as errors. Use the `validate` result as the source of truth.

## include: split configuration for maintenance

### Path resolution

**All include paths are resolved relative to the repository root**, whether they appear in `code_scan_config.yml` or
in an included subfile:

```yaml title=".cnb/security/code_scan_config.yml"
include:
  - .cnb/security/checks/secrets.yml
```

```yaml title=".cnb/security/checks/secrets.yml"
include:
  - .cnb/security/checks/secrets-extra.yml # ✅ still relative to repo root
```

### Tolerate optional subfiles

In string form, a missing subfile causes an error. If you want to load the file when it exists and skip it when it is
missing, use object form and set `ignoreError: true`:

```yaml title=".cnb/security/code_scan_config.yml"
include:
  - { path: .cnb/security/checks/local-overrides.yml, ignoreError: true }
```

> **Tip**: `ignoreError` only ignores missing files. Parsing errors, circular references, and paths escaping the
> repository still cause errors.

### Merge rules

`code_scan_config.yml` and all included subfiles are eventually merged into one configuration. Different capabilities
are independent. Fields under the same capability are merged as follows:

| Field type | Merge behavior |
|---|---|
| Scalar (`ignoreFrom`) | A later value overwrites an earlier value |
| Array (`paths`, `rules`) | Appended and merged, in order from low to high priority |

**Priority, from low to high**:

```text
include[0] < include[1] < ... < include[N-1] < local code_scan_config.yml
```

* **Local values override include values**: if both the local file and an included file define a scalar field,
  the local value takes effect.
* **Order within include**: earlier includes have lower priority, and later values overwrite earlier ones.

> **Note the overwrite behavior of arrays**: array fields are not overwritten. Elements from all sources are
> **appended** in low-to-high priority order. This is important for gitignore negation rules: a local `!pattern`
> must come after included values to take effect.

#### Example

```yaml title=".cnb/security/code_scan_config.yml"
# Local configuration, highest priority
include:
  - .cnb/security/checks/secrets.yml
secrets:
  scan:
    ignoreFrom: .scanignore-local # local scalar
    paths: ["!keep-local.go"] # local array
```

```yaml title=".cnb/security/checks/secrets.yml"
secrets:
  scan:
    ignoreFrom: .scanignore-included
    paths: ["vendor/**"]
```

Merged result:

```yaml
secrets:
  scan:
    ignoreFrom: .scanignore-local # local wins
    paths: ["vendor/**", "!keep-local.go"] # appended, local at end
```

### Security restrictions

* include paths must be **relative** and must not escape the repository root. `../..` is not allowed.
* Absolute paths and URLs are not allowed. Cross-repository references are not supported.
* Symlinks are not allowed. Even if the target is inside the repository, an error is reported.
* A single parse supports at most **50** files, including recursive includes.
* Circular includes are not allowed. For example, `A → B → A` reports an error.

## Self-check with the validate plugin

`validate` is used to confirm that the configuration is correct before submission. Exit code `0` means the
configuration is valid. A non-zero exit code means there is an issue, and the logs list each issue and its line number.

### Call it in a CNB pipeline, recommended

Add a plugin task in the repository's `.cnb.yml` to block invalid configuration at the PR stage:

```yaml title=".cnb.yml"
main:
  pull_request:
    - stages:
        - name: validate code scan config
          image: cnbcool/code-security-config
          args:
            - validate
```

* `args` are appended to the plugin image entry command, equivalent to running `code-security-config validate`.
* By default, it reads `.cnb/security/code_scan_config.yml`.
* If the configuration is in another location, use `args: [validate, --config, <repository-relative path>]`.
* When validation fails, the task exits with a non-zero exit code. The pipeline fails, and the logs show each issue
  and its line number.

### Call it locally

```bash
code-security-config validate                              # reads .cnb/security/code_scan_config.yml
code-security-config validate --config path/to/code_scan_config.yml
```

## Next steps

[Syntax reference](./syntax-reference.md) — complete field-level reference.
