Skip to content

Rules

The principle of ThreatGet is based on comparing a system model with a threat model. The system model is created by the user within the Enterprise Architect plugin. To do this, the user defines the elements and connectors to be used in the web-based interface. Elements and connectors are largely described by their properties. These properties can also be defined and assigned in the web-based interface. Properties are called TaggedValues within ThreatGet.

Example:

Example Diagram from Enterprise Architect

The threat model is based on a set of rules, which can also be defined within the web interface. The rules use their own "Domain Specific Language", which will be described here. The rules are based on previously defined elements and connectors, because only what is known can be queried.

A rule, also called expression, can be based on two approaches. On the one hand on a query for elements (ElementExpression) and on the other hand on the query of Connectors (ConnectorExpression). Generally speaking, rules or expressions describe states in a system model that can pose a certain danger. When comparing the system model and the threat model, these rules look for the conditions described in them. If a rules matches, a threat is generated for the affected elements, connectors and boundaries.

Below a short explanation of the most important terms is given.

Expression / Rule

Expressions in ThreatGet come with different quantifiers. They imply how often a specific expression can occur within a rule. Below you can find the list of quantifiers.

:?                                      -> zero or one time
:*                                      -> zero, one or multiple times
:+                                      -> one or multiple times

Expressions can be of different types. The expression type can be chosen when a new rule is created.

(left expression | right expression)    -> left or right expression

The Full specification of the language can be found at the bottom of the page.

Connector

In the general context, a connector describes a connection between two elements. This connection can be considered physical, as well as logical. For example, you want to check what kind of connection there is between two elements, wireless or wired. Or you want to investigate what kind of data is exchanged between these elements.

Tagged Value

A TaggedValue can be seen as an attribute / property of an element or connector. A TaggedValue is a key-value relationship and describes a single property. The user can define these TaggedValues within the web interface and then add them to elements and connectors. When defining a Tag, several possible Values can be assigned. One of these Values can then be selected during the modeling phase.

An example of this would be that you want to define a TaggedValue that describes which protocol a connector uses.

Tag = protocol
Values = HTTP, HTTPS, SMTP ..... 

Relation

A Relation describes the inner relationships of elements. Please take the following system model as an example:

Example Diagram for element relationship explanation

First, let's look at the left side. There we see a sensor which lies within two boundaries. The lower boundary represents its ROOT element. The boundary which also lies in this ROOT describes the PARENT element of the sensor. ROOT and Parent are both Ancestors of the sensor. The sensor is the Child of the Parent and a Descendant of the ROOT.

On the right you can see a Software element, which lies within only one boundary. This makes this boundary both his ROOT and his PARENT. In this case the Software is a direct child of the ROOT and PARENT and also a Descendant.

Examples

Element oriented

First, let's take a look at the rules that focus on a particular element (ElementExpression). Elements affected by a certain rule are searched for in the system model, from now on called diagram. The general structure of such a rule is as follows:

ElementExpression           -> ( SingleElementExpression | MultipleElementExpression )  TaggedValueFilter:? ConnectorFilter:* RelationFilter:* 
SingleElementExpression     -> "Type(" "\"" identifierString "\"" ")"    
MultipleElementExpression   -> "("  ( AndConnectedElements | OrConnectedElements ) ")"            
AndConnectedElements     -> ElementExpression _ "&" _  ElementExpression  ( _ "&" _ ElementExpression ):* 
OrConnectedElements      -> ElementExpression _ "|" _  ElementExpression  ( _ "|" _ ElementExpression ):*  

This may seem very complex in the beginning, but all the terms used in the description will be explained throughout the course of this document along with examples.

Single Element Expression

Let's start with the simplest case the SingleElementExpression.

Type("ANY")

This rule uses the keyword ANY to focus on every element inside the diagram. The result would be:

Marked example diagram type ANY rule

It is also possible to restrict the search by specifying a special "type identifier". This enables specific types of elements to be searched for within the diagram. The example below matches every element inside of the diagram with the type Sensor.

Type("Sensor")

The result is:

Marked example diagram type sensor rule

But one thing has to be mentioned here. Elements within the diagram can be defined in classes. For example, Sensor represents a class definition. The element Sensor inherits its TaggedValues to all of its sub-elements, such as Contact Senor.

Sensor top element with sub categories

If the type is a parent type subtypes will also be matched. If there is a Sensor and a Contact Sensor inside the Diagram both will be marked.

Example diagram with marked sensors

If only the specfic Contact Sensor should be addressed by the rule then the rule must be:

Type("Contact Sensor")

Example diagram with marked contact sensor

Multiple Element Expression

The MultipleElementExpression can be used to search for several elements within the diagram. Basically, a MultipleElementExpression consists of compound statement, a left side (ElementExpression), a conjunction or disjunction and a right side (ElementExpression). More precisely a compound statement is formed by connecting the left and the right side with AND or OR. These more specific rules must be enclosed in parentheses. This makes it possible to nest these expressions.

Example:

(Type("Contact Sensor") & Type("Electronic Control Unit")) 
(Type("Contact Sensor") | Type("Electronic Control Unit")) 

In this case both rules would yield the same result:

Example diagram with marked AND OR Connected Expression

Now we apply the following rule:

(Type("Contact Sensor") & Type("Actuator")) 

Example diagram with negative resulting AND Connected Expression

This yields no result for us. But if we change the AND to an OR, we can detect the Contact Sensor

(Type("Contact Sensor") | Type("Actuator")) 

Example diagram with positive resulting OR Connected Expression

We get different results because inside an AndConnectedElements rule both sides must apply. Inside a OrConnectedElements rule only one side must apply.

Additional Examples with nested expressions:

(Type("Contact Sensor") | Type("ANY") | Type("Actuator")) 
(Type("Contact Sensor") & Type("ANY") & Type("Actuator") & Type("Sensor")) 
(Type("Contact Sensor") | (Type("ANY") & Type("Actuator"))) 
((Type("Contact Sensor") | Type("ANY")) | (Type("ANY") & Type("Actuator"))) 
((Type("Contact Sensor") | Type("ANY")) | (Type("ANY") & (Type("Actuator") | Type("Memory")))) 

In addition to the type, a distinction can also be made according to other TaggedValues. Consequently, these distinctions can be understood as filters. First all matching elements are found and then sorted out according to the specified filter.

Possible Filters:

* TaggedValueFilter
* ConnectorFilter
* RelationFilter

TaggedValueFilter:

An TaggedValueFilter is there to distinguish elements in terms of their defined TaggedValues.

General structure:

TaggedValueFilter   -> ".tv(" Tagged Value ")" 

The user can specify these TaggedValues inside the diagram and then add TaggedValueFilters to the rules. By using the following TaggedValueFilter inside a rule, the rule focuses only on Sensors whose tagged value Authentication is set to No.

Type("Sensor").tv(Authentication = No)

The user can specify the unequal operator with !=. Please note that != YES is not the same as = No. The tagged value may also be undefined or contain a different string.

Type("Sensor").tv(Redundancy != Triple)

With this TaggedValueFilter the rule focuses only on Sensors whose tagged value Redundancy is NOT set to Triple. Please compare the following illustration:

Enterprise Architect element tagged value dropdown selection

It is possible to query for multiple tagged values.

Type("Sensor").tv(Authentication = No, Authorization != Yes)
Type("Sensor").tv(Authentication = No & Authorization != Yes)

Both queries above express the same thing. The , and & are synonyms for AND. The expressions are so called ANDConnectedTaggedValues which means that all tagged values MUST apply.

Type("Sensor").tv(Authentication = No; Authorization != YES)
Type("Sensor").tv(Authentication = No | Authorization != YES)

Both queries above express the same thing. The ; and | are synonyms for OR. The expressions are so called ORConnectedTaggedValues which means that one or all tagged values CAN apply.

The image below shows a Sensor with the tagged value Authentication set to No and the tagged value Authorization set to Yes.

Enterprise Architect sensor tagged values

This following rule would not apply because Authorization is set to Yes for the sensor, but the rule requires != YES.

Type("Sensor").tv(Authentication = No & Authorization != YES)

But the rule below would apply because Authentication is set to No and only one tagged value is required inside an ORConnectedTaggedValues expression. Authorization != YES is not required for this rule to work.

Type("Sensor").tv(Authentication = No | Authorization != YES)

It is possible to combine ORConnectedTaggedValues and ANDConnectedTaggedValues but then these must be enclosed within additional brackets.

Type("Sensor").tv((Authentication = No & Authorization = No) | Feedback = No)

This TaggedValueFilter contains a left and a right side. Overall it is an ORConnectedTaggedValues expression with a left side as an ANDConnectedTaggedValues expression and a normal tagged value on the right side. These expressions can be nested.

Additional examples:

Type("Sensor").tv(Authentication = No | (Feedback = No & Redundancy = No))
Type("Sensor").tv((Authentication = No & Authorization = No) | (Feedback = No & Redundancy = No))
Type("Sensor").tv((Authentication = No & Authorization = No) | (Feedback = No & (Redundancy = No & Data Safety integrity != Yes)))

ConnectorFilter:

A connector filter can be used to distinguish between elements that have a specific connector to other elements and which do not.

General structure:

ElementExpression ".hasConnector(Connector" SourceFilter TypeFilter:? CrossesFilter:? TaggedValueFilter:?)
ElementExpression ".hasConnector(Connector" TargetFilter TypeFilter:? CrossesFilter:? TaggedValueFilter:?)

Example:

Type("Sensor").hasConnector(Connector.to(Type("Electronic Control Unit")))
Type("Electronic Control Unit").hasConnector(Connector.from(Type("Sensor")))

This rule checks all Sensor-elements inside the diagram and filters them according to whether they have a connector TO an Electronic Control Unit or not. Very important here is the part Connector.to because it defines the direction of the Connector.

Diagram example:

Example diagram with marked connector to rule

The red marked Connector is addressed by this rule because it is a Sensor with a Connector TO an Electronic Control Unit. The other Connector is NOT addressed by this rule because it is a Sensor with a Connector TO a Memory and NOT an Electronic Control Unit.

Type("Memory").hasConnector(Connector.from(Type("Sensor")))

This rule checks all Memory-elements inside the diagram and filters them according to whether they have a connector FROM a Sensor or not. Very important here is the part Connector.from because it defines the direction of the Connector from Sensor.

Diagram example:

Example diagram with marked connector from rule

It is also possible to add an TaggedValueFilter to this ConnectorFilter. This makes it possible to check whether this connector has certain tagged values.

Type("Memory").hasConnector(Connector.from(Type("Sensor")).tv(Content = Critical Data))

Other filters are also possible. They are described later on in the "Connector Oriented" section.

Additional examples:

Type("Sensor").tv(Authentication = No | (Feedback = No & Redundancy = No)).hasConnector(Connector.to(Type("Memory")).type("Wireless Flow"))
Type("Sensor").hasConnector(Connector.to(Type("Memory")).crosses(BOUNDARY))
Type("Sensor").hasConnector(Connector.to(Type("Memory")).crosses(BOUNDARY))
Type("Sensor").tv(Authentication = No | (Feedback = No & Redundancy = No)).hasConnector(Connector.to(Type("Memory")).crosses(BOUNDARY))
Type("Sensor").tv(Authentication = No | (Feedback = No & Redundancy = No)).hasConnector(Connector.to(Type("Memory")).crosses(BOUNDARY.type("Physical")))
Type("Sensor").tv(Authentication = No | (Feedback = No & Redundancy = No)).hasConnector(Connector.to(Type("Memory")).type("Wireless Flow").crosses(BOUNDARY).tv(Content = Critical Data))
Type("Sensor").tv(Authentication = No | (Feedback = No & Redundancy = No)).hasConnector(Connector.to(Type("Memory").tv(Authentication = No)).type("Wireless Flow").crosses(BOUNDARY).tv(Content = Critical Data))

RelationFilter:

A RelationFilter is there to differentiate elements depending on their relationship to other elements. Basically, it is about differentiating which element lies in another element.

General structure:

ElementExpression ".hasDescendant(" ElementExpression ")"
ElementExpression ".hasAncestor(" ElementExpression ")"

The best example for a RelationFilter is an element inside a boundary:

Example diagram to explain element boundary relation

Example:

Type("Boundary").hasDescendant(Type("Sensor"))
Type("Sensor").hasAncestor(Type("Boundary"))

The Sensor is the Descendant of the Boundary and the Boundary is the Ancestor of the Sensor.

Consider following example diagram:

Example diagram with different elements in different boundaries

Analysed by this rule:

Type("Boundary").hasDescendant(Type("Sensor"))

Results in:

Example diagram with marked boundary with descendant rule

Analysed by this rule:

Type("Memory").hasAncestor(Type("Boundary"))

Results in:

Example diagram with marked element with ancestor type memory rule

Analysed by this rule:

Type("Sensor").hasAncestor(Type("Boundary"))

Results in:

Example diagram with marked element with ancestor type sensor rule

Additional examples:

Type("Sensor").tv(Authentication = No).hasConnector(Connector.to(Type("Memory")).type("Wireless Flow")).hasAncestor(Type("Boundary"))
Type("Sensor").tv(Authentication = No | (Feedback = No & Redundancy = No)).hasConnector(Connector.to(Type("Memory")).crosses(BOUNDARY)).hasAncestor(Type("Boundary"))

Connector oriented

In addition to the element oriented rules, there is also the option of searching connectors within the diagram. These rules are called Connector oriented. A connector is basically between a start point and an end point, i.e. a Source and a Target. The basic structure of such a rule is as follows:

ConnectorExpression             -> ( SingleConnectorExpression | MultipleConnectorExpression )
SingleConnectorExpression       -> "Connector" SourceFilter TargetFilter TypeFilter:? CrossesFilter:? TaggedValueFilter:? 
SourceFilter                     -> ".from(" ElementExpression ")" 
TargetFilter                     -> ".to(" ElementExpression ")" 
TypeFilter                       -> ".type(" "\"" identifierString "\"" ")"   
CrossesFilter                    -> ".crosses(" BoundaryFilter ")" 
BoundaryFilter                   -> ("BOUNDARY" |  "BOUNDARY.type("  "\"" identifierString "\"" ")")
TaggedValueFilter                  -> ".tv(" Tagged Value ")" 

Again this may seem very complex, all the terms used in the description above will be explained within this chapter along with examples.

Single Connector Expression

First let's start with the simple case, the SingleConnectorExpression. The MultipleConnectorExpression will be explained later in this section. A simple example would be: (Only the SourceFilter and TargetFilter are used now. The other filters will be explained later.)

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit"))

With this example diagram:

Example diagram with connected elements

Results in:

Example diagram with connected elements and marked connections

The two marked connectors start at a "Sensor" and end at an "Electronic Control Unit". The third connector starts at an "Electronic Control Unit" and ends at a "Sensor", so this connector is not covered by this rule.

Multiple Connector Expression

The MultipleConnectorExpression is very similar to the previously described MultipleElementExpression. It follows the same structure. The MultipleConnectorExpression is used to simultaneously check several Connectors within the diagram. General structure:

ConnectorExpression            -> ( SingleConnectorExpression | MultipleConnectorExpression )
MultipleConnectorExpression    -> "("  ( AndConnectedConnectors | OrConnectedConnectors ) ")" 
AndConnectedConnectors         -> ConnectorExpression _ "&" _  ConnectorExpression  ( _ "&" _ ConnectorExpression ):*
OrConnectedConnectors          -> ConnectorExpression _ "|" _  ConnectorExpression  ( _ "|" _ ConnectorExpression ):*  

Basically, AndConnectedConnectors and OrConnectedConnectors differ depending on whether conjunction (&) or disjunction (|) is used. Both expressions must be encapsulated in brackets.

Rule Examples:

(Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")) & Connector.from(Type("Electronic Control Unit")).to(Type("Memory")))
(Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")) | Connector.from(Type("Sensor")).to(Type("Memory")))

Consider following diagram:

Example diagram with connected elements and boundary container

The first rule would not apply in this diagram because there is no Connector between an Electronic Control Unit and a Memory. An AndConnectedConnectors requires that both sides of the expression are fulfilled.

The second rule the OrConnectedConnectors would give this result:

Example diagram with connected elements marked for OR Connected rule

The left side of the expression is fulfilled: there is a connector between a Sensor and an Electronic Control Unit.

If we would rewrite the first rule like this:

(Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")) & Connector.from(Type("Electronic Control Unit")).to(Type("Software")) & Connector.from(Type("Software")).to(Type("Memory")))

The result would look like this:

Example diagram with connected elements marked for AND Connected rule

Now all connectors are marked because all parts of the AndConnectedConnectors are fulfilled.

As it can be seen inside the general structure these expressions can be nested. The filters explained below can also be added to each of the SingleConnectorExpressions inside these rules.

Additional Examples:

(Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).type(Wireless Flow) & Connector.from(Type("Electronic Control Unit")).to(Type("Memory")).tv(Content = Sensor Data))
(Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).type(Wireless Flow).tv(Secure = false) & Connector.from(Type("Electronic Control Unit")).to(Type("Memory")).crosses(BOUNDARY).tv(Conentent = Sensor Data))
(Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")) & (Connector.from(Type("Electronic Control Unit")).to(Type("Memory")) | Connector.from(Type("Electronic Control Unit")).to(Type("Software"))))
(Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")) | Connector.from(Type("Sensor")).to(Type("Memory")) | Connector.from(Type("Sensor")).to(Type("Software")))
(Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")) | Connector.from(Type("Sensor")).to(Type("Memory")) | (Connector.from(Type("Sensor")).to(Type("Software")) & Connector.from(Type("Software")).to(Type("Memory"))))

TypeFilter:

A TypeFilter is there to distinguish between the possible types of connectors. As can be seen in the following picture, the top group of Connectors is represented by the class definition called Communication Flow. The subclasses are Wired Flow and Wireless Flow. The Wired Flow refers to a physical cable connection and the Wireless Flow refers to a wireless connection, such as WLAN.

Example diagram with connected elements marked for connection rule with sub type filter

An example rule would be:

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).type("Wireless Flow")

Running an analysis on the example diagram from before results in:

Example diagram with connected elements marked for connection rule with top type filter

The defined rule focuses only on connectors which start at a Sensor and end at an Electronic Contnrol Unit and have the type Wireless Flow. Only the last Connector fulfills these requirements.

As the example before worked with a subclass of Communication Flow, the analysis below shows the affected connectors when using Communication Flow as Connector type.

The respective rule:

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).type("Communication Flow")

This results in:

Example diagram with connected elements and multiple boundaries

By using inheritance all connectors derived from the class Communication Flow are analyzed.

Additional examples:

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).type("Wired Flow")
Connector.from(Type("Sensor").tv(Authentication = No)).to(Type("Electronic Control Unit")).type("Wired Flow")
Connector.from(Type("Sensor").tv(Authentication = No)).to(Type("Electronic Control Unit").hasAncestor(Type("Boundary")).type("Wired Flow")

CrossesFilter:

A CrossesFilter is there to distinguish between Connectors which cross a border and which do not. A border can be defined as a Boundary.

General Structure:

CrossesFilter                    -> ".crosses(" BoundaryFilter  ")" 
BoundaryFilter                   -> ("BOUNDARY" |  "BOUNDARY.type("  "\"" identifierString "\"" ")")

Example rules would be:

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).crosses(BOUNDARY)
Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).crosses(BOUNDARY.type("Logical Boundary"))

Now we will apply these rules on the following diagram. As it can be seen, the elements at the top of the diagram are nested in Boundaries. The types of boundaries are also different.

Example diagram with connected elements and multiple boundaries

First rule:

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).crosses(BOUNDARY)

Example diagram with connected elements and multiple boundaries marked boundary crossing connections

Both marked Connectors cross a BOUNDARY of the Source or Target. So this rule is fulfilled.

Second rule:

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).crosses(BOUNDARY.type("Logical Boundary"))

Example diagram with connected elements and multiple boundaries marked boundary with typefilter crossing connections

The marked Connector crosses a BOUNDARY with the type Logical Boundary. If you compare this rule with the third one, you can see that the other Connector crosses a BOUNDARY with the type Physical Boundary.

Additional examples:

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).type("Wired Flow").crosses(BOUNDARY)
Connector.from(Type("Sensor").tv(Authentication = No)).to(Type("Electronic Control Unit")).type("Wired Flow").crosses(BOUNDARY)
Connector.from(Type("Sensor").tv(Authentication = No)).to(Type("Electronic Control Unit").hasAncestor(Type("Boundary")).type("Wired Flow").crosses(BOUNDARY)
Connector.from(Type("Sensor").tv(Authentication = No)).to(Type("Electronic Control Unit").hasAncestor(Type("Boundary")).type("Wired Flow").crosses(BOUNDARY.type("Logical Boundary"))

TaggedValueFilter:

An TaggedValueFilter is there to distinguish connectors in terms of their defined TaggedValues.

General Structure:

TaggedValueFilter   -> ".tv(" Tagged Value ")" 

The TaggedValueFilter for Connectors works the same way as for Elements. Example rule:

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).tv(Content = Sensor Data)

The image below shows the tagged value of the Connector from the Sensor to the Electronic Control Unit set to Sensor Data.

Example diagram shows connection tagged values

Using the rule from above on the diagram with the tagged value set to Sensor Data yields the following result.

Example diagram with marked connection for connection rule with tagged value filter

Only the upper Connector between Sensor and the Electronic Control Unit is affected as the other connector does not have the tagged value Content set to Sensor Data.

Additional examples:

Connector.from(Type("Sensor")).to(Type("Electronic Control Unit")).type("Wired Flow").crosses(BOUNDARY)
Connector.from(Type("Sensor").tv(Authentication = No)).to(Type("Electronic Control Unit")).type("Wired Flow").crosses(BOUNDARY)
Connector.from(Type("Sensor").tv(Authentication = No)).to(Type("Electronic Control Unit").hasAncestor(Type("Boundary")).type("Wired Flow").crosses(BOUNDARY)
Connector.from(Type("Sensor").tv(Authentication = No)).to(Type("Electronic Control Unit").hasAncestor(Type("Boundary")).type("Wired Flow").crosses(BOUNDARY.type("Logical Boundary"))

Full specification of the language

Expression                                      ->  ElementExpression | ConnectorExpression  

ElementExpression                               -> ( SingleElementExpression | MultipleElementExpression )  TaggedValueFilter:? ConnectorFilter:* RelationFilter:* 

SingleElementExpression                         -> (Identifier|SingleElementType)

SingleElementType                               -> "Type(" "\"" identifierString "\"" ")"    

Identifier                                      -> "\"" identifierString "\"" 

MultipleElementExpression                       -> "("  ( AndConnectedElements | OrConnectedElements ) ")" 

AndConnectedElements                            -> ElementExpression _ "&" _  ElementExpression  ( _ "&" _ ElementExpression ):* 

OrConnectedElements                             -> ElementExpression _ "|" _  ElementExpression  ( _ "|" _ ElementExpression ):*  

TaggedValueFilter                               -> ".tv(" TaggedValue ")" 

TaggedValue                                     -> ( SingleTaggedValue | MultipleTaggedValues ) 

InnerTaggedValue                                -> ( SingleTaggedValue | InnerMultipleTaggedValues )    

MultipleTaggedValues                            -> ( ORConnectedTaggedValues | ANDConnectedTaggedValues )   

InnerMultipleTaggedValues                       -> "("  ( ORConnectedTaggedValues | ANDConnectedTaggedValues ) ")" 

ORConnectedTaggedValues                         -> InnerTaggedValue ( ( ";" | _ "|" ) _ InnerTaggedValue):+     

ANDConnectedTaggedValues                        -> InnerTaggedValue ( ( "," | _ "&" ) _ InnerTaggedValue):+ 

SingleTaggedValue                               -> ( StringValueTaggedValue | NumericValueTaggedValue | NumericRangeValueTaggedValue | MultipleStringValueTaggedValues | MultipleNumberValueTaggedValuesWithOutUnit |MultipleNumberValueTaggedValuesWithUnit )

MultipleNumberValueTaggedValuesWithOutUnit      -> multipleString _ "=" _ "(" (singleNumber | decimalNumber) ( (";" | _ "|") _ (singleNumber | decimalNumber) ):+  ")" 

MultipleNumberValueTaggedValuesWithUnit         -> multipleString _ "=" _ "(" (singleNumber | decimalNumber)  taggedValueUnit ( (";" | _ "|") _ (singleNumber | decimalNumber)  taggedValueUnit ):+  ")" 

MultipleStringValueTaggedValues                 -> multipleString _ "=" _ "(" multipleString ((";" | _ "|") _ multipleString):+  ")" 

NumericRangeValueTaggedValue                    -> (NumericRangeValueTaggedValueWithUnit|NumericRangeValueTaggedValueWithOutUnit)

NumericRangeValueTaggedValueWithOutUnit         -> multipleString _ ("in"|"="|"!=") _ "(" (singleNumber | decimalNumber) _ "-" _ (singleNumber | decimalNumber)  ")" 

NumericRangeValueTaggedValueWithUnit            -> multipleString _ "in" _ "(" (singleNumber | decimalNumber) taggedValueUnit  _ "-" _ (singleNumber | decimalNumber) taggedValueUnit ")" 

StringValueTaggedValue                          -> multipleString _ ("=" | "!=") _ multipleString 

NumericValueTaggedValue                         -> multipleString _ ("=" | ">" | "<" | "!=" ) _ (singleNumber | decimalNumber) ( _ taggedValueUnit):? 

ConnectorFilter                                 -> ".hasConnector(Connector" (SourceFilter | TargetFilter) TypeFilter:? CrossesFilter:? TaggedValueFilter:? ")"

SourceFilter                                    -> ".from(" ElementExpression ")" 

TargetFilter                                    -> ".to(" ElementExpression ")" 

CrossesFilter                                   -> ".crosses(" BoundaryFilter ")"   

BoundaryFilter                                  -> ("BOUNDARY" |  "BOUNDARY.type("  "\"" identifierString "\"" ")") 

TypeFilter                                      -> ".type(" "\"" identifierString "\"" ")"

RelationFilter                                  -> ( ".hasDescendant(" ElementExpression ")" | ".hasAncestor(" ElementExpression ")" ) 

ConnectorExpression                             -> ( SingleConnectorExpression | MultipleConnectorExpression ) 

SingleConnectorExpression                       -> "Connector" SourceFilter:? TargetFilter:? TypeFilter:? CrossesFilter:? TaggedValueFilter:? 

MultipleConnectorExpression                     -> "("  ( AndConnectedConnectors | OrConnectedConnectors ) ")" 

AndConnectedConnectors                          -> ConnectorExpression _ "&" _  ConnectorExpression  ( _ "&" _ ConnectorExpression ):* 

OrConnectedConnectors                           -> ConnectorExpression _ "|" _  ConnectorExpression  ( _ "|" _ ConnectorExpression ):*  

singleString                                    -> [a-zA-Z]:+ 

multipleString                                  -> singleString ( _ singleString ):* 

identifierString                                -> singleStringWithNumbers ( _ singleStringWithNumbers ):* 

singleStringWithNumbers                         -> [a-zA-Z0-9_]:+ 

singleNumber                                    -> [0-9]:+ 

decimalNumber                                   -> [0-9]:+ "." [0-9]:+ 

taggedValueUnit                                 -> [a-zA-z/%]:+ 

_                                               -> [ \s]:+