# Mute Descriptor Format

This page describes the YAML format of the `.xygeni.mute.yml` descriptor used by [scanner-side mute rules](/xygeni-scanner-cli/xygeni-cli-overview/mute-rules.md).

## Top-level fields

A mute descriptor is a YAML file with the following top-level fields:

| Field         | Type         | Required | Description                               |
| ------------- | ------------ | -------- | ----------------------------------------- |
| `version`     | `String`     | No       | Format version (e.g. `"1.0"`)             |
| `description` | `String`     | No       | Human-readable description of the ruleset |
| `rules`       | `List<Rule>` | Yes      | Ordered list of mute rules                |

## Rule fields

Each entry in `rules` has the following fields:

| Field     | Type                       | Required | Default   | Description                                      |
| --------- | -------------------------- | -------- | --------- | ------------------------------------------------ |
| `id`      | `String`                   | No       | —         | Rule identifier (recommended for traceability)   |
| `reason`  | `String`                   | No       | —         | Human-readable justification for the suppression |
| `scanner` | `String` or `List<String>` | No       | all       | Scanner type filter (single value or array)      |
| `action`  | `MuteAction`               | No       | `mute`    | Action to take when a finding matches            |
| `tags`    | `List<String>`             | No       | —         | Custom tags added to matched findings            |
| `match`   | `MuteMatch`                | No       | match all | Matching criteria (omit to match everything)     |

### `scanner` field

The `scanner` field accepts both single values and arrays:

```yaml
scanner: secrets           # single value
scanner: [secrets, sast]   # array — matches findings from either scanner
```

Scanner type aliases are supported for convenience:

| Aliases                                                         | Canonical name |
| --------------------------------------------------------------- | -------------- |
| `dependencies`, `scan-deps`, `sca`, `component_vulnerabilities` | `deps`         |
| `secret`                                                        | `secrets`      |
| `iac_flaws`                                                     | `iac`          |
| `cicd`, `misconfiguration`, `misconfigurations`                 | `misconf`      |
| `critical_file_modification`                                    | `codetamper`   |
| `code_vulnerabilities`                                          | `sast`         |
| `dynamic_vulnerabilities`                                       | `dast`         |
| `evidence`                                                      | `malware`      |

## `match` fields

All non-null fields in `match` are **ANDed** together. Within each list field, patterns are **ORed** (any match suffices).

All pattern fields support **negation** (`!` prefix) and **regex** (`~` prefix). See [Matching semantics](#matching-semantics) for details.

| Field          | Type           | Applies to          | Description                                                                   |
| -------------- | -------------- | ------------------- | ----------------------------------------------------------------------------- |
| `detectors`    | `List<String>` | All scanners        | Glob patterns against detector IDs (e.g. `hardcoded_*`)                       |
| `categories`   | `List<String>` | All scanners        | Glob patterns against issue category tags                                     |
| `files`        | `List<String>` | All scanners        | Glob patterns against file paths (e.g. `src/test/**`)                         |
| `secrets`      | `List<String>` | Secrets scanner     | Patterns against cleartext secret values (e.g. `test-key-*`). See note below. |
| `secretTypes`  | `List<String>` | Secrets scanner     | Patterns against secret type name (e.g. `aws_access_key`, `generic_password`) |
| `secretKeys`   | `List<String>` | Secrets scanner     | Patterns against secret key/variable names (e.g. `TEST_*`)                    |
| `dependencies` | `List<String>` | Deps scanners       | Patterns against GAV coordinates (e.g. `com.example:*`)                       |
| `severity`     | `List<String>` | All scanners        | Exact severity levels: `critical`, `high`, `low`, `info`                      |
| `minSeverity`  | `String`       | All scanners        | Minimum severity (inclusive): `info`, `low`, `high`, `critical`               |
| `maxSeverity`  | `String`       | All scanners        | Maximum severity (inclusive): `info`, `low`, `high`, `critical`               |
| `confidence`   | `List<String>` | SAST, Secrets, Misc | Confidence levels: `low`, `medium`, `high`, `highest`                         |
| `language`     | `List<String>` | SAST only           | Language patterns (e.g. `java`, `python`)                                     |
| `cwes`         | `List<String>` | SAST only           | CWE ID patterns (e.g. `CWE-89`, `CWE-7*`)                                     |
| `cves`         | `List<String>` | SAST only           | CVE ID patterns (e.g. `CVE-2023-*`)                                           |
| `issueType`    | `List<String>` | IaC, Misc           | Issue type / IacFlawType patterns (e.g. `encryption`, `iam`)                  |
| `framework`    | `List<String>` | IaC only            | IaC framework patterns (e.g. `terraform`, `kubernetes`)                       |
| `newOnly`      | `Boolean`      | All scanners        | `true` = only new findings, `false` = only existing                           |
| `verified`     | `Boolean`      | Secrets only        | `true` = only verified secrets, `false` = only unverified                     |

{% hint style="warning" %}
**Note on the `secrets` field:** it matches against the cleartext secret value. Mute rules are applied **before** secret cleanup (which nullifies cleartext values), so the field is always available during mute evaluation. Cleartext values are never uploaded to the server.
{% endhint %}

## `action` values

| Action              | Suppressed? | Severity changed? | In report? | Tags added                                          |
| ------------------- | ----------- | ----------------- | ---------- | --------------------------------------------------- |
| `mute`              | Yes         | No                | Yes        | `suppressed`, `muted-by-config`, `mute-reason:<id>` |
| `set_severity_info` | No          | Yes (to `info`)   | Yes        | `muted-by-config`, `mute-reason:<id>`               |
| `tag_only`          | No          | No                | Yes        | Only the custom `tags` from the rule                |
| `discard`           | —           | —                 | **No**     | — (finding removed from report entirely)            |

For `mute`, `set_severity_info` and `tag_only`, any custom `tags` defined on the rule are also added to the finding.

For `discard`, the finding is removed from the report before upload, so no tags are added. Each discarded finding is logged at INFO level with its detector, issue ID, and the rule ID that triggered the discard.

**Choosing between `mute` and `discard`:** use `mute` when you want the finding to stay in the report (visible but excluded from guardrails and policy checks). Use `discard` when you want the finding completely removed — it will not appear in the report, dashboard, or any downstream processing. Discarded findings leave no audit trail in the report, so use with care.

## Matching semantics

1. **AND across criteria.** All non-null fields in `match` must match for the rule to fire. For example, if both `files` and `detectors` are specified, the finding must match at least one file pattern *and* at least one detector pattern.
2. **OR within lists.** Within a single field (e.g. `files: ["src/test/**", "*.yml"]`), matching any one pattern is sufficient.
3. **Glob patterns.** All pattern fields use glob syntax (`*` matches any sequence within a segment, `**` matches across path separators). Matching is **case-insensitive**.
4. **Negation (`!` prefix).** Patterns prefixed with `!` act as exclusions. If only negative patterns are specified, everything not excluded matches. If both positive and negative patterns are specified, the value must match at least one positive pattern AND not match any negative pattern.

   ```yaml
   # Match everything except test files
   files:
     - "!src/test/**"

   # Match hardcoded_* detectors, but exclude hardcoded_test
   detectors:
     - "hardcoded_*"
     - "!hardcoded_test"
   ```
5. **Regex (`~` prefix).** Patterns prefixed with `~` are treated as raw regular expressions (case-insensitive) instead of glob patterns. Can be combined with negation as `!~`.

   ```yaml
   # Match detectors using regex
   detectors:
     - "~java\\.sql_.*"

   # Negated regex: match everything except detectors ending in _test
   detectors:
     - "*"
     - "!~.*_test$"
   ```
6. **Severity ranges.** Use `minSeverity` and `maxSeverity` for inclusive range filtering. These work alongside the `severity` list field (when both are specified they are ANDed).

   ```yaml
   # Match low and high (but not info or critical)
   match:
     minSeverity: low
     maxSeverity: high
   ```
7. **First-match-wins.** Rules are evaluated in the order they appear. The first matching rule's action is applied and no further rules are checked.
8. **Scanner filter.** If `scanner` is set on a rule, only findings from that scanner type are considered. A rule with no `scanner` field applies to all scanner types. Multiple scanner types can be specified as an array.
9. **Omitted `match` block.** A rule with no `match` block (or `match: null`) matches *every* finding of the specified scanner type (or all scanners if `scanner` is also omitted).

## Use cases and examples

### Suppress test secrets

Suppress hardcoded credentials found in test directories, since they are not real secrets:

```yaml
rules:
  - id: mute-test-secrets
    reason: "Known test credentials, not real secrets"
    scanner: secrets
    action: mute
    match:
      files:
        - "src/test/**"
        - "**/*Test*"
      detectors:
        - "hardcoded_*"
```

### Whitelist known secret values

Suppress specific known test/example secret values that are safe to ignore. The `secrets` field matches against the cleartext secret value:

```yaml
rules:
  - id: mute-known-secret-values
    reason: "Known test/example credentials"
    scanner: secrets
    action: mute
    match:
      secrets:
        - "AKIAIOSFODNN7EXAMPLE"
        - "test-key-*"
        - "~^dummy[_-].*"
```

### Suppress secrets by key name or type

Use `secretKeys` to match the variable/key name, and `secretTypes` to match the kind of secret:

```yaml
rules:
  - id: mute-test-secret-keys
    reason: "Test credentials identified by variable name"
    scanner: secrets
    action: mute
    match:
      secretKeys:
        - "TEST_*"
        - "DUMMY_*"
        - "EXAMPLE_*"

  - id: mute-generic-passwords-tests
    reason: "Generic passwords in test directories"
    scanner: secrets
    action: mute
    match:
      secretTypes:
        - "generic_password"
      files:
        - "src/test/**"
```

All three secret fields (`secrets`, `secretTypes`, `secretKeys`) can be combined in the same rule — they are ANDed together (all must match).

### Multi-scanner rule

Suppress findings from multiple scanners with a single rule:

```yaml
rules:
  - id: mute-test-code
    reason: "Test code findings are expected"
    scanner: [secrets, sast]
    action: mute
    match:
      files:
        - "src/test/**"
```

### Suppress SAST findings in test files

Test code often uses patterns that trigger SAST detectors (e.g. SQL injection in test fixtures):

```yaml
rules:
  - id: mute-sast-tests
    reason: "SAST findings in test code are expected"
    scanner: sast
    action: mute
    match:
      files:
        - "src/test/**"
        - "**/*Test.java"
        - "**/*Spec.js"
```

### Suppress low-severity findings

Focus on critical and high findings by suppressing info and low severity issues:

```yaml
rules:
  - id: mute-low-severity
    reason: "Focus on critical/high severity findings"
    action: mute
    match:
      severity:
        - "info"
        - "low"
```

Or equivalently, using a severity range:

```yaml
rules:
  - id: mute-low-severity
    reason: "Focus on critical/high severity findings"
    action: mute
    match:
      maxSeverity: low
```

### Suppress low-confidence SAST findings

Only keep high-confidence SAST findings:

```yaml
rules:
  - id: mute-low-confidence-sast
    reason: "Low confidence findings are likely false positives"
    scanner: sast
    action: mute
    match:
      confidence:
        - "low"
        - "medium"
```

### Filter by language

Suppress SAST findings for a specific language:

```yaml
rules:
  - id: mute-python-sast
    reason: "Python SAST rules still being calibrated"
    scanner: sast
    action: mute
    match:
      language:
        - "python"
```

### Filter by CWE or CVE

Suppress specific vulnerability classes:

```yaml
rules:
  - id: mute-sql-injection
    reason: "SQL injection handled by parameterized queries"
    scanner: sast
    action: mute
    match:
      cwes:
        - "CWE-89"
        - "CWE-564"
```

### Filter IaC by type and framework

Suppress specific IaC flaw types for a particular framework:

```yaml
rules:
  - id: mute-tf-encryption
    reason: "Encryption handled at infrastructure level"
    scanner: iac
    action: mute
    match:
      issueType:
        - "encryption"
      framework:
        - "terraform"
```

### Using negation patterns

Suppress findings everywhere except specific detectors. With only negation patterns, everything not excluded matches:

```yaml
rules:
  # Mute everything except sql_injection and hardcoded_password
  - id: focus-on-critical-detectors
    reason: "Only review critical detectors"
    action: mute
    match:
      detectors:
        - "!sql_injection"
        - "!hardcoded_password"
```

Combine positive and negative patterns — the value must match at least one positive AND not match any negative:

```yaml
rules:
  # Match all hardcoded_* detectors except hardcoded_test
  - id: mute-hardcoded-except-test
    reason: "Suppress hardcoded detectors except test"
    action: mute
    match:
      detectors:
        - "hardcoded_*"
        - "!hardcoded_test"
```

### Tag for audit trail without suppression

Mark findings for review without suppressing them. Useful for tracking deprecated detectors or categories requiring attention:

```yaml
rules:
  - id: tag-deprecated-detectors
    reason: "Flag deprecated detectors for review"
    action: tag_only
    tags:
      - "review-needed"
      - "deprecated-detector"
    match:
      detectors:
        - "deprecated_*"
```

### Downgrade severity for accepted risks

When a risk is accepted but you still want visibility, downgrade the severity to `info` instead of fully suppressing:

```yaml
rules:
  - id: downgrade-sql-tests
    reason: "SQL injection in test code is an accepted risk"
    scanner: sast
    action: set_severity_info
    match:
      detectors:
        - "sql_injection"
      files:
        - "src/test/**"
```

### Discard findings entirely

Remove findings from the report so they are never uploaded. Use with care — discarded findings leave no trace in the report:

```yaml
rules:
  - id: discard-test-secrets
    reason: "Remove test secrets from report entirely"
    scanner: secrets
    action: discard
    match:
      files:
        - "src/test/**"
      secretKeys:
        - "TEST_*"
        - "DUMMY_*"
```

### Suppress all findings for a scanner

Suppress all IaC findings (e.g. during a migration when IaC configs are in flux):

```yaml
rules:
  - id: mute-all-iac
    reason: "IaC configs under active migration"
    scanner: iac
    action: mute
    # No match block = matches all findings for this scanner
```

### Filter by dependency coordinates

Suppress known false positives for specific dependencies:

```yaml
rules:
  - id: mute-internal-deps
    reason: "Internal dependencies, vulnerabilities tracked separately"
    scanner: deps
    action: mute
    match:
      dependencies:
        - "com.mycompany:*"
        - "com.mycompany.internal:*"
```

### Only match verified secrets

Suppress verified secrets in test directories (unverified secrets remain visible):

```yaml
rules:
  - id: mute-verified-test-secrets
    reason: "Verified test secrets are known and accepted"
    scanner: secrets
    action: mute
    match:
      files:
        - "src/test/**"
      verified: true
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.xygeni.io/xygeni-scanner-cli/xygeni-cli-overview/mute-rules/descriptor-format.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
