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:

NameTypeRequired
idstringyes
statusstringyes
noncestring or nullno

project

Project is a simple object with the following fields:

NameTypeRequired
idstringyes

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:

NameTypeRequired
idstringyes

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

NameTypeRequired
idstringyes

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:

  1. It doesn't make sense to write something like if: true && (expr). It effectively is the same as writing if: expr.
  2. The always only makes sense if you want to always run this :).