Skip to main content

Check out Interactive Visual Stories to gain hands-on experience with the SSE product features. Click here.

Skyhigh Security

Mowgli Language SSE

​​ Mowgli stands for MOdules With Gluing Language Interpreter and defines a programming language used to create customer specific rules. This document introduces the different language elements and describes the syntax of these elements.

Here is a short example to show how Mowgli code looks like:

# a comment starts with either // or #
BOOLEAN b = FALSE # define a variable named b of type BOOLEAN
                  # with value FALSE
NUMBER n = Math.Random(0, 10) # define variable n of type NUMBER
                              # with a random value between 0 and 10
IF n > 5 THEN {
    b = TRUE
} ELSE {
    b = FALSE
}
VECTOR<NUMBER> vec = 1, 2, 3  # define a vector of numbers and initialize
                              # with some constant values
NUMBER val = 4
LOOP 6 {
    vec = Add(vec, val)
    val = val + 1
}
# now vector vec contains the elements 1, 2, 3, 4, 5, 6, 7, 8 and 9 

Basic data types and language keywords are always uppercase, like STRING, IF or LOOP. The different elements of the language are described in the next section.

Basic Syntax

This section describes the syntax of the different language elements.

Variables

A variable is a named storage location that holds a value. A variable can be defined with or without an initial value. The syntax is

<type> <name> [ = <value>]

Example:

// defines a variable of type NUMBER with name n and value 42
NUMBER n = 42
// without specifying an initial value the value of str is an empty string
STRING str
// defines a vector of boolean values with name vec and initialized with
// the three elements TRUE, FALSE and TRUE
VECTOR<BOOLEAN> vec = TRUE, FALSE, TRUE

Data Types

Mowgli provides domain specific data types like Net.IP and the following basic data types.

►BOOLEAN
  • Representation: BOOLEAN
  • Value: TRUE FALSE
  • Operators:
    Assignment:     =
    Relational:         ==,   !=,   <,   <=,   >,   >=
    Logical:              AND,   OR,   NOT
  • Example:
    BOOLEAN a // default value FALSE
    BOOLEAN b = TRUE
    BOOLEAN c = NOT b   # c is FALSE
    BOOLEAN d = b != c  # d is TRUE
    BOOLEAN e = StartsWith("hello", "hell") # e is TRUE
 
►NUMBER
  • Representation: NUMBER
  • Value: 0, 12345, -39.999
  • Operators:
    Assignment:     =
    Mathematical:   +,   -,   *,   /
    Relational:         ==,   !=,   <,   <=,   >,   >=
  • Example:
    NUMBER a // default value 0
    NUMBER b = 2
    NUMBER c = b - 1
    NUMBER d = Math.Pi
    BOOLEAN var = c == 3

NOTE: Double representation of a number, which is for both integer and floating point values. A variable of type NUMBER can hold big integer values accurately (up to 4503599627370496 (2 to the power of 52) depending on the platform). However a variable of type NUMBER can not be used for 64 bit integer values!

►String
  • RepresentationSTRING
  • Value: string values
  • Operators:
    Assignment:     =
    Mathematical: + (Concatenation)
    Relational:           ==,   !=,   <,   <=,   >,   >=
  • Escape Sequences:
    Escape sequences are used to represent certain special characters within Mowgli string literals. The following backslash escapes are available:
    \" double quote
    \\ backslash
    \n line feed - new line
    \r carriage return
    \t horizontal tab \xAB code unit AB (A and B are hexadecimal digits), for example, \x41 for character 
  • Example:
    STRING a // default value empty string
    STRING b = "hello "
    STRING c = "world"
    STRING d = b + c            // d is "hello world"
    STRING e = Erase(d, 1, 9)   // e is "hd"
    STRING f = "\x41\n\x42"     // f has two lines, first line is "A"
                                // and second line is "B"

►DATAREF
  • RepresentationDATAREF
  • Value: binary data
  • Operators:
    Assignment:     =
    Relational:         ==,   !=
    For relational operators DATAREF types are compared byte by byte
  • Example: 
    DATAREF a = ToDataRef("test")
    STRING b = Binary.PartToString(a, 1, 2) // b is "es"
►Vector

A vector stores elements of a given type in a linear arrangement, and allows fast random access to any element. Vectors are persistent, all functions changing a vector will return the changed vector.

  • Example
    VECTOR<NUMBER> vec1 = 1,2,3
    VECTOR<NUMBER> vec2 = Add(vec1, 4)
    # vec1 has the elements 1,2,3
    # vec2 has the elements 1,2,3,4
    vec1 = Add(vec1, 5)
    # vec1 has the elements 1,2,3,5
     
  • RepresentationVECTOR<ITEMTYPE>
  • ITEMTYPE may be: BOOLEAN, NUMBER, STRING and DATAREF <domain specific type>
  • Value: list of values of corresponding item type
  • Operators:
    Assignment:     =
    Relational:         ==,   !=
  • Basic VECTOR FunctionsVECTOR<ITEMTYPE> Add (VECTOR<ITEMTYPE> vec, ITEMTYPE item)push back an item to the vector
  • Parameters : 
    vec: vector to add the item to 
    item: item to be added
  • Returns : 
    new VECTOR containing new element ITEMTYPE Get (VECTOR<ITEMTYPE> vec, NUMBER index)gets the element of the given index. If ‘index’ is not in range, the default value is returned (i.e. 0 for NUMBER or “” for STRING)
  • Parameters : 
    vec: vector to get the item from
    index: index of the element
  • Returns :
    ITEMTYPE of element at position index (or last element if index >= vector size), 0 <= index < vector size BOOLEAN IsEmpty (VECTOR<ITEMTYPE> vec)checks if a VECTOR contains elements.
  • Parameters : 
    vec: vector to check
  • Returns : 
    TRUE if vector is empty BOOLEAN IsSorted (VECTOR<ITEMTYPE> vec) returns TRUE if vector is sorted
  • Parameters 
    vec: vector to check
  • Returns :
    TRUE if vector is sorted NUMBER Find (VECTOR<ITEMTYPE> vec, ITEMTYPE item) finds element (case sensitive search (for ITEMTYPE STRING)
  • Parameters :
    vec: vector to search for item   item: item to find
  • Returns :
    index: index of the element if found, -1 otherwise NUMBER FindCaseInsensitive (VECTOR<ITEMTYPE> vec, STRING item) only for ITEMTYPE STRINGfinds element (case insensitive search)
  • Parameters : 
    vec: vector to search for item item: item to find
  • Returns :
    index: index of the element if found, -1 otherwise VECTOR<ITEMTYPE> RemoveLast (VECTOR<ITEMTYPE> vec) removes the last element
  • Parameters :
    vec: vector to remove last element from
  • Returns : 
    new VECTOR without the previously last element VECTOR<ITEMTYPE> Remove (VECTOR<ITEMTYPE> vec, NUMBER index) removes the element at position ‘index’. If ‘index’ is out of range, a copy of the given vector will be returned.
  • Parameters : 
    vec: vector to remove element from
    index: index of the element to remove
  • Returns : 
    new VECTOR without element VECTOR<ITEMTYPE> Set (VECTOR<ITEMTYPE> vec, NUMBER index, ITEMTYPE item) sets an item to the vector at position ‘index’. If ‘index’ is out of range, a copy of the given vector will be returned.
  • Parameters : 
    vec: vector to set item 
    index: index of the element to be set 
    item: item to be set
  • Returns : 
    new VECTOR containing new element VECTOR<ITEMTYPE> SetSorted (VECTOR<ITEMTYPE> vec, BOOLEAN sort) sorts vector
  • Parameters :
    vec: source vector
    sort: if TRUE the returned vector is in a sorted state, adding new elements keeps the sorted state. If FALSE with new added elements the returned vector will no longer be sorted 
  • Returns :
    new sorted VECTOR NUMBER Size (VECTOR<ITEMTYPE> vec)size of the vector
  • Parameters :
    vec: vector to get number of elements from
  • Returns :
    number of elements in the vector
  • Example :
    VECTOR<STRING> vec1 = "first", "second"
    VECTOR<STRING,SORTED> vec2 = "second", "first"
    vec1 = Add(vec1, "third")
    vec2 = Add(vec2, "third")
    STRING str = Get(vec, 0)
    BOOLEAN b1 = str == "first" # b1 is TRUE
    BOOLEAN b2 = VectorUtils.MatchesInList(vec, ".*con.*") # b2 is TRUE
    STRING str1 = ToString(vec1) # str1 is "first", "second", "third"
    STRING str2 = ToString(vec2) # str2 is "first", "second", "third"
►MAP

A MAP is a data structure that stores key-value pairs. Maps are persistent, all functions changing a map will return the changed map.

  • Example:

    MAP<STRING, NUMBER> map1 = ("key1", 42), ("key2", -1.23)
    MAP<STRING, NUMBER> map2 = Set(map1, "key3", 0)
    # map1 has the elements ("key1", 42), ("key2", -1.23)
    # map2 has the elements ("key1", 42), ("key2", -1.23), ("key3", 0)
    map1 = Set(map1, "key4", 123)
    # map1 has the elements ("key1", 42), ("key2", -1.23), ("key4", 123

  • RepresentationMAP<KEYTYPE, VALUETYPE>
  • KEYTYPE may be: NUMBER and STRING
  • VALUETYPE may be: BOOLEAN, NUMBERSTRING, and DATAREF<domain specific type>
  • Value: map of key-value pairs of corresponding key and value types
  • Operators:
    Assignment:     =
    Relational:         ==,   !=
  • Basic MAP Functions:VALUETYPE Get (MAP<KEYTYPE, VALUETYPE> map, KEYTYPE key)gets the value for the given key of the map entry. If ‘key’ is not in the map, the default value is returned (i.e. 0 for NUMBER or "" for STRING).
  • Parameters :
    map: map to get the value for given key from
    key: key of the map entry
  • Returns :
    VALUETYPE of map entry with given key BOOLEAN HasKey (MAP<KEYTYPE, VALUETYPE> map, KEYTYPE key) returns TRUE, if given key exists in map
  • Parameters : 
    map: map to check
    key: key of the map entry
  • Returns :
    TRUE if key exists in map, otherwise FALSE BOOLEAN IsEmpty (MAP<KEYTYPE, VALUETYPE> map) returns TRUE, if map is empty
  • Parameters :
    map: map to check
  • Returns :
  • TRUE if map is empty, otherwise FALSE VECTOR<KEYTYPE> GetKeys (MAP<KEYTYPE, VALUETYPE> map) returns a list of keys
  • Parameters :
    map: map to get the keys from
  • Returns :
    list of keys MAP<KEYTYPE, VALUETYPE> Remove (MAP<KEYTYPE, VALUETYPE> map, KEYTYPE key)removes a map entry with given key. If key doesn’t exist in map, a copy of the given map will be returned.
  • Parameters :
    map: map to remove an entry from
    key: key of the map entry to remove
  • Returns :
  • new MAP without map entry MAP<KEYTYPE, VALUETYPE> Set (MAP<KEYTYPE, VALUETYPE> map, KEYTYPE key, VALUETYPE value)adds a key-value pair to the map
  • Parameters :
    map: map to add key-value pair to
    key: key of the map entry to add
    value: value of the map entry to add
  • Returns
    new MAP containing new map entry NUMBER Size (MAP<KEYTYPE, VALUETYPE>)size of the map
  • Parameters
    map: map to get size from
  • Returns :
    size of the map
  • Example: 
    MAP<STRING,NUMBER> map = ("first", 1), ("second", 2), ("third", 3.4)
    map = Set(map, "zero", 0)
    NUMBER value = Get(map, "second")  # value is 2
    STRING str = ToString(map)         # str is ("first", 1), ("second", 2),
                                      # ("third", 3.4), ("zero", 0)

Type Conversions

Mowgli is type safe. This means that every variable has a well defined type and there is no automatic conversion from one type to another. For example

NUMBER a = 123
STRING s = a

will not work, because Mowgli will not automatically convert the NUMBER to a STRING. Conversion functions need to be used instead. The correct Mowgli code for the example above is therefore

NUMBER a = 123
STRING s = ToString(a)

Functions and Procedure Calls

The difference between functions and procedures is that functions have a return value and procedures do not have one. The return value of a function needs to be used, it is not allowed to call a function like a procedure without using the return value.

If a function or procedure doesn’t have parameters, the brackets may be omitted. Example:

NUMBER pi = Math.Pi()
NUMBER e = Math.E

A reference of all functions and procedures with help and code examples can be found here.

Control flow (if/else, loops)

►IF THEN ELSE

The IF THEN ELSE statement is used to execute different blocks of code based on whether a given condition is TRUE or FALSE. The basic structure is

IF condition THEN {
    // Code to execute if condition is TRUE
} ELSE {
    // Code to execute if condition is FALSE
}

Explanation:

  • IF: This keyword initiates the conditional statement.
  • condition: This is an expression that evaluates to either true or false.
  • Examples:
    x > 10
    name == "John"
    NOT b
  • THEN: This keyword separates the condition from the code block to be executed if the condition is true. The curly brackets are optional if the code to execute only contains one line.
  • ELSE: This keyword and the following block are optional. If included, it introduces the code block to be executed if the condition is false. The curly brackets are optional if the code to execute only contains one line.
  • Example:
    NUMBER n = Math.Random(0, 10)
    STRING s

    IF n > 5 THEN s = "n is higher than 5" ELSE s = "n is less or equal to 5"

    IF n > 5 THEN {
        s = "n is higher than 5"
    } ELSE {
        s = "n is less or equal to 5"
    }

    IF n > 5 THEN
        s = "n is higher than 5"
    ELSE
        s = "n is less or equal to 5"

    NUMBER n2 = Math.Random(0, 10)
    s = "n and n2 are not both higher than 5"
    IF n > 5 AND n2 > 5 THEN s = "n and n2 are both higher than 5"
►LOOP

With LOOP(x) (or LOOP x) the next command (or block) is executed x times.

  • Example:
    NUMBER n
    LOOP 10 {
      n = n + 1
    }
    // n is 10


    VECTOR<NUMBER> vec = 9, 8, 7
    NUMBER res = 0
    NUMBER i = 0
    LOOP Size(vec) {
      i = i + 1
      IF i == 2 THEN
          CONTINUE
      ELSE IF i == 3 THEN
          BREAK
      res = Get(vec, i - 1)
    }
    // res is 9

The keyword CONTINUE starts the next iteration of the loop. BREAK stops the loop. Therefore in the last example above the assignment to res will only be executed in the first iteration of the loop.

►FOR IN DO

The FOR loop has the form “FOR <elem> IN <vector> DO” and is executed with every element of a vector.

  • Example:
    VECTOR<NUMBER> vec = 4, 2, 3
    NUMBER r
    NUMBER i
    FOR i IN vec DO {
      r = r + i
    }
    // r is 9

    VECTOR<JSON> vec2
    vec2 = Add(vec2, JSON.FromString("[\"Hello\", \"Mowgli\"]"))
    vec2 = Add(vec2, JSON.FromString("[true]"))
    vec2 = Add(vec2, JSON.FromString("[38,{\"a\":39,\"b\":65}]"))
    vec2 = Add(vec2, JSON.FromString("[42]"))
    JSON j = JSON.CreateEmptyArray
    JSON e
    FOR e IN vec2 DO {
        IF JSON.Size(e) == 1 THEN CONTINUE
        j = JSON.UnionArrays(j, e)
    }
    // ToString(j) returns ["Hello","Mowgli",38,{"b":65,"a":39}]

As with LOOP, the keyword CONTINUE starts the next iteration of the for loop. BREAK stops the for loop.

Validity of Objects

Sometimes functions return objects of Mowgli types that are not valid, e.g. NULL pointers for DATAREF or other types or NAN for NUMBER types. There is the Mowgli function ‘Valid’ that returns FALSE if an object is invalid. Example:

STRING jsonStr = "{\"broken\": json"
JSON j = JSON.FromString(jsonStr)
IF Valid(j) THEN {
    NUMBER s = JSON.Size(j)
    j = JSON.Set(j, "a", 42)
}

In this example the ‘JSON.FromString’ function returns an invalid JSON object if the STRING parameter is not a valid JSON. To avoid that more JSON functions are called on the invalid JSON (which would trigger additional errors each time), the validity of the variable j is checked with the Valid function.

Advanced Concepts

This section describes advanced topics like an alternative syntax to ease readability and error handling.

Dot Notation

Namespace and element name are separated by a dot (e.g. AV.Infected).

As an alternate notation for an object being passed as the first function call argument, the function can also be appended to the object and separated by a dot. For example with the procedure definition

Variant.Assign(Variant, NUMBER)

the Mowgli code

Variant var
Variant.Assign(var, 123)

can also be written as

Variant var
var.Assign(123)

With the following three function definitions:

Web.ReqResp Web.GetReqResp(Web.Transaction, STRING)
Web.URL Web.GetURL(Web.ReqResp)
STRING Web.GetHost(Web.URL)

cascading function calls like

Web.Transaction trans
STRING s = Web.GetHost(Web.GetURL(Web.GetReqRespObj(trans, "Request")))

can be written as

Web.Transaction trans
STRING s = trans.GetReqRespObj("Request").GetURL.GetHost

which is easier to read.

The dot-notation can also start with a namespace. Only a single namespace is allowed and that has to be given at the beginning of the statement. All tokens within the expression will then be searched within the given and the global namespace.

Example:

Web.Transaction trans
Web.GetReqRespObj(trans, "Request").GetURL.GetHost

Suppose a variable has a type that is defined in a certain namespace. In that case, the dot notation is also allowed after the variable and tokens are searched in the namespace of the variable type and in the global namespace like in this example (the same then the one above):

Web.Transaction trans
STRING s = trans.GetReqRespObj("Request").GetURL.GetHost

Error Handling

The general idea of error handling in Mowgli is to handle errors in error routines. With this, a policy can decide how to react on an error. This allows the policy to do something on specific errors and to stop the execution of the policy or to ignore an error and continue the execution flow. An error can be triggered by a called function or procedure or by the Mowgli code and can have one of the following severities:

1 (the return value of function Error.Warning)  // error situation is
                                                // determined as a warning
2 (the return value of function Error.Error)    // error situation is
                                                // a standard error
3 (the return value of function Error.Critical) // error situation is
                                                // a critical system error

Here is an example code:

NUMBER n = -1
NUMBER s = Math.Sqrt(n) // square root of negative number is not defined,
                        // Math.Sqrt will trigger an error with severity
                        // warning
STRING str = "Square root of " + ToString(n) + " is " + ToString(s)
NUMBER d = DateTime.LocalStringToTimestamp("abc", "%D")
  // first parameter is an illegal format string, therefore the function
  // triggers an error of severity error
str = ToString(d) // this line will never be executed due to the
                  // MWG.Block call in error routine


Code in error routine with trigger Error:

IF Error.Severity > 1 THEN
{
  MWG.Block("Error in policy: " + Error.Message)
}

A negative input parameter for Math.Sqrt is invalid and the function creates an appropriate error. Since this is no severe error (and most likely can be ignored) the error severity is 1 (Error.Warning). The first parameter to DateTime.LocalStringToTimestamp is a format string. The example above uses an invalid one, which will result in an error with severity 2 (Error.Error). The code in the error routine blocks all errors with severity greater than 1.

As we can see in this example, execution continues after an error of type Error. Warning, while execution stops after an error of severity Error.Error or Error.Critical (because MWG.Block will not return).

One can write error routines for specific errors and error routines to handle all errors like this:

NUMBER n = -1
NUMBER s = Math.Sqrt(n)
STRING str = "Square root of " + ToString(n) + " is " + ToString(s)
NUMBER d = DateTime.LocalStringToTimestamp("abc", "%D")
STRING str2 = "This line will now be reached!"


Create an error routine with trigger Error.Mowgli.InvalidArgument, but no code. Such an error routine will ignore these errors.

Code in error routine with trigger Error:

{
    Error.CancelTransaction("Runtime Error occurred")
}

Note: The first error routine will be called which matches the error trigger. Matching is done by substring separated by a dot, i.e. Error.Mowgli matches Error.Mowgli.InvalidArgument and Error.Mowgli.SomethingElse, but not Error.System.OutOfMemory and not Error.MowgliSomething.InvalidArgument.

Therefore, an error routine for a specific error needs to be in front of an error routine for general errors like in the example above.

Errors can be triggered in a module function but also directly in policy. To trigger a policy error the function Error.Throw can be used like in the following example:

STRING listName = "test"
VECTOR<NUMBER> vec = MWG.NumberListByName(listName)
IF vec.Size == 0 THEN {
    Error.Throw(Error.Error, "Error.Policy.ListNotFound",
                "List " + listName + " not found!")
    EXIT
}

 

Code in error routine with trigger Error.Policy:
// code in error routine with trigger Error.Policy
NUMBER errorSeverity = Error.Severity
STRING errorTrigger = Trigger
STRING errorMessage = Error.Message
// handle errors thrown by policy
EXIT

If the NUMBER list test is not defined, an error of severity error is triggered with error trigger Error.Policy.ListNotFound and the error message List  not found!. In the routine Error the error severity can be retrieved with function Error.Severity, the error trigger with function Trigger and the error message with function Error.Message.

If an error has been triggered Mowgli will search the error routine which is “near” to the line where the error occurred. Mowgli will first search for the first error routine with matching trigger in the routine where the error occurred (from top to bottom only in the direct child-routines, not in any sub-routines of these child-routines). If no error routine is found, Mowgli searches for an error routine with matching trigger in all direct child-routines (and no sub-routines) of the parent routine and so on until all direct root routines are checked.

  • Was this article helpful?