# Xygeni Guardrails

## Guardrails Specification

{% hint style="info" %}
This is a guardrail specification for Xygeni scanner. Guardrails can be executed at server side (Audit Analysis), for more information read [CI/CD Audit Analysis](https://docs.xygeni.io/xygeni-scanner-cli/xygeni-cli-overview/guardrails/ci-cd-audit-analysis)
{% endhint %}

When specific rules or special exit codes are required by pipelines or security policies, complex conditions can be defined using guardrail expressions.

Guardrails enables users to customize and specify how a Xygeni command should behave in the presence of certain conditions or criteria, thereby allowing for flexible and tailored handling of failure scenarios.

Guardrail specification is a subset of **Xygeni™ XyFlow Language**, tailored for guardrail conditions.

### Guardrail Convention <a href="#guardrail_convention" id="guardrail_convention"></a>

A guardrail expression consists of a name or string, followed by a pipe that contains actions limited to the set selected.

A pipe processes a stream of events matching a condition then performing actions on each event individually or on the full set of matching events.

```linenums
guardrail <name>
    on
      entries_selector
    when
      expressions...
    then
      then-actions...
    else
      else-actions...
    also
      when ... then ... else
  ...
]
```

{% hint style="info" %}
The guardrail name is optional. There are restrictions on the characters that can be included in this name; it is recommended to use only alphanumeric characters and the underscore "\_". Alternatively, if you wish to use characters such as the hyphen "-", you can enclose the guardrail name in quotation marks.
{% endhint %}

#### ON Clause

The `on` clause chooses which items from scan report to process. The following are the recognized names with the report matched and the type of the items to process:

| type(s)                                  | report                 | items                    |
| ---------------------------------------- | ---------------------- | ------------------------ |
| inventory, asset, assets                 | InventoryReport        | Asset                    |
| deps, scan-deps, dependencies, sca       | ScanReport             | Dependency               |
| suspectdeps                              | SuspectDepsReport      | SuspectDependency        |
| malware, evidence                        | MalwareEvidencesReport | MalwareEvidence          |
| secrets, secret                          | SecretsReport          | PotentialSecret          |
| iac, iac\_flaws                          | IacFlawsReport         | IacFlaw                  |
| cicd, misconf, misconfiguration(s)       | MiscReport             | Misconfiguration         |
| codetamper, critical\_file\_modification | CodeTamperReport       | CriticalFileModification |
| compliance                               | ComplianceReport       | Compliance               |
| entry, entries, item, items              | Collection, Array, Map | Object or Map.Entry      |
| any                                      | \<Any of the above>    | \<Corresponding type>    |

{% hint style="info" %}
`Closed Issues` are removed from the item list before processing by the guardrails. It require an available connection to xygeni server to retrieve the list of closed issues for the current project if exits at server side. It applies to secrets, misconfigurations, suspectdeps, malware and iac issue types.
{% endhint %}

Examples:

```
# guardrail will apply when running a SuspectDepsAnalysis
'guardrail <name> on suspectdeps when <condition> then ...'
```

```
# guardrail will apply when running a SecretsAnalysis or MisconfigurationsAnalysis
'guardrail <name> on secrets,misconf when <condition> then ...'
```

```
# guardrail will apply on any analysis
'guardrail <name> on any when <condition> then ...'
```

### Item Properties Reference

Each item type available in the on clause exposes a set of properties that can be used in when expressions, function arguments, or actions. These properties are accessible using dot notation ($property.subproperty) and are context-specific.

#### Default Properties (Common to Most Items)

These properties are available in most issue-related items (e.g., secrets, misconfigurations, iac flaws, malware, suspect dependencies):

* `severity`: Issue severity (e.g., `info`, `low`, `high`, `critical`)
* `confidence`: Detection confidence (e.g., `low`, `medium`, `high`, `highest`)
* `issueKind`: Type or classification of the issue (e.g., `secret`, `iac_flaw`)
* `detector`: Name or identifier of the rule or scanner that found the issue
* `location.filepath`: File path where the issue was detected
* `branch`: Name of the branch that triggered the scan
* `explanation`: Description or rationale behind the detection

#### CodeVulnerability (`on: sast`)

* `language`: (e.g., 'java', 'python')
* `cve`: CVE identifier (e.g., 'CVE-2022-1234')

#### SuspectDependency (`on: suspectdeps`)

* `dependency.language`: Dependency language
* `group`: Dependency group (e.g., 'com.example')
* `name`: Dependency name
* `version`: Version number

#### MalwareEvidence (`on: malware`, `evidence`)

* `kind`: Kind of evidence (e.g., 'execution', 'file', 'network', 'registry', '\_package', 'sensitive\_data', 'system')

#### PotentialSecret (`on: secrets`)

* `tags.verified`: Returns true if the secret is verified
* `tags.inactive`: Returns true if the secret is inactive

#### IacFlaw (`on: iac`, `iac_flaws`)

* `framework`: IaC framework (e.g., `terraform`, `ansible`)
* `type`: Type of the IaC issue (e.g., `appsec`, `backup_recovery`, `convention`, `encryption`, `gensec`, `iam`, `logging`, `network`, `secrets`)

#### Misconfiguration (`on: cicd`, `misconf`)

* `type`: Type of the misconfiguration (e.g., `cicd_security`, `network`, `credentials`)

#### CriticalFileModification (`on: codetamper`)

* `type`: Type of the critical file modification (e.g., `codeowners`, `configurations`, `shell_script`, `cicd_workflow`)

#### WHEN Clause

The `when` clause defines the condition for each item to be processed.\
A list of matched items will be passed to the `then` clause. In case an else clause is provided, a list of unmatched items will be passed to the `else` clause.

See [Expressions](#expressions) for more details.

#### THEN Clause <a href="#then_clause" id="then_clause"></a>

The `then` clause defines the actions to be executed on the matched items.

{% hint style="info" %}
If some items are found (issues or evidence) but none is matched neither `else` clause is provided, an exitcode will be set to 127 to force a non zero exit.
{% endhint %}

#### ELSE Clause

The `else` clause defines the actions to be executed on the unmatched items.

#### ALSO Clause

The `also` clause allows to define a nested `when-then-else` clause that will be evaluated over the matched items.

### General Syntax

#### Keywords

* `guardrail`: Indicates the beginning of a guardrail definition.
* `on`: Specifies the triggering events.
* `when`: Defines the condition for event/item processing.
* `then`: Specifies the actions to be performed.
* `also`: Optional keyword for additional conditions and actions.

#### Identifiers

Represent variable names or properties and follow the standard naming conventions similar to Java identifiers. They can start with a letter or underscore (\_), followed by any combination of letters, digits, and underscores.

#### Delimiters

* `[]`: Square brackets used for Array elements
* `[:]`: Square brackets with a colon used for Maps elements
* `()`: Parentheses used for function calls
* `,`: Comma used to separate arguments
* `:`: Colon used on function calls to identify named arguments

#### Literals

Include `integers`, `floating-point numbers`, `strings` (enclosed in double quotes "…​"), and `booleans` (true, false).

#### Operators

**Arithmetic operators:**

* `+`, `-`, `*`, `/`, `%`
* `++`, `--`

**Comparison operators:**

* `<`, `>`
* `<=`, `>=`
* `=`, `!=`, `<>`

**Logical operators:**

* `and`, `&&`
* `or`, `||`
* `not`, `!`

**String operators**

* `starts-with`, `ends-with`, `matches`, `contains`

**Collection operators**

* `in`, `contains`

**Object operators**

* `instanceOf`, `exists`

Examples of operators expressions

```
severity > 'low' (more than)
location.filepath = 'data/config.yml' (equals)
location.filepath starts-with 'data/'
location.filepath matches '.*\.yml$' (regex)
tags contains 'verified'
confidence in ['high', 'highest']
```

#### Comments

Comments can be included to provide clarity and documentation within guardrail definitions. Only Single-line comments are provided, it starts with `#`.

#### Functions

Functions are invoked using the `@` symbol followed by the function name and a list of arguments enclosed in parentheses, e.g., `@functionName(arg1, arg2)`.

#### Dot Notation <a href="#dot_notation" id="dot_notation"></a>

Dot notation can be used to access object properties and allows for specifying conditions or actions based on properties of objects within the guardrail execution context.

```
'... when report.baseline = true then ...'
```

#### Interpolation and Expression Embedding

Variables can be referenced using the `$` symbol followed by the variable name, e.g., `$report.baseline`.\
`$` alone references the current item, e.g., `@print($)`.

`$Variable` can be used to interpolate values in a string, e.g., `"Report is baseline: $report.baseline"`.

`${}` can be used to interpolate an expression in a string, e.g., `"Total number of secrets: ${@count(report.secrets)}"`.

#### Context Objects

Some objects are saved to global context and can be accessed implicitly from expressions:

* `report` a reference to the current analysis report generated.
* `metadata` a direct reference to report.metadata properties if present
* `context` the context itself is saved to global context

Examples:

```
'... when metadata.baseline = true then ...'
```

#### Expressions

Expressions can include variables, literals, functions, and operators combined to evaluate to a **Truth in XyFlow**.\
An expression is Truth when y resolved as:

* a true boolean
* a non-zero number
* a non-blank string
* a non-empty array, map or collection
* a non-null object

#### Actions

Actions are evaluated over each matched item (default) or over the complete list of matched items.\
To evaluate over the complete list use a kind name with the `_all` suffix.\
To select a specific action, use the `@` symbol followed by the name of the action.

Example:

```
on secrets
  when
    severity = 'critical'
  then
    @exitcode(167)'
  else
    print_all:@print("non-critical:" + @count($))'
```

### Built-in Functions

* **Aggregate Functions**\
  Available aggregate functions like `count`, `sum`, `avg`, `min`, `max`, and `sort`, which can operate on collections or arrays. These functions are crucial for data manipulation within guardrails.
  * **@count(collection)**: Returns the number of elements in a collection, map, array, or the length of a string.
  * **@sum(collection)**: Calculates the sum of numeric values within a collection or array.
  * **@avg(collection)**: Computes the average of numeric values in a collection or array.
  * **@min(collection)**: Finds the minimum numeric value in a collection or array.
  * **@max(collection)**: Identifies the maximum numeric value in a collection or array.
  * **@sort(collection)**: Returns a new collection with elements sorted in their natural order.
* **Exitcode**\
  Allows the specification of exit codes for guardrails, which can be used to indicate success or failure states in a customizable manner.
  * **@exitcode(code)**: Sets the exit code of the guardrail, enabling custom exit statuses to communicate outcomes to the CI/CD pipeline.
  * **@fail()**: Sets exit code of the guardrail to value 1, to force an exit with error. It is a simplified version of the `@exitcode(1)` function.
* **Item Functions**
  * **@is\_new()**: Returns true if the element is an Issue (Secret, Misconfiguration, IacFlaw, SuspectDependency, or MalwareEvidence) that wasn't present in the baseline analysis of the current project. Also returns true when a newly added Dependency contains Vulnerabilities not found in the baseline analysis.
* **Print/Debug Functions**\
  Enables printing messages to different outputs (stdout, stderr, or a file), which can be used for logging or debugging within guardrails.
  * **@print(expression)**: Allows printing of specified messages to stdout, stderr, or a file, useful for logging and debugging purposes. Parameters:
    * item: The text to print
    * output: `stdout`, `stderr`, or a file path. Defaults to `stdout`.
  * **@dump(msg)**: Dumps the message to stdout, typically for debugging. Used during dry-run testing of guardrails.
* **Scm Functions**\
  Functions for interacting with Source Control Management (SCM) systems, currently GitHub, through functions like github.status\_check and github.commit\_comment. These functions are used for reporting the results of guardrail checks back to SCM systems.
  * **@github.status\_check(…​)**: Creates a GitHub commit status, useful for reporting the outcome of a guardrail evaluation directly within a GitHub repository. Parameters:
    * repo: name of the repo (owner/name)
    * sha: commit sha (default last commit)
    * state: error, failure(default), pending, success
    * description: A short description of the status
  * **@github.commit\_comment(…​)**: Posts a comment on a specific commit in GitHub, allowing for inline feedback on guardrail checks. Parameters:
    * repo: name of the repo (owner/name
    * sha: commit sha (default last commit)
    * state: error, failure(default), pending, success
    * body: The body of the comment
    * path: Optional path of the file to comment on
    * Position: Optional position in the file to comment on

### Guardrails Examples

#### **Exit on Critical Issues**

* Condition: Break the build on any issue with a 'critical' severity level.
* Action: Exit with a specific exit code only when 'critical' severity level is found.

```xyflow
   on
     any
   when
     severity = critical
   then
     @exitcode(167)
```

#### **Exit on Critical Files**

* Condition: Break the build on any issue on critical files.
* Action: Exit with a specific exit code only when issue found on critical files.

```asciidoc
   on
     any
   when
     severity in ['critical', 'high'] and
     location.filepath starts-with 'src/admin'
   then
     @exitcode(167)
```

#### **New critical Secret or Dependency Issue not allowed**

* Condition: Break the build on any new critical secret or dependency.
* Action: Exit with a specific exit code only when new critical issues not present in baseline analysis are found.
* NOTE: This require a connection to xygeni server in order to retrieve current baseline issues.

```asciidoc
   guardrail
     new_critical_issues
   on
     secrets,suspectdeps
   when
     @is_new() and 
     severity = 'critical'
   then
     @exitcode(155)
```

#### **Critical Secrets Exposure**

* Condition: Any issue where the kind is a 'secret', with a 'critical' severity level.
* Action: Break the build with a specific exit code and set commit status failure

```xyflow
   on
     secrets
   when
     severity = critical
   then
     print_status_all:@print("updating commit status on repo: " + metadata.scm.fullName),
     set_status_all:@github.status_check(repo: $metadata.scm.fullName,state: 'failure',
    description: "Secret found in ${report.metadata.projectName}",context: 'security/secret'),
     @exitcode(167)
```

#### **High Confidence Misconfigurations**

* Condition: Issues categorized under 'misconfiguration' with 'high' or 'critical' severity and 'high' or 'highest' confidence.
* Action: Break the build with a specific exit code and open a ticket for the relevant team

```xyflow
# High Confidence Misconfigurations
on
  cicd
when
  severity >= high and confidence >= high
then
  # Creation of a ticket for the relevant team with a request for urgent remediation
  print_action:@print("open ticket with: " + metadata.scm.fullName),
  open_ticket:@http.send(
    url: "http://issueserver/rest/api/issue",
    method: 'POST',
    headers: [tokenId: "AHGFCM..."],
    body: "CI/CD issue found in ${report.metadata.projectName}, file ${location.file}, commitSha: ${commitSha}",
    context: 'security/secret'),
  @exitcode(177)
```

{% hint style="info" %}
This guardrail must be run server-side, as a [CI/CD Audit](https://docs.xygeni.io/xygeni-scanner-cli/xygeni-cli-overview/guardrails/ci-cd-audit-analysis).
{% endhint %}

#### Critical or High Issues

* Condition: Issues with 'high' or 'critical' severity.
* Action: Break the build with a specific exit code.

```xyflow
# Critical or High Issues
guardrail "no_critical"
on any
    when severity in  ["critical", "high"]
then
    @exitcode(200)
```

#### New Critical or High Issues

* Condition: New issues with 'high' or 'critical' severity.
* Action: Break the build with a specific exit code.

```xyflow
# New Critical or High Issues
guardrail "no_new_critical"
on any
    when @is_new() and severity in  ["critical", "high"]
then
    @exitcode(201)
```

#### Any Secret

* Condition: any secret
* Action: Break the build with a specific exit code.

```xyflow
# Any Secret
guardrail "no_secrets"
on secrets
    when severity in ["critical", "high", "low", "info"]
then
    @exitcode(202)
```

#### Malware detection

* Condition: detect any kind of malware
* Action: Break the build with a specific exit code.
* This guardrail is divided in 4 small guardrails, they are intended to be used together using --fail-on="#guardrail1,#guardrail2..."

```xyflow
# Deps Malware
guardrail "deps_malware"
on deps
    when id starts-with "MAL"
then
    print_all:@print("Malware has been detected on dependencies:" + @count($)),
    @exitcode(204)
```

```
# Suspect Deps Malware
guardrail "suspectdeps_malware"
on suspectdeps
 when type="malware_dependency"
 then
    print_all: @print("Malware has been detected on some dependencies that are marked as susp:" + @count($))  ,
    @exitcode(205)
```

```
# SAST Malware
guardrail "sast_malware"
on sast
when cwes contains "CWE-506"
 then
    print_all: @print("The product contains code that appears to be malicious.")  ,
    @exitcode(206)
```

```
# Other Malware
guardrail 'no-malware'
on malware
  when severity in ['critical','high','low']
  then
    print_all:@print("Malware has been detected:" + @count($)),
    @exitcode(207)
```
