Governance Specification Language (GSL)
CloudGuard Governance Specification Language (GSL) is a syntax to define posture management rules, which can be included in rulesets in the CloudGuard Posture Management. GSL has a core language augmented by a set of functions that add domain-specific functionality for different cloud providers (AWS Amazon® Web Services. Public cloud platform that offers global compute, storage, database, application and other cloud services., Azure Collection of integrated cloud services that developers and IT professionals use to build, deploy, and manage applications through a global network of data centers managed by Microsoft®., GCP Google® Cloud Platform - a suite of cloud computing services that runs on the same infrastructure that Google uses internally for its end-user products, such as Google Search, Gmail, Google Drive, and YouTube., Alibaba Cloud Cloud computing platform that provides cloud computing services to online businesses and Alibaba's own e-commerce ecosystem., and Kubernetes Kubernetes, often abbreviated as “K8s”, orchestrates containerized applications to run on a cluster of hosts.). These functions include IP addresses and networking, cloud entities such as instances, strings matching, date & time, etc.
Use Case
GSL is used to create compliance rules and Intelligence queries that you can run on your cloud environments to assess them. CloudGuard provides a graphical interface called GSL Builder or GSL Editor that helps you build the rule in the applicable context. For more information, see GSL Builder.
Rule Syntax
GSL rules have the form:
|
The Target is the cloud entity type that the rule checks, for example, instance or SecurityGroup. Each rule can check only one target.
You can qualify the target to match a smaller set of entities that you add with the where keyword.
The formal definition of the target is:
|
Where:
-
entity_type - The pivot cloud entity type that CloudGuard checks, for example, Instance, SecurityGroup, ELB.
-
expression - Can be any complex GSL expression to match the target entity type.
Examples:
|
|
The Condition is the actual essence of the rule. It contains the attributes of the target that CloudGuard examines. The condition can be a complex GSL expression that tests some attributes related to the target.
Context Travel
When you use a command that references an array (with, contain, contain-any, contain-all, contain-none, groupBy
, and so on), you execute an action that searches in the array's internal objects.
Each time you use array commands, you create a new context and delve deeper into the array. In a sense, you travel deeper into the child objects of the array which themselves can also be arrays. This is called child context travel.
You can use commands to refer back to the parent context and return to the parent array.
To do this, you can use some types of commands. Each command has a text representation and a symbol representation.
Command Symbol |
Command Text |
Description |
---|---|---|
$ |
this() |
Reference the current array context level. The current array you have reached in the GSL query |
~ |
root() |
Reference the topmost level of the array context. The root level of any GSL query is the asset itself. |
^ |
parent(n) |
Reference the n level above the current array context. |
To travel more than one context level, type more than one symbol (^^ or ^^^) or use its text equivalents, such as parent(2) or parent(3).
Examples
-
The $ refers to each role in the roles array.
ServiceAccount should not have roles contain-any [ $ like 'roles/iam.serviceAccount%']
-
This compares the tags value with the value in the ‘instanceType’ field.
Instance should have tags contain [ value like ~.instanceType ]
-
In this example, the ‘^.name’ equals to ‘IamUser.name’.
IamUser should have firstAccessKey with [ isActive = 'true' and ^.name like '%d%']
Expressions
The % wildcard indicates zero or more characters.
For example, like '%er' matches the strings 'smarter', 'lover', 'her', etc.
The % wildcard can appear a number of times in an expression. For example, like '%DB%' matches 'MongoDB', 'DB123' and 'prodDB_111'.
Wildcards are used with Like and Unlike.
Examples:
-
This checks the region of an instance, and the name of the region must contain the characters “eu_”. Strings like ‘eu_central_1’ or ‘eu_west_2’ can match.
Instance should have region like 'eu_%'
-
This rule checks that ‘*’ character does not exist in the domain name.
AcmCertificate should not have domainValidationOptions contain [ domainName like '%*%']
These expressions are of the form:
|
Where:
-
property_name - The applicable property of the entity being tested.
-
comparison_operator - One of the operators: = , < , >, !=, <=, >= , like.
-
expected_value - An expression you want to test with (can be a string, number, a different property, or a function result).
Examples:
|
|
|
KMS AWS Key Management Service (AWS KMS) - A managed service that simplifies the creation and control of encryption keys that are used to encrypt data. where isCustomerManaged=true and deletionDate<=0 and isSymmetricKey=true should have rotationStatus=true |
The "like" Comparison Operator
The GSL like operator is almost the same as to 'like' syntax in SQL. It allows you to match text with wildcards (%). You can like on string values.
The like comparisons are case-insensitive, meaning that 'a' like 'A' is true.
Example:
|
EcsService should not have role.combinedPolicies contain [name like '%admin%'] |
The "unlike" Comparison Operator
The unlike operator is the opposite of like.
The keyword have is not mandatory. It makes a rule more readable. The GSL parser removes it when processing the rule.
|
ELB should have accessLog.enabled=true |
It is useful when you write rules that are also human-readable.
The 'in' built-in function verifies that a given object's property value is found in a list of user-defined values.
Syntax:
|
Examples:
|
|
|
IamUser should have mfaType in('Virtual','Hardware') |
This rule makes that a property of the type 'list' contains a specific item. The syntax allows you to build complex expressions to find or match an item.
Syntax:
|
Where:
-
property_name - The property of the entity being tested.
-
containment-operator - Is one of the following:
-
contain or contain-any - Are syntactically the same and return a Boolean True or False result.
These options allow you to build rules that look correct in English. If any of the collection elements satisfies the query expression, then the full 'contain' statement is considered true.
Examples:
VMInstance should not have serviceAccounts contain [isDefaultServiceAccount=true]
Region should have accessAnalyzers contain-any [status='ACTIVE']
-
contain-all - Requires all elements in the collection to satisfy the expression and returns a Boolean True or False result.
Example:
ApiGateway should have resources contain-all [ (methods contain-all [ apiKeyRequired=true ]) or (methods isEmpty()) ] or authorizers isEmpty() = false
-
contain-none - Requires none of the elements in the collection to satisfy the expression, and returns a Boolean True or False result.
Example:
SQLServer should have firewallRules contain-none [ startIpAddress='0.0.0.0' and endIpAddress='255.255.255.255'
-
with - Is almost the same as contain, but returns a list of elements matching the expression (where the number of items in the list can be counted).
-
-
Expression - In the square brackets block
[ ]
can be any complex GSL expression. It defines the query to test each collection item with.If the list is blank, the expression returns as False.
Examples:
Iterate each rule in the SecurityGroups' inboundRules collection. Each rule is matched with the properties of port and scope:
|
This example is a nested expression - the outer 'with' iterates each nic in the nics collection, and the inner 'contain-any' iterates each security group:
|
This example counts the number of IAM Identity and Access Management (IAM) - A web service that customers can use to manage users and user permissions within their organizations. users with Admin access, and checks that it is not more than 3.
|
These expressions make sure that a property exists and that it is not empty.
CloudGuard can identify if a property type is empty, including empty collections, empty objects, null, 0, or empty strings.
Syntax:
|
Examples:
-
These are equivalent:
Instance should have vpc
Instance should vpc
Instance should have not vpc isEmpty()
-
These are equivalent:
SecurityGroup where outboundRules...
SecurityGroup where outboundRules.length >0
These are regular expressions that match the text in an entity name or element. The expression ‘regexMatch’ can be applied to every element, including arrays and objects.
Syntax:
|
Examples:
|
StorageAccount should have networkRuleSet.bypass regexMatch /.*AzureServices.*/ |
SystemManagerParameter where name regexMatch /(pass|user|login|pwd|key|secret)/ should have parameterType='SecureString' |
You can make complex expressions from multiple simple ones with the operators and, or, not, (<expr>)
AND
Make sure that the two sides of the and keyword are true.
Syntax:
|
Examples:
-
The instance should satisfy the existence of property (name), and the existence of an element in a collection (tags). It could also be written as two separate rules.
Instance should have name and tags with [key='owner']
-
A rule that cannot be logically split as each element is validated considering the two conditions:
SecurityGroup should not have inboundRules with [port=22 and scope='0.0.0.0/0']
OR
Make sure that the each side of the or keyword is a true expression.
Syntax:
|
Examples:
|
AcmCertificate should not have status='FAILED' or status='VALIDATION_TIMED_OUT' |
NOT
Satisfies (returns true) if the following expression is false, and vice versa.
Syntax:
|
Examples:
|
VMInstance should not have canIpForward=true |
Parenthesis
Use parenthesis "(", ")" for complex expressions that contain multiple and and or expressions, to remove ambiguities.
Syntax:
|
Examples:
|
S3Bucket should not have policy.Statement with [Effect='Allow' and (Principal='*' or Principal.AWS='*') and Condition isEmpty()] |
|
Note - Without the parenthesis, the first rule is ambiguous:
Because it is not understandable if you mean:
The parser uses one of the options, but it is recommended not to rely on an undocumented default implementation, and as an alternative, use '( )' to make the expression unambiguous. |
This expression enumerates a list of items (defined by the following clauses in the GSL rule). The expression can be used with the items and with keywords, after which additional expressions can then be added. The target of the operation is the full list of items. If one or more items in a list fail a rule condition, the result of the rule evaluation is 'fail'. But, in an assessment, this is considered as one failure.
List expressions can also be used with the groupBy keyword to evaluate the number of items in an enumerated list. Grouping aggregates items (in a list-type data entity) by a specific attribute, which can then be used in logical expressions.
Syntax:
|
Examples:
-
This enumerates a list of instances, and checks that their number is less than a limit.
List<Instance> should have items length() < 50
-
The with keyword is used to filter the list for a specific name before it is enumerated, and then the result is checked with a condition.
List<Instance> should have items with [name like 'db'] length() < LIMIT
-
This enumerates the Security Groups in each VPC, and checks that there are less than 100 in each (the AWS limit).
List<SecurityGroup> should have items groupBy [vpc.id] contain-all [values length() < 100]
Data Types
GSL has different syntax for strings (textual values) and numeric values.
Strings are surrounded by single quotation marks. For example, 'my string value'.
Examples:
|
VpcEndpoint should have tags contain [key like '%Name%'] |
Numbers are written without quotes.
Example:
|
List entities have a length property (entity.length). For example, inboundRules, in the example above. As an alternative, use the length() function.
Examples:
|
|
Functions
The core GSL syntax is enriched by internal functions that provide domain-specific functionality in multiple areas such as IP addresses, dates, and string matching.
Syntax:
|
Where:
-
property_name is the property/object we wish to operate on (equivalent to functions in object-oriented languages)
-
function_name is the name of the functions from the above list params the required parameters based on the type of the function, separated by
General Functions
Checks if the object/property is empty based on the property type. It returns 'true' for empty collections and empty objects (and as null values, 0 empty strings)
Parameters:
None
Examples:
These are equivalent:
|
|
EcrRepository should not have encryptionConfiguration.kmsKey isEmpty() |
Returns the length of a list. Follow this function with a comparator: >, <, =, >=, <=, !=.
Parameters:
None
Examples:
|
VMInstance should have labels length() > 0 |
Returns 'true' if the attribute value is contained in the provided list of values.
Parameters:
List of values to match.
Examples:
|
PostgreSQL should have logsConfiguration contain [ name='log_retention_days' and value in ('4', '5', '6', '7')] |
Splits a string based on a provided separator and converts it into an array
Parameters:
Position |
Description |
Values |
---|---|---|
1 |
Separator |
The separator to use when splitting the string |
Example:
Make sure that the instance name format is x-y-z (more tests could be applied on x, y and z)
|
Concatenates multiple strings into one string. This function can be useful to dynamically concatenate entity attributes.
Parameters:
Position |
Description |
Values |
---|---|---|
1 |
Separator |
The separator to use when concatenating the strings |
... |
Strings |
The strings to concatenate |
Examples:
This example makes sure that the instance name is in the format "PROD-<Instance ID>
" (for example, PROD-i-a0b01c01
).
|
This example is for the instance names of the format like t2.micro-us_east_1.
Instance should have tags contain [ value like join('-', ~.instanceType ,~.region)] |
Returns true if the attribute value contains the environment ID or is equal to it.
Parameters:
None
Example:
|
Returns 'true' if a target property has values identified as secrets. The function works with entities, inner object, arrays, and single-string properties.
Parameters:
None
Examples:
-
All the instance fields and subfields should not have secrets.
Instance should not containSecrets()
-
EcsTask should not have an inner vpc object that has fields containing secrets.
EcsTask should not have vpc containSecrets()
-
None of the tags of the instance should contain secrets.
Instance should not have tags containSecrets()
-
IAM role should not have external findings with resource names that contain exposed secrets.
Iam should not have externalFindings.findings with [ resourceName containSecrets() ]
Networking Functions - General
Checks for the existence of a subnet that is not part of the known private routable (for example, non-RFC 1918 [1]) IPv4 addresses.
Example:
This Compliance rule is applied only to EC2 Amazon EC2 - A web service for launching and managing Linux/UNIX and Windows Server instances in Amazon data centers. Instances that have one or more NICs that are currently associated with one or more publicly routable IPv4 addresses, which are not considered as the known private ones.
|
Parameters:
None
Counts the number of possible IP addresses in the provided CIDR. It removes the broadcast address
Parameters:
None
Example:
|
Operating on an IP address or a CIDR network and testing if it is fully contained in any of the provided networks.
Parameters:
List of CIDRs representing networks to test
Example:
This rule checks that there is no SSH connection from non-local or 'friendly' networks: '1.2.3.4/10','5.6.7.8/24'
|
Operating on an IP address or a CIDR network and testing if it has some overlap with any of the provided networks.
Parameters:
List of CIDRs representing networks to test
Example:
In this example, '10.1.2.0/24','192.168.100.0/24' (internal) networks belong to a third party peered with our VPC. Make sure that you do not allow these networks to SSH into the servers.
|
Check if a field is in CIDR format.
Parameters:
None
Example:
|
Checks if the attribute references a Security Group A set of access control rules that acts as a virtual firewall for your virtual machine instances to control incoming and outgoing traffic..
Some attributes can contain multiple types of objects (that is, a Security Group or a CIDR). This function determines if the value is a Security Group.
Parameters:
None
Example:
This example makes sure that security groups should only allow traffic from a different security group, and not a CIDR
|
Networking Functions for AWS NACL and MS Azure NSG
AWS NACL and MS Azure NSGs have different firewall semantics.
The firewall rules are in an order and may contain explicit 'DROP'. This makes the order of the rules very important.
These functions operate on a list of rules.
Returns the number of IP addresses (hosts) that can be connected to the applicable port protected by this firewall.
Parameters
Position |
Description |
Values |
---|---|---|
1 |
Port number(mandatory) |
any valid port number, such as 22, 80, and so on |
Example:
This rule prevents having overly permissive port 22 (that is allowed to more than 256 addresses). This is a rule for Azure Virtual Machines.
|
Returns the number of public IP addresses (hosts) that can be connected to the applicable port protected by this firewall.
Parameters:
Position |
Description |
Values |
---|---|---|
1 |
Port number(mandatory) |
any valid port number, such as 22, 80, and so on |
Example:
This rule prevents having overly permissive port 22 (that is allowed to more than 256 addresses). This is a rule for Azure Virtual Machines.
|
Returns 'true' if the provided IP addresses / CIDRs are allowed to connect to the specified port.
This method returns 'true' if one or some of the addresses in the provided CIDRs are allowed to connect.
Parameters:
Position |
Description |
Values |
---|---|---|
1 |
Port number (mandatory) |
a valid port number, such as 22, 80, and so on |
2 |
CIDR to test (a minimum of 1 is mandatory) |
CIDR syntax like 1.2.3.4/24 |
... |
other optional CIDRs |
CIDR syntax like 1.2.3.4/24 |
Example:
This rule prevents SSH access from not approved third-party networks such as 1.2.3.0/24', '5.5.6.7/32
.
|
Returns 'true' if the specified port can only be accessed by internal IP addresses
This method returns 'true' if one or some of the addresses in the provided CIDRs are allowed to connect.
Parameters:
Position |
Description |
Values |
---|---|---|
1 |
Port number (mandatory) |
Any valid port number, such as 22, 80 and more |
... |
Additional CIDR to mark as private (optional) |
CIDR syntax like 1.2.3.4/24 |
Example:
This rule prevents SSH access from public IP addresses.
|
Resource Functions
These functions return values for properties of assets. Especially, they can return secondary values for assets (for example, the value of a rule address for an SG assigned to an EC2 instance).
-
Returns the first instance of a resource, based on a field and value.
Parameters:
Position
Description
Value
1
resourceType
An asset property, for example, 'subnet', or 'vpc'
2
resourceFieldValue
The value of a resource property
3
resourceFieldName
The name of the resource property
Example:
Instance should have getResource('Subnet', subnet_name, 'name') with [cidr = '172.31.32.0/20']
-
Returns the first instance of a resource, given the type and an id.
This is almost the same as the instance above, only the field is assumed to be the ID of the specified asset type.
Parameters:
Position
Description
Value
1
resourceType
An asset property, for example, 'subnet', or 'vpc'
2
resourceId
The resource id to be returned, for example, subnet_id
Example:
Instance should have getResource('Subnet', subnet_id) with [cidr = '172.31.32.0/20']
SecurityGroup should have getResource('VPC', property_with_the_vpc_id) getValue(property_in_VPC_entity) = true
-
For AWS KMS entities only, returns the last instance that matches one of the resources below and an ID.
Parameters:
Position
Description
Value
1
resourceType
A constant value: KMS
2
KMSId
The resource id to be returned, whatever of these matches:
-
arn
-
id
-
aliases.arn
-
multiRegionConfiguration.primaryKey.arn
-
multiRegionConfiguration.replicaKeys.arn
Example:
SecretManager should have getResource('KMS', kmsKeyId) getValue('isCustomerManaged') = true
-
This function returns a list of instances of a resource, based on a field and a value. This is almost the same as getResource above but returns a list of all instances matching the field and value.
Parameters:
Position |
Description |
Value |
---|---|---|
1 |
resourceType |
An asset property, for example, 'subnet', or 'vpc' |
2 |
resourceFieldValue |
The value of a resource property (optional value) |
3 |
resourceFieldName |
The name of the resource property (optional value) |
Example:
|
Returns the value of an object, given its path.
Parameters:
Position |
Description |
Value |
---|---|---|
1 |
path |
The path to a resource, for example, 'vpc.cidr' |
Examples:
|
You can use getValue(n) with numeric arguments to index lists.
|
Returns an array of values that matches the provided path parameter.
Parameters:
Position |
Description |
Value |
---|---|---|
1 |
path |
The path to a resource, for example, 'vpc.cidr' |
Example:
|
Supports AWS NACL, AWS Security Group, MS Azure Network Security Group, and GCP Security Group assets.
Verifies that a minimum of one port, which is part of the port list, also exists in the application configuration range.
Parameters:
Position |
Description |
Value |
---|---|---|
1 |
Port number or GenericList |
GenericList or any valid port number, such as 22, 80 etc... |
Examples:
|
|
Time Functions
Test if the date property in the UNIX time format is before, that is, smaller than the desired relative time.
Time is relative to real-time evaluation time rather than the date of rule authorship and can be defined with each of the supported time units.
Parameters:
Name |
Description |
Values |
---|---|---|
count |
The number of the units; negative numbers resolve into past dates |
Any positive or negative number such as -5, 10, ... |
unit |
The measurement unit |
A string - 'minutes', 'hours', 'days', 'months' |
Examples:
This verifies that no instance was launched more than three months ago.
|
This verifies that no certificate of an ALB-secured listener expires in seven days.
|
Test if the date property in the UNIX time format is equal to or is greater than the desired relative time.
Time is relative to real-time / evaluation time rather than the date of writing the rule and can be defined with each of the supported time units.
|
Note - Unlike before(), this function also includes the desired time. |
Parameters:
Name |
Description |
Values |
---|---|---|
count |
The number of the units; negative numbers resolve into past dates |
Any positive or negative number such as -5,10, and so on |
unit |
The measurement unit |
A string - 'minutes', 'hours', 'days', 'months' |
Examples:
This verifies that user with an enabled password used it during the last 45 days.
|
Do an action for the instances that were launched in the last two weeks.
|
|
Best Practice - To use fixed (non-relative) time, you can use a standard numeric comparison with the UNIX date/time format. For example, to do something with all instances that were created before 1/1/2016 (which translates into the UNIX time 1451606400), use:
|
Returns the time difference between two dates, in time units of your selection (outputUnit).
Parameters:
Position |
Description |
Values |
---|---|---|
secondDate |
The other date to compare to |
date (string) |
outputUnit |
The desired time unit of the output |
'seconds', 'minutes', 'hours', 'days', 'months', 'years' (string) |
Date formats:
-
Unix time (time in seconds)
-
YYYY-MM-DDTHH:mm:ss.SSSZ
-
YYYY-MM-DDTHH:mm:ss.SSSSSSSZ
-
YYYY-MM-DDTHH:mm
-
YYYY-MM-DD
-
MM-DD-YYYY
-
MM/DD/YYYY HH:mm:ss
-
Month (as string) DD, YYYY HH:mm:ss
Tip:
To create a date in local time, include the time explicitly as in '2019-06-11T00:00'.
When you create a date without specifying the time, you get the date set in UTC.
Examples:
Check if the creation date (createDate) of a user (IamUser) is equal to or less than 90 days from the input date ('2022-01-17T11:09:00.000Z').
|
Check if the time difference between the time of creation (createdDateTime) of a user (User) and the input date is equal to 10 minutes.
|
Check if the updateDate of a RamUser is equal to or less than 20 days from the input date.
|