Expressions
Expressions are implemented as a way for you to describe the conditional, or to evaluate something based on the context you are in.
Currently, expressions are used in 3 places:
- Scheduling (
on.expr
, statement defines the expression that should evaluate to a boolean value) - Conditional step (
scans.[ID].steps[].if
, allows steps to be conditionally executed. It should evaluate to a boolean value) - Run expression (Within a run, you can use template expression in form of
${{ expression }}
. Expression should evaluate to a string, or an integer value.)
Syntax
We use a language called CEL. If you've ever used languages that look like C, you'll find this one pretty familiar.
In BountyHub, we handle expressions with something we call Context.
Think of Context
as a state. State is populated with predefined variables and structures. Then that state is used to evaluate the expression and get a result of some type.
For that reason, it is important that you are aware which expressions should evaluate to boolean values, and which ones should evaluate to a string value.
Context variables
Context variables are variables that are pre-populated by the server at the time the job is sent to the runner.
It allows you to create generic scans and run the command based on your new state. The following context variables are available:
- scans: Map of scans and their last 10 jobs for each scan
- project: Trimmed down project object
- workflow: Trimmed down workflow object
- revision: Trimmed down revision object
- vars: Map of project variables defined in your project settings
- always: Evaluates to
true
. Special purpose variable that allows you to run the step even if the job is about to fail. - success: Variable prepended to your expression that holds the current success status of the job.
- steps: Gradually populated list of step states.
Let's talk about them all:
scans
This is basically a map, with scan names used as keys, and job contexts used as values. It looks something similar to this:
{
"scan1": [], // list of scan objects
"scan2": [],
"scan3": []
}
The job object contains the following fields:
Name | Type | Required |
---|---|---|
id | string | yes |
status | string | yes |
nonce | string or null | no |
project
Project is a simple object with the following fields:
Name | Type | Required |
---|---|---|
id | string | yes |
It allows you to use the project id in order to fetch some object, like job result, from the BountyHub API.
workflow
Workflow is a simple object with the following fields:
Name | Type | Required |
---|---|---|
id | string | yes |
It allows you to use the workflow id in order to fetch some object from the BountyHub API.
revision
Revision is a simple object with the following fields
Name | Type | Required |
---|---|---|
id | string | yes |
vars
Vars is the map of key-value pairs that you configured as project variables in your project.
This maps string -> string
, and looks something like:
{
"domain": "bountyhub.org",
"tool_rate": "5",
"api_key": "api key for your tools if you need one"
}
success
This is a variable that has a bool type, and mutates after each step. Every step if
is prepended with success && (YOUR_EXPRESSION_HERE)
.
That means that if any step fails, the success
will evaluate to false
, causing steps to be skipped. You don't need to explicitly use this variable, but you should know that it exists.
always
This is basically a keyword. It is a constant evaluating to true
, but it has a special meaning. If the value of if
step expression is always
, this step will be always executed. The server will not prepend the success
. Keep in mind that the value cannot be something like: if: always && (steps[0].status == 'succeeded')
. There are 2 reasons for this:
- It doesn't make sense to write something like
if: true && (expr)
. It effectively is the same as writingif: expr
. - The
always
only makes sense if you want to always run this :).