# Custom Checks ## Overview You can write custom checks in [Rego][rego]. Once you finish writing custom checks, you can pass the check files or the directory where those checks are stored with --config-check` option. ``` bash trivy config --config-check /path/to/policy.rego --config-check /path/to/custom_checks --namespaces user /path/to/config_dir ``` As for `--namespaces` option, the detail is described as below. ### File formats If a file name matches the following file patterns, Trivy will parse the file and pass it as input to your Rego policy. | File format | File pattern | |---------------|-----------------------------------------------------------| | JSON | `*.json` | | YAML | `*.yaml` and `*.yml` | | Dockerfile | `Dockerfile`, `Dockerfile.*`, and `*.Dockerfile` | | Containerfile | `Containerfile`, `Containerfile.*`, and `*.Containerfile` | | Terraform | `*.tf` and `*.tf.json` | ### Configuration languages In the above general file formats, Trivy automatically identifies the following types of configuration files: - CloudFormation (JSON/YAML) - Kubernetes (JSON/YAML) - Helm (YAML) - Terraform Plan (JSON/Snapshot) This is useful for filtering inputs, as described below. ## Rego format A single package must contain only one policy. !!!example ``` rego # METADATA # title: Deployment not allowed # description: Deployments are not allowed because of some reasons. # schemas: # - input: schema["kubernetes"] # custom: # id: ID001 # severity: LOW # input: # selector: # - type: kubernetes package user.kubernetes.ID001 deny[res] { input.kind == "Deployment" msg := sprintf("Found deployment '%s' but deployments are not allowed", [input.metadata.name]) res := result.new(msg, input.kind) } ``` In this example, ID001 "Deployment not allowed" is defined under `user.kubernetes.ID001`. If you add a new custom policy, it must be defined under a new package like `user.kubernetes.ID002`. ### Policy structure `# METADATA` (optional unless the check will be contributed into Trivy) : - SHOULD be defined for clarity since these values will be displayed in the scan results - `custom.input` SHOULD be set to indicate the input type the policy should be applied to. See [list of available types][source-types] `package` (required) : - MUST follow the Rego's [specification][package] - MUST be unique per policy - SHOULD include policy id for uniqueness - MAY include the group name such as `kubernetes` for clarity - Group name has no effect on policy evaluation `deny` (required) : - SHOULD be `deny` or start with `deny_` - Although `warn`, `warn_*`, `violation`, `violation_` also work for compatibility, `deny` is recommended as severity can be defined in `__rego_metadata__`. - SHOULD return ONE OF: - The result of a call to `result.new(msg, cause)`. The `msg` is a `string` describing the issue occurrence, and the `cause` is the property/object where the issue occurred. Providing this allows Trivy to ascertain line numbers and highlight code in the output. - A `string` denoting the detected issue - Although `object` with `msg` field is accepted, other fields are dropped and `string` is recommended if `result.new()` is not utilised. - e.g. `{"msg": "deny message", "details": "something"}` ### Package A package name must be unique per policy. !!!example ``` rego package user.kubernetes.ID001 ``` By default, only `builtin.*` packages will be evaluated. If you define custom packages, you have to specify the package prefix via `--namespaces` option. By default, Trivy only runs in its own namespace, unless specified by the user. Note that the custom namespace does not have to be `user` as in this example. It could be anything user-defined. ``` bash trivy config --config-check /path/to/custom_checks --namespaces user /path/to/config_dir ``` In this case, `user.*` will be evaluated. Any package prefixes such as `main` and `user` are allowed. ### Metadata The check must contain a [Rego Metadata](https://www.openpolicyagent.org/docs/latest/policy-language/#metadata) section. Trivy uses standard rego metadata to define the new policy and general information about it. Trivy supports extra fields in the `custom` section as described below. !!!example ``` rego # METADATA # title: Deployment not allowed # description: Deployments are not allowed because of some reasons. # custom: # id: ID001 # severity: LOW # input: # selector: # - type: kubernetes ``` If you are creating checks for your Trivy misconfiguration scans, some fields are optional as referenced in the table below. The `schemas` field should be used to enable policy validation using a built-in schema. It is recommended to use this to ensure your checks are correct and do not reference incorrect properties/values. | Field name | Allowed values | Default value | In table | In JSON | |------------------------------|----------------------------------------------------------------------------------------------|:----------------------------:|:--------:|:-------:| | title | Any characters | N/A | ✅ | ✅ | | description | Any characters | | - | ✅ | | schemas.input | `schema["kubernetes"]`, `schema["dockerfile"]`, `schema["cloud"]`, `schema["terraform-raw"]` | (applied to all input types) | - | - | | custom.id | Any characters | N/A | ✅ | ✅ | | custom.severity | `LOW`, `MEDIUM`, `HIGH`, `CRITICAL` | UNKNOWN | ✅ | ✅ | | custom.recommended_actions | Any characters | | - | ✅ | | custom.deprecated | `true`, `false` | `false` | - | ✅ | | custom.input.selector.type | Any item(s) in [this list][source-types] | | - | ✅ | | custom.minimum_trivy_version | The minimum version of Trivy that's required to evaluate this check | | - | ✅ | | url | Any characters | | - | ✅ | #### custom.avd_id and custom.id The AVD_ID can be used to link the check to the Aqua Vulnerability Database (AVD) entry. For example, the `avd_id` `AVD-AWS-0176` is the ID of the check in the [AWS Vulnerability Database](https://avd.aquasec.com/). If you are [contributing your check to trivy-checks](../../../../community/contribute/checks/overview.md), you need to generate an ID using `make id` in the [trivy-checks](https://github.com/aquasecurity/trivy-checks) repository. The output of the command will provide you the next free IDs for the different providers in Trivy. The ID is based on the AVD_ID. For instance if the `avd_id` is `AVD-AWS-0176`, the ID is `ID0176`. #### custom.provider The `provider` field references the [provider](https://github.com/aquasecurity/trivy/tree/main/pkg/iac/providers) available in Trivy. This should be the same as the provider name in the `pkg/iac/providers` directory, e.g. `aws`. #### custom.service Services are defined within a provider. For instance, RDS is a service and AWS is a provider. This should be the same as the service name in one of the provider directories. ([Link](https://github.com/aquasecurity/trivy/tree/main/pkg/iac/providers)), e.g. `aws/rds`. #### custom.input The `input` tells Trivy what inputs this check should be applied to. Cloud provider checks should always use the `selector` input, and should always use the `type` selector with `cloud`. Check targeting Kubernetes yaml can use `kubernetes`, RBAC can use `rbac`, and so on. #### Subtypes in the custom data Subtypes currently only need to be defined for cloud providers [as detailed in the documentation.](./selectors.md/#enabling-selectors-and-subtypes) #### Scan Result Some fields are displayed in scan results. ``` bash k.yaml (kubernetes) ─────────────────── Tests: 32 (SUCCESSES: 31, FAILURES: 1) Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0) LOW: Found deployment 'my-deployment' but deployments are not allowed ════════════════════════════════════════════════════════════════════════ Deployments are not allowed because of some reasons. ──────────────────────────────────────────────────────────────────────── k.yaml:1-2 ──────────────────────────────────────────────────────────────────────── 1 ┌ apiVersion: v1 2 └ kind: Deployment ──────────────────────────────────────────────────────────────────────── ``` ### Input You can specify input format via the `custom.input` annotation. !!!example ``` rego # METADATA # custom: # input: # combine: false # selector: # - type: kubernetes ``` `combine` (boolean) : The details are [here](combine.md). `selector` (array) : This option filters the input by file format or configuration language. In the above example, Trivy passes only Kubernetes files to this policy. Even if a Dockerfile exists in the specified directory, it will not be passed to the policy as input. Possible values for input types are: - `dockerfile` (Dockerfile) - `kubernetes` (Kubernetes YAML/JSON) - `rbac` (Kubernetes RBAC YAML/JSON) - `cloud` (Cloud format, as defined by Trivy - this is used for Terraform, CloudFormation, and Cloud/AWS scanning) - `yaml` (Generic YAML) - `json` (Generic JSON) - `toml` (Generic TOML) - `terraform-raw` (Terraform configuration is not converted to common state as for the Cloud format, allowing for more flexible and direct checks on the original code) When configuration languages such as Kubernetes are not identified, file formats such as JSON will be used as `type`. When a configuration language is identified, it will overwrite `type`. !!! example `pod.yaml` including Kubernetes Pod will be handled as `kubernetes`, not `yaml`. `type` is overwritten by `kubernetes` from `yaml`. `type` accepts `kubernetes`, `dockerfile`, `cloudformation`, `terraform`, `terraformplan`, `json`, or `yaml`. ### Schemas See [here](schema.md) for the detail. [rego]: https://www.openpolicyagent.org/docs/latest/policy-language/ [package]: https://www.openpolicyagent.org/docs/latest/policy-language/#packages [source-types]: https://github.com/aquasecurity/trivy/blob/e4af279b29ed5b77ed1d62e31b232b1f9b92ef4f/pkg/iac/types/sources.go#L5-L17