# README
Cloud Armor Rules CLI
Cloud Armor Rules supports Common Expression Language (CEL) expressions to configure its behavior. The CLI provides support for command- line compilation and testing of Cloud Armor expressions in a manner which is consistent with how the service will evaluate these rules.
Getting Started
First, build the CLI:
go build -o rulescli github.com/cel-expr/cloud-armor-rules/cmd && chmod 0755 rulescli
This will produce a rulescli
binary in the current directory which can be
executed using ./rulescli
to provide a basic usage message.
Usage
The CLI provides three modes -expr
, -file
and -test
.
expr
The -expr=<expr>
flag indicates that the expression provided following the
flag will be compiled and output into a textproto format. The -output_format
flag can be used with the -expr
flag to produce either a textproto or binary
protocol buffer (binarypb) file as well.
Here's a simple example:
rulescli -expr="request.method == 'GET'"
If no flag is specified, the default behavior is equivalent to using -expr:
rulescli "request.method == 'GET'"
If used with output_format as textproto:
rulescli "request.method == 'GET'" -output_format=textproto
Will produce the following dev.cel.expr.CheckedExpr
output:
# proto-file: github.com/google/cel-spec/proto/cel/expr/checked.proto
# proto-message: dev.cel.expr.CheckedExpr
reference_map: {
key: 2
value: {
name: "request.method"
}
}
reference_map: {
key: 3
value: {
overload_id: "equals_string"
}
}
type_map: {
key: 2
value: {
primitive: STRING
}
}
type_map: {
key: 3
value: {
primitive: BOOL
}
}
type_map: {
key: 4
value: {
primitive: STRING
}
}
source_info: {
location: "<input>"
line_offsets: 24
positions: {
key: 1
value: 0
}
positions: {
key: 2
value: 7
}
positions: {
key: 3
value: 15
}
positions: {
key: 4
value: 18
}
}
expr: {
id: 3
call_expr: {
function: "_==_"
args: {
id: 2
ident_expr: {
name: "request.method"
}
}
args: {
id: 4
const_expr: {
string_value: "GET"
}
}
}
}
To produce a binary protocol buffer, use the following option:
rulescli -expr="request.method == 'GET'" -output_format=binarypb
An invalid expression will produce a list of issues to be resolved from the input:
> rulescli -expr "request.metho == 'GET'"
failed to compile expression: ERROR: <input>:1:1: undeclared reference to 'request' (in container '')
| request.metho == 'GET'
| ^
By default, these expressions would be able to test the currently exposed Cloud armor attributes. To test the next versions of attributes, like request.params and request.body, set the version of the expressions to VNext as follow:
rulescli -expr="request.method == 'GET'" -version VNext
file
The -file=<filename>
flag indicates that the expressions contained in the
provided file will be compiled.
Here's a simple example:
rulescli -file="fileExpr.txt"
Expressions in the file should be separated by the delimiter ';' and could extend to multiline expressions.
Contents for file fileExpr.txt:
request.method == "POST";
request.query.contains('XyZ') &&
request.path.startsWith('path');
request.path1.startsWith('path1') || request.method == "GET";
Expected output:
failed to compile expression: ERROR: <input>:1:1: undeclared reference to 'request' (in container '')
| request.path1.startsWith('path1') || request.method == "GET"
| ^
Error processing file: failed to compile expression: request.path1.startsWith('path1') || request.method == "GET"
Whereas, additional information could be fetched using -verbose flag as follow:
rulescli -file="test/fileExpr.txt" -verbose
Expected Output:
Reading file: fileExpr.txt
Processing expr at index: 0 , line: 1 expr: request.method == "POST"
Successfully compiled expression: request.method == "POST"
Processing expr at index: 1 , line: 3 expr: request.query.contains('XyZ') &&
request.path.startsWith('path')
Successfully compiled expression: request.query.contains('XyZ') &&
request.path.startsWith('path')
Processing expr at index: 2 , line: 4 expr: request.path1.startsWith('path1') || request.method == "GET"
failed to compile expression: ERROR: <input>:1:1: undeclared reference to 'request' (in container '')
| request.path1.startsWith('path1') || request.method == "GET"
| ^
Error processing file: failed to compile expression: request.path1.startsWith('path1') || request.method == "GET"
whereas, for the following contents for file fileExpr.txt:
request.method == "POST" &&
request.query.contains('XyZ');
request.path.startsWith('path');;
request.path.startsWith('path1') || request.path.startsWith('path2');
request.scheme == 'http'; request.scheme == 'https';
request.headers['User-Agent'].contains('Chrome');
request.scheme == 'http' && request.method == 'GET' ||
request.path.startsWith('/path');
expected output:
Successfully compiled expression: request.method == "POST" &&
request.query.contains('XyZ')
Successfully compiled expression: request.path.startsWith('path')
Successfully compiled expression: request.path.startsWith('path1') || request.path.startsWith('path2')
Successfully compiled expression: request.scheme == 'http'
Successfully compiled expression: request.scheme == 'https'
Successfully compiled expression: request.headers['User-Agent'].contains('Chrome')
Successfully compiled expression: request.scheme == 'http' && request.method == 'GET' ||
request.path.startsWith('/path')
To print the AST expressions of CEL expressions from a file as textproto:
rulescli -file="test/fileExpr.txt" -output_format=textproto
By default, file flag would allow to test the currently exposed Cloud armor attributes. To test the next versions of attributes, like request.params and request.body, set the version of the expressions to VNext as follow:
rulescli -file="test/fileExpr.txt" -version VNext
Test
The -test
flag may be used to provide a file path to a test suite written as
YAML which indicates a test expr
value and a set of test cases whose format
is indicated below:
name: "Test Suite Name"
expr: >
<multiline-cel-expression>
tests:
- name: "<case-name>"
expect: <true|false>
error: 'error substring'
when: <variables>
The tests
value is a list of TestCase
objects which have only one of the
expect
or error
values set. A test which omits both of these fields will
implicitly expect an evaluation of false
; however, it is best to explicitly
set the test expectation.
Variables
The when: <variables>
field expects to receive a map of values whose structure
reflects the
documented attributes
in Cloud Armor. In Cloud Armor and in CEL, these attributes are flat, meaning
they do not reflect an object hierarchy and instead are treated as namespaced
values. In other words request.method
is a type string
field, but the
variable request
is not defined. For convenience, the YAML supports a
structured object as input for the sake of simplicity and reducing repetition
of test code.
New Attributes (Proposed for NextVersion)
-
request.body Represents the entire POST Body as string. e.g. Expression: request.body.contains('bad_data')
-
request.params It represents the query_parameters from URL in GET requests as well as key-value parameters from POST Body.
e.g. for request curl "https://www.example.com/nonauth/random1.cs?dest=/somepath"
expression would be:
has(request.params.dest) or has(request.params['dest'])
Similarly, it also supports accessing the nested keys as below:
request.params.keys.key1 or request.params['keys']['key1']
Execution
An end-to-end example of the file content might look as follows:
name: http-tests
expr: >
request.method == 'GET'
tests:
- name: 'request-method-matches'
expect: true
when:
request:
method: GET
When you are ready to run your tests, provide a fully qualified file name or referring to the
./rulescli -test $(pwd)'test/http-tests.yaml'
Disclaimer: This is not an official Google project