VAST's query language is designed for effective subsetting of data. The syntax
borrows from the terseness of
awk and from network-centric focus of
tcpdump, but enhanced with a rich type system.
A query is a boolean expression that declaratively defines the subset of
interest. Sub-expressions can be connected as conjunctions (
||), and negations (
!). Expression operands are either sub-expressions
or predicates at the leaves.
The following example shows an abstract syntax tree (AST) along with the corresponding expression.
Let's take a look at the individual components in more depth.
A predicate has the form
LHS denotes the left-hand
side operand and
RHS the right-hand side operand. The relational operator
op is typed, i.e., only a subset of the cross product
of operand types is a valid syntax. An operand is either an
extractor or a data value. Operands are always typed,
but for extractors the type can sometimes only be inferred at query runtime.
In this case, the type check also takes place lazily.
The following operators separate two operands:
|not equal to|
|in (left to right)|
|not in (left to right)|
|in (right to left)|
|not in (right to left)|
The table below illustrates a partial function over the cross product of
available types. Green cells represent a valid combination of
for the given set of operator classes.
An extractor retrieves a certain aspect of an event. VAST has the following extractor types:
Attribute: matches fields that have a particular attribute value.
Type: extracts all event types that have a field of a given type.
Field: extracts all fields whose name match a given record field name.
Attribute extractors have the form
x is the name of an attribute.
#timestamp extracts the event timestamp and requires an operand
time. A predicate with a
#timestamp extractor only considers event
types that have a field with the
#type == "zeek.conn"
#timestamp > 2 days ago
Type extractors have the form
T is the type of a field. Type
extractors work for all basic types.
:addr == 184.108.40.206
:count > 42M
"evil" in :string
Predicates with type extractors and equality operators can be written tersely
as data predicates. That is, if a predicate has the form
:T == X where
X is a value and
T the type of
X, it suffices to just write
X as query.
The predicate parser deduces the type of
X automatically in this case.
220.127.116.11 is a valid predicate and expands to
:addr == 18.104.22.168.
This allows for quick type-based point queries, such as
(22.214.171.124 || 80/tcp) && "evil".
Field extractors have the form
z match on
record field names. The access fields in nested records. Using a type name as
leftmost element before a
. is also possible.
A field extractor has suffix semantics. It is possible to just write
x.y.z. In fact, writing
z is equivalent to
*.z and creates a
disjunction of all fields ending in
ts > 1 day ago
zeek.conn.id.orig_h in 192.168.0.0/24
orig_bytes >= 10Ki