# Syntax

This document provides information about the VQL (Veza Query Language) query specification, which offers a SQL-like interface to the [Assessment Query API](https://docs.veza.com/4yItIzMvkpAvMVFAamTf/developers/api/query-builder). See the [Quick Start Guide](https://docs.veza.com/4yItIzMvkpAvMVFAamTf/features/search/vql/vql-quickstart) for detailed usage and examples.

* [Concepts](#concepts)
* [VQL Expression Syntax](#vql-expression-syntax)
* [NodeSpec Syntax](#nodespec-syntax)
* [Usage Examples](#usage-examples)
  * [Where Clause Operators](#where-clause-operators)
    * [Comparison Operators](#comparison-operators)
  * [Data Types and Restrictions](#data-types-and-restrictions)
  * [Entity Attributes](#entity-attributes)
* [Permissions](#permissions)
  * [System Permissions](#system-permissions)
  * [Effective Permissions](#effective-permissions)
* [Intermediate Nodes](#intermediate-nodes)
* [Modifiers and Filters](#modifiers-and-filters)
* [Enrichment Clauses](#enrichment-clauses)
* [Result Output](#result-output)

### Concepts

> **Early Access:** VQL is subject to modifications as we add and improve functionality. Future updates will aim to preserve compatibility with earlier versions.

A VQL query is composed of the following components:

* **Source Nodes:** Node types in VQL represent entity types within Veza's Identity Graph. Each node type can have many individual instances, returned as rows in the query output.
* **Path requirements:** Graph nodes can be interrelated, forming complex graph structures. Specifying related nodes filters results with a matching relationship.
* **Filters and Modifiers:** Filter expressions (`WHERE` clauses) to constrain results based on attributes or other criteria.

### VQL Expression Syntax

A VQL query always includes a `SHOW` statement describing the source node type. The general syntax is:

```vql
SHOW [NodeSpec] -- Specifies the source node type
[ [NOT] RELATED TO [NodeSpec]] -- Filters results by related node type
[WITH | NOT WITH] PATH [NodeSpec] -- Filters on intermediate nodes
[WHERE (filter expressions)]
[RESULT INCLUDE [DESTINATION NODES | DESTINATION NODE COUNT | PATH SUMMARY]]
[HAVING [entity_result_count (condition) | percentage_of_total_count (condition)]]
[WITH QUERY OPTIONS (options)]
[ENRICH WITH [NodeSpec | (NodeSpec, ...)]] -- Enrich source nodes (default)
[ENRICH SOURCE WITH [NodeSpec | (NodeSpec, ...)]] -- Explicitly enrich source nodes
[ENRICH DESTINATION WITH [NodeSpec | (NodeSpec, ...)]] -- Enrich destination nodes
[AFTER CURSOR 'cursor_token'] -- For pagination, use cursor token from previous results
[LIMIT <number>]; -- Limit number of results returned
```

### NodeSpec Syntax

A `NodeSpec` describes a source or destination entity type. It can include attribute selection (which columns to return) and filters using a `WHERE` clause. The full syntax is:

```vql
[NodeType] [{ attribute1, attribute2, ... }] [WHERE (<attribute_name> <operator> <value> [AND|OR] ...)]
```

Basic Components:

* **NodeType:** The type of node (e.g., `AwsIamUser`, `OktaGroup`)
* **Attribute Selection**: Optional curly braces `{ }` containing a comma-separated list of attributes to include in the results
* **WHERE:** Optional clause that applies attribute-based filters to the nodes using the selected operators

### Usage Examples

Basic node specification (returns all attributes):

```vql
SHOW AwsIamUser
```

Select specific attributes to display:

```vql
SHOW AwsIamUser { created_at, is_active, full_admin }
```

Apply filters without attribute selection:

```vql
SHOW AwsIamUser WHERE is_active = true
```

Combine attribute selection and filters:

```vql
SHOW AwsIamUser { created_at, email } WHERE is_active = true AND risk_score > 70
```

In this more complex example, we select specific attributes for both the source (OktaUser) and destination (S3Bucket) nodes while also applying filters to the results:

```vql
SHOW OktaUser { email, last_login_at, department }
WHERE last_login_at >= CURRENT_DATE - 30
RELATED TO S3Bucket
WHERE block_public_acls = false
WITH PATH OktaGroup
WITH QUERY OPTIONS ( over_provisioned_score > 85 )
```

#### Where Clause Operators

A range of operators can be used to filter results depending on node properties. Currently, VQL supports the comparison operators:

**Comparison Operators**

| Operator                        | Supported Data Types               | Example                                                                                                                                        |
| ------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `<`, `>`, `<=`, `>=`, `=`, `!=` | NUMERIC, TIMESTAMP, TIME FUNCTIONS | <p><code>risk\_score < 80</code><br><code>created\_at >= '2023-10-05 14:30:00.123'</code><br><code>created\_at < CURRENT\_DATE - 30</code></p> |
| `STARTS_WITH`                   | STRING                             | `name STARTS_WITH 'S'`                                                                                                                         |
| `ENDS_WITH`                     | STRING                             | `name ENDS_WITH 'E'`                                                                                                                           |
| `LIST_CONTAINS`                 | STRING                             | `permissions LIST_CONTAINS 'iam:PassRole'`                                                                                                     |
| `LIST_ALL_ELEMENTS_IN`          | STRING                             | `accounts_assumed_by LIST_ALL_ELEMENTS_IN ('accountid1', 'accountid2')`                                                                        |
| `REGEX`                         | STRING                             | `name REGEX 'TEst.*'`                                                                                                                          |
| `LIST_ANY_ELEMENT_EQ`           | STRING                             | `permissions LIST_ANY_ELEMENT_EQ 'iam:SetDefaultPolicyVersion'`                                                                                |
| `LIST_ANY_ELEMENT_STARTS_WITH`  | STRING                             | `cai_tags LIST_ANY_ELEMENT_STARTS_WITH 'P'`                                                                                                    |
| `LIST_ANY_ELEMENT_CONTAINS`     | STRING                             | `cai_tags LIST_ANY_ELEMENT_CONTAINS 'policy'`                                                                                                  |
| `LIST_ANY_ELEMENT_ENDS_WITH`    | STRING                             | `cai_tags LIST_ANY_ELEMENT_ENDS_WITH 'admin'`                                                                                                  |
| `LIST_ANY_ELEMENT_REGEX`        | STRING                             | `cai_tags LIST_ANY_ELEMENT_REGEX '::'`                                                                                                         |
| `IS NULL`                       | STRING                             | `show Key WHERE last_rotated_at IS NULL`                                                                                                       |
| `IS NOT NULL`                   | STRING                             | `show Key WHERE last_rotated_at IS NOT NULL`                                                                                                   |

#### Data Types and Restrictions

* **Data Types:** VQL supports boolean, integer, string, and null data types.
* **Case Sensitivity:** VQL is typically case-sensitive for:
  * **Node Types:** Must be written exactly as defined (e.g., `AwsIamUser`, not `awsiamuser`).
  * **Attribute Names:** Must match the exact casing (e.g., `is_active`, not `Is_Active`).

#### Entity Attributes

Entity attributes in VQL are used to filter and select graph nodes. They consist solely of alphanumeric characters or underscores (e.g., `last_login`, `email_address`).

### Permissions

In addition to attribute filters, VQL queries can use permission filters. Both system permissions and effective permissions are supported.

#### System Permissions

System permissions are raw, system-level permissions and vary depending on the specific integration and resource. The following query identifies users that specifically have the ability to create new S3 buckets:

```vql
SHOW AwsIamUser
RELATED TO S3Bucket
WITH SYSTEM PERMISSIONS = ANY ('s3:CreateBucket');
```

* **ANY:** Used to filter for any of the supplied permissions (logical OR).
* **ALL:** Ensures that all permissions passed should be present on the resource (logical AND).

#### Effective Permissions

Veza supports nine different effective permissions. These are abstracted permissions that express system permissions in common groups:

* `METADATA_READ`
* `METADATA_WRITE`
* `METADATA_CREATE`
* `METADATA_DELETE`
* `DATA_READ`
* `DATA_WRITE`
* `DATA_CREATE`
* `DATA_DELETE`
* `NON_DATA`

The following query shows all AWS IAM Roles that grant effective permissions to read or write S3 bucket metadata:

```vql
SHOW AwsIamRole
RELATED TO S3Bucket
WHERE is_active = false
WITH EFFECTIVE PERMISSIONS = ALL ('METADATA_READ', 'METADATA_WRITE');
```

Effective permissions filters require a destination type (specified by the RELATED TO clause).

### Intermediate Nodes

Intermediate node options include or exclude results based on certain node types within the path, for analyzing complex relationships that involve hierarchies of groups, roles, or entities. These queries are often used to find users whose access is (or is not) granted by group membership or role assignment:

* **Including Intermediate Nodes:**

  ```vql
  SHOW OktaUser
  RELATED TO SnowflakeTable
  WITH PATH SnowflakeUser;
  ```
* **Excluding Intermediate Nodes:**

  ```vql
  SHOW OktaUser
  RELATED TO SnowflakeTable
  NOT WITH PATH OktaGroup;
  ```

### Modifiers and Filters

Use the `WHERE` clause to apply filters. You can combine multiple conditions with `AND` or `OR` statements:

Example:

```vql
SHOW AwsIamUser
WHERE is_active = true AND risk_score > 70;
```

### Pagination

VQL supports pagination for queries that return large result sets. Two keywords control pagination behavior:

* **LIMIT:** Restricts the number of results returned in a single query
* **AFTER CURSOR:** Used with a cursor token to retrieve the next set of results

Example of initial query with limit:

```vql
SHOW AwsIamUser
RELATED TO S3Bucket
LIMIT 50;
```

For subsequent requests, use the cursor token from the previous response:

```vql
SHOW AwsIamUser
RELATED TO S3Bucket
AFTER CURSOR 'elcl9uYW1lIjoicjJkMiJ9fQ=='
LIMIT 50;
```

### Enrichment Clauses

Enrichment clauses join additional node types to query results, augmenting source or destination nodes with related entity data from adjacent integrations. For example, you can enrich IAM role results with employee records from Workday, or annotate destination resources with metadata from another system.

| Clause                    | Description                                        |
| ------------------------- | -------------------------------------------------- |
| `ENRICH WITH`             | Join nodes connected to the source side (default)  |
| `ENRICH SOURCE WITH`      | Explicitly join nodes connected to the source side |
| `ENRICH DESTINATION WITH` | Join nodes connected to the destination side       |

Multiple enrichment clauses can appear in a single query. To enrich with more than one node type, provide a comma-separated list in parentheses.

{% hint style="info" %}
**Result type required:** Enrichment only populates data when the query uses `RESULT INCLUDE DESTINATION NODES` or `RESULT INCLUDE PATH SUMMARY`. On source-only queries, the `ENRICH` clause is accepted but silently ignored.
{% endhint %}

{% hint style="info" %}
**Single-match constraint:** If a source node is connected to more than one node of the enrichment type, the enrichment for that result row is omitted.
{% endhint %}

**Enrich source nodes with a single type:**

```vql
SHOW OktaUser RELATED TO AwsIamRole ENRICH WITH WorkdayEmployee
```

**Enrich source nodes with multiple types:**

```vql
SHOW OktaUser RELATED TO AwsIamRole ENRICH WITH (WorkdayEmployee, ActiveDirectoryUser)
```

**Enrich source and destination nodes separately:**

```vql
SHOW OktaUser RELATED TO AwsIamRole ENRICH SOURCE WITH WorkdayEmployee ENRICH DESTINATION WITH S3Object
```

**Combine enrichment with query options and pagination:**

```vql
SHOW OktaUser RELATED TO AwsIamRole
WITH QUERY OPTIONS (QUERY_TYPE = SOURCE TO DESTINATION)
ENRICH WITH WorkdayEmployee
LIMIT 10
```

### Result Output

By default, queries return a list of source entity types and attributes. Use the `RESULT INCLUDE` clause to specify how query results appear:

* **DESTINATION NODES:** Includes information about related destination nodes.
* **DESTINATION NODE COUNT:** Provides a count of related destination nodes.
* **PATH SUMMARY:** Summarizes the paths between source and destination nodes.

Example:

```vql
SHOW AwsIamUser
RELATED TO S3Bucket
RESULT INCLUDE DESTINATION NODES;
```
