Test and Custom Plugins
  • 25 Apr 2024
  • 19 Minutes to read
  • PDF

Test and Custom Plugins

  • PDF

Article Summary

Traceable provides some plugins by default, known as Test Plugins. These plugins are pre-designed to assess and validate the security of your APIs against the most common vulnerabilities. Test plugins are part of the scan policy, and you can use them to perform security checks, identify vulnerabilities, and potential security weaknesses.

Test Plugins

In addition to these out-of-the-box plugins, Traceable provides you the option to create custom plugins and upload them to the Test Plugins page. These plugins enable you to implement specific security rules or logic tailored according to your requirements, such as enforcing particular security policies, etc. You must configure these custom plugins in the config.yaml file. The following section explains the various components of a custom plugin along with the description of the parameters and operators that are required to create this plugin.


Custom Plugins

Default Location — TRACEABLE_HOME

CLI — $HOME/.traceable

Docker — /app/userdata

File Directory

Default Location

Description

config.yaml

$TRACEABLE_HOME`

Contains the configuration for AST, such as pre-hooks, post-hooks, and custom plugin definitions.

custom

$TRACEABLE_HOME/plugins/custom

Contains the custom plugin implementations.

hooks

$TRACEABLE_HOME/hooks

Contains the pre and post-hooks.

testsuite (per API)

$TRACEABLE_HOME/data/<scan_id>/*.json

Contains the JSON files that represent test suites. Each file is a suite of tests generated at the moment for a specific API.

Configuring Custom Plugins

The following section in the config.yaml file defines the custom plugins to load during the test runs:

custom:
   sample_plugin: {}

In the above code snippet, the plugin name is sample_plugin which should match the name of the plugin defined in the custom plugin code placed in the $TRACEABLE_HOME/plugins/custom directory.


Writing a Custom Plugin

The custom plugin is written in Python and should define some of the mandatory fields for identifying the test case to be performed and the name of the plugin. The following is a sample code snippet for writing a custom plugin:

from traceable.ast.testsuite.assertion import Assertion
from traceable.ast.testsuite.mutation import Mutation
from traceable.ast.testsuite.plugin import Plugin
from traceable.config import logger
from traceable.ast.context import ScanContext
import re


class CustomSamplePlugin(Plugin):
   # Required fields - See table below for description of the fields.
   ###############################################################################
   # This is the category of the plugin and is used to group plugins
   category = "custom"  # Can't be changed
   name = "SamplePlugin"  # Name of the plugin
   title = "Custom Sample Plugin"  # Title of the plugin used to display in the vulnerability
   # Description about what the plugin does used to display in the vulnerability
   description = "This is a description text for vulnerability generated by sample plugin"
   # Mitigation for the vulnerability this plugin finds
   mitigation = "This is a mitigation text for vulnerability generated by sample plugin"
   # Severity of the vulnerability this plugin finds
   severity = "CRITICAL"
   # CVSS vector string for the vulnerability this plugin finds. Score is calculated automatically
   # https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?version=3.1&vector=AV:A/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:L
   cvssVectorString = "AV:A/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:L"


   # Optional Fields - See table below for description of the fields.
   ###############################################################################
   displayName = "Broken Object Level Authorization Plugin"  # Display name for plugin in UI
   affectedEntityType = "API"  # API/SERVICE/CUSTOM
   impact = "This is a impact text for vulnerability generated by sample plugin"
   references = "This is a references text for vulnerability generated by sample plugin"
   estimatedFixTime = 1.0
   estimatedBountyValue = 100
   tags = {
       "author": "Tester",
   }


   def __init__(self, scanctx: ScanContext):
       super().__init__(scanctx)


   # This will be called per testsuite generated per API call
   def run(self):


       # Logic to run the plugin only once per api per scan. Comment the following to run for each invocation
       if self.scanctx.get(self.name + self.api_name, False):
           return
       else:
           self.scanctx[self.name + self.api_name] = True


       logger.info("Running sample custom plugin")


       # Your custom generator here
       for role in ["sampleuser", "guestuser"]:
           attribues_suseptible_to_sample = self.attributes.get("mutated.http.request.*(?i:id|title|path\.param).*", regex=True)
           for attr in attribues_suseptible_to_sample:
               test = self.create_test_case(vector="Accessing resource using other user on %s" % role, onattribute=attr.key)
               # set test attributes. By default the attributes are inherited from the parent plugin if not set
               # test.set_attributes(self.attributes)
                
               # TODO
               # Your custom evaluation logic here
               #
               # Mutation function signature:: create(operator: str, key: str, value: any = "") - See table below for details around this function.
               test.mutations = [
                   # Set the sampleuser/guestuser creds in prehooks to be available for all plugins or you can set it here
                   Mutation.create("MUTATION_SET", "mutated.http.request.header.authorization", "${mutated.role.%s}" % role),
                  
                   Mutation.create("MUTATION_SET", attr.key, attr.value),
               ]


               # Assertion function signature:: create(key: str, operator: str, mutated: str, original: str, **kwargs) - See table below for details around this function.


               # Assertion function signature:: create_logical_assertion(operator: str = "OR|AND", assertions: List[Assertion]) - See table below for details around this function.


               assertions_or_group = Assertion.create_logical_assertion("LOGICAL_OPERATOR_OR", [
                   Assertion.create("mutated.http.response.body", "MATCH_OPERATOR_KEYS_EQUALS", "${mutated.http.response.body}", "${original.http.response.body}"),
                   Assertion.create("mutated.http.response.body", "MATCH_OPERATOR_FUZZY_EQUALS", "${mutated.http.response.body}", "${original.http.response.body}", data={"level": "5", "threshold": "0.9"}),
                   Assertion.create("mutated.http.response.body", "MATCH_OPERATOR_VALUES_SEARCH", "${mutated.http.response.body}", attr.value, data={"level": "5"}),
               ])


               test.assertions = [
                   assertions_or_group,
                   Assertion.create("mutated.http.response.code", "MATCH_OPERATOR_EQUALS", "${mutated.http.response.code}", "${original.http.response.code}"),
               ]
               test_result = test.run(self.attributes)


               # Upload the test result to the server
               self.upload_result(test_result)


               # OR you can use the following to force the result
               # // ok = True/False (True = success, False = failure/vulnerability)
               # ok = True
               # self.submit_test_results(test, ok)

The following sections specify the description of various fields present in the above code, along with the operators that you can use in them:

Required Fields

Field

Description

category

For a custom plugin, the value of this field should always be “custom”.

name

The name of the custom plugin. The name you specify here should match the one in the config.yaml file.

For example, in the above code:

name = "SamplePlugin"

Similarly, in the config.yaml file:

name = "SamplePlugin"
scan:
   plugins:
.....
      custom:
         SamplePlugin: {}

title

The plugin title you want to display in the vulnerability.

description

The description of the vulnerability identified using the plugin.

mitigation

The steps for mitigating the vulnerability that the plugin finds.

severity

The severity of the vulnerability that the plugin finds. The severity can be either UNSPECIFIED, CRITICAL, HIGH, MEDIUM, or LOW.

cvss_vector_string

The string specifies the attributes of the vulnerability, along with the attack vector, complexity, privileges required, and impact. These values are used to calculate the severity score. For more information, see CVSS Vector Strings.

Optional Fields

Field

Description

displayName

The name of the plugin should be visible in the Traceable platform user interface.

affectedEntityType

This field should have the value “CUSTOM”.

impact

A description of the impact of the vulnerability that the plugin finds.

references

The list of references for the vulnerability generated by the plugin.

estimated_fix_time

The estimated fix time for the vulnerability that the plugin finds.

estimated_bounty_value

The estimated bounty value in case the vulnerability is exploited.

tags

The tags associated with the plugin.

Attributes Convention

Attributes in a custom plugin are the configurable parameters that determine how the plugin should operate and process API requests and responses.

While the attributes starting with original. correspond to the requests Traceable is receiving from you or that are being generated from other sources, the attributes starting with mutated. represent the modified traffic from Traceable.

All attributes are stored in the form of their fully qualified name. For example, the authorization header is represented as original.http.request.header.authorization in the original request and mutated.http.request.header.authorization in the mutated request.

The following is the common list of attributes that you can use while writing a custom plugin:

Common List of Attributes

  • (original/mutated).http.request.method: "POST"

  • (original/mutated).http.request.url: "http://example.com?arg=val1&arg2=val2"

  • (original/mutated).net.host.scheme: https

  • (original/mutated).net.host.name: "example.com"

  • (original/mutated).net.host.port: 443

  • (original/mutated).http.request.header.*: <All header keys are in lowercase>

  • (original/mutated).http.request.header.user-agent: "curl/1.1"

  • (original/mutated).http.request.header.accept: "application/json"

  • (original/mutated).http.request.header.content-type: "application/json"

  • (original/mutated).http.request.cookie.PHPSESSID: "064d213baaef028e724b06042a69561dceb256f3119721b846234d72dcc35134"

  • (original/mutated).http.request.query.param.limit: 100

  • (original/mutated).http.request.body: '{"username": "user1", "password": "p4s1d135DDg4"}'

  • (original/mutated).http.request.body.username: "user1"

  • (original/mutated).http.request.body.password: "user1"

  • (original/mutated).http.response.code'

  • (original/mutated).http.response.duration

  • (original/mutated).http.response.header.*: <All header keys are in lowercase>

  • (original/mutated).http.request.header.content-type: "application/json"

  • (original/mutated).http.response.cookie.PHPSESSID: "064d213baaef028e724b06042a69561dceb256f3119721b846234d72dcc35134"

  • (original/mutated).http.response.body: '{"token": "jshdlahflafnfbdfbkfnalfja", "msg": "Success"}'

  • (original/mutated).http.response.token: "jshdlahflafnfbdfbkfnalfja"

  • (original/mutated).http.response.msg: "Success"

Attribute Operators

The following are the supported operators and their descriptions that you can use while writing a custom plugin:

Operator

Description

attributes.add_attributes(attributeList, prefix: str = "")

This operator is used to add attributes from another attribute list. It adds new values without modifying the existing ones.

attributes.set_attributes(attributeList, prefix: str = "")

This operator is used to set attributes from another attribute list. It modifies any existing values or creates them if they don’t exist.

attributes.get_one(key, , default: any = None, use_prefix: bool = False, regex: bool = False)

This operator is used to fetch one value from the attribute.

attributes.get(key, : str, default: any = None, use_prefix: bool = False, regex: bool = False)

This operator is used to fetch values from the attribute.

attributes.get_one_attr(key, , default: any = None, use_prefix: bool = False, regex: bool = False)

This operator is used to fetch one attribute.

attributes.get_original(key, , default: any = None, use_prefix: bool = False, regex: bool = False)

This operator is used to fetch the original value from the attribute.

attributes.items(self)

This operator is used to fetch the attribute list iterator.

attributes.add(key, : str, value: any, original_and_mutated: bool = False, is_dirty: bool = False, force: bool = False)

This operator is used to add attributes to the list. If original_and_mutated is True, both original and mutated keys are added; otherwise, only one copy without a prefix is added.

attributes.modify(key, : str, value: any, first_only: bool = False, regex: bool = False)

This operator is used to update all attributes with the specified key. If first_only is True, only the first attribute is updated. If the key is not found, the instruction is ignored.

attributes.set(key, : str, value: any, original_and_mutated: bool = False, regex: bool = False, is_dirty: bool = False, force: bool = True)

This operator is used to set attributes to the list. If original_and_mutated is True, both original and mutated keys are modified or are created if they don’t exist; otherwise, only one copy without a prefix is modified or created.

attributes.expand_attributes(self, data, regex: bool = False, user_prefix: bool = False)

This operator is used to expand templated variables to fetch the attribute value.

attributes.expand(self, data, regex: bool = False, use_prefix: bool = False)

This operator is used to expand templated variables to fetch the attribute value. For example, if the input is mutated.${authorization_header} and the authorization_header is request.header.jwt, then the output is mutated.request.header.jwt.

attributes.delete(key, value, regex: bool = False)

This operator is used to delete attributes from the attribute list.

Mutation Function

The mutation function adds, modifies, and deletes specific parameters within API requests.

SyntaxMutation.create(operator: str, key: str, value: any = ””)

The function contains the following parameters:

  • operator (String) — The type of operation to be performed.

  • key (String) — The key or attribute to be mutated.

  • value (Any) — The new value to replace the existing value. This parameter can contain any value, for example, none or an empty string (““).

Mutation Operators

The following are the supported operators that you can use along with the mutation function while writing a custom plugin:

Operator

Description

MUTATION_ADD

This operator adds a new key attribute with the specified value. If the key attribute already exists, it duplicates it.

Example:

test.mutaions = [
  Mutation.create("MUTATION_ADD", “mutated.http.query.param.id”, "100”)
  Mutation.create("MUTATION_ADD", “mutated.http.query.param.id”, "50”)
]

This adds two query params in the url resulting in ?id=100&id=50. This is typically used to test parameter pollution.

MUTATION_DELETE

This operator deletes an existing key attribute.

Example:

test.mutaions = [
  Mutation.create("MUTATION_DELETE", “mutated.http.query.param.id”, "”)
]

This removes the query param id from the API request.

MUTATION_MODIFY

This operator modifies a key attribute if it exists; otherwise, it does nothing.

Example:

test.mutaions = [
  Mutation.create("MUTATION_MODIFY", “mutated.http.query.param.id”, "100”)
]

This updates the query param id with value 100 if it exists; otherwise, it ignores the instruction.

MUTATION_SET

This operator sets the value of an existing key attribute or adds it if it does not exist.

Example:

test.mutaions = [
  Mutation.create("MUTATION_SET", “mutated.http.query.param.id”, "100”)
]

This sets the query param id to 100 if it already exists; otherwise, it creates a query param id with the same value.

MUTATION_REGEX_DELETE

This operator deletes all attributes that match the key/regular expression with value.

Example:

test.mutaions = [
  Mutation.create("MUTATION_REGEX_DELETE", r“mutated\.http\.query\.param.*amount”, "”)
]

This deletes all query params ending with the string “amount”.

MUTATION_REGEX_MODIFY

This operator modifies all attributes that match the key/regular expression with value.

Example:

test.mutaions = [
  Mutation.create("MUTATION_REGEX_MODIFY", r“mutated\.http\.query\.param.*amount”, "100”)
]

This modifies the query param ending with the string “amount” with value 100, if it exists; otherwise, it ignores the instruction.

MUTATION_REGEX_SET

This operator sets all attributes that match the key/regular expression with value.

Example:

test.mutaions = [
  Mutation.create("MUTATION_REGEX_SET", r“mutated\.http\.query\.param.*amount”, "100”)
]

This sets the query param ending with the string “amount” with a value of 100, if it exists; otherwise, it ignores the instruction.

Logical Assertion Function

The logical assertion function is used to apply conditional operators (AND/OR) to a list of assertions.

Syntaxcreate_logical_assertion(operator: str = "OR|AND", assertions: List[Assertion]): VULN(True)/NOT_VULN(False)

The function contains the following parameters:

  • operator (String) — The logical operator used to combine multiple assertions. The default operator is OR; however, it can be set to AND.

  • assertions (List[Assertion]) — A list of assertion objects to be combined using the logical operator.

Vulnerable Logical Assertion Operators

The following are the supported operators that you can use along with the logical assertion function while writing a custom plugin:

Operator

Description

LOGICAL_OPERATOR_AND

All assertion items in the assertion list need to be VULN(True) for the return value to be VULN(True).

LOGICAL_OPERATOR_OR

Any assertion item in the assertion list needs to be VULN(True) for the return value to be VULN(True).

Assertion Create Function

Assertions are used to evaluate the scans’ responses for vulnerabilities.

Syntax Assertion.create(key: str, operator: str, mutated: str, original: str, **kwargs)

The function contains the following parameters:

  • key (String) — Used for display purposes as part of visualization on the UI. It is not used for any processing.

  • operator (String) — The match operator used for comparison.

  • mutated (String) — The mutated value for comparison.

  • original (String) — The original value for comparison.

  • **kwargs (optional) — Additional parameters used to provide metadata for the assertion. These parameters can vary depending on the match operator used.

Assertion Operators

The following are the supported operators that you can use along with the assertion create function while writing a custom plugin:

Operator

Description

MATCH_OPERATOR_EQUALS

This operator checks whether the mutated value matches the original value. If it does, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.code", “MATCH_OPERATOR_EQUALS”, "${mutated.http.response.code}", “${original.http.response.code}”)

This evaluates to True if the response code received from the mutated request matches the response code received from the original request.

MATCH_OPERATOR_NOT_EQUALS

This operator checks whether the mutated value does not match the original value. If it does not, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body.param1", “MATCH_OPERATOR_NOT_EQUALS”, "${mutated.http.response.body.param1}", “${original.http.response.body.param1}”)

This evaluates to True if param1 received in the body of the response received for the mutated request does not match param1 in the body of the response received from the original request.

MATCH_OPERATOR_MATCHES_REGEX

This operator checks whether the mutated value matches the regex. If it does, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.duration", “MATCH_OPERATOR_MATCHES_REGEX”, "${mutated.http.response.code}", r"^5\d\d")

This evaluates to True if the response code received for the mutated request matches the specified regular expression.

MATCH_OPERATOR_NOT_MATCHES_REGEX

This operator checks whether the mutated value does not match the regex. If it does not, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.duration", “MATCH_OPERATOR_NOT_MATCHES_REGEX”, "${mutated.http.response.code}", r"^4\d\d")

This evaluates to True if the response code received for the mutated request does not match the specified regular expression.

MATCH_OPERATOR_GREATER_THAN

This operator checks whether the mutated value is greater than the original value. If it is, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.duration", “MATCH_OPERATOR_GREATER_THAN”, "${mutated.http.response.duration}", "100")

This evaluates to True if the time taken to receive a response to the mutated request takes longer than 100 ms.

MATCH_OPERATOR_GREATER_THAN_EQUALS

This operator checks whether the mutated value is greater than or equal to the original value. If it is, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body.param1", "MATCH_OPERATOR_GREATER_THAN_EQUALS", "${mutated.http.response.body.param1}", "100")

This evaluates to True if param1 in the body of the response received for the mutated request has a value greater than or equal to 100.

MATCH_OPERATOR_LESS_THAN

This operator checks whether the mutated value is less than the original value. If it is, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body.param1", "MATCH_OPERATOR_LESS_THAN", "${mutated.http.response.body.param1}", "100")

This evaluates to True if param1 in the body of the response received for the mutated request has a value less than 100.

MATCH_OPERATOR_LESS_THAN_EQUALS

This operator checks whether the mutated value is less than or equal to the original value. If it is, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body.param1", "MATCH_OPERATOR_LESS_THAN_EQUALS", "${mutated.http.response.body.param1}", "100")

This evaluates to True if param1 in the body of the response received for the mutated request has a value less than or equal to 100.

MATCH_OPERATOR_CONTAINS

This operator checks whether the mutated value contains the original value. If it does, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.
It also supports various python data structures, such as list, set, and string.

Example:

Assertion.create("http.response.body.param1", "MATCH_OPERATOR_NOT_CONTAINS", "${mutated.http.response.body.param1}", "Unauthorized")

This evaluates to True if param1 in the body of the response received for the mutated request contains the key word “Unauthorized”.

MATCH_OPERATOR_NOT_CONTAINS

This operator checks whether the mutated value does not contain the original value. If it does not, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

It also supports various python data structures, such as list, set, and string.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_NOT_CONTAINS", "${mutated.http.response.body}", "Bad request")

This evaluates to True if the body of the response received for the mutated request does not contain the key word “Bad request”.

MATCH_OPERATOR_IN

This operator checks whether the mutated value is present in the original value. If it is, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_IN", "${mutated.http.response.body.message}", "[\”FAILED\”,\”ERRORED\”]")

This evaluates to True if the message in the body of the response received for the mutated request includes either “Failed” or “Errored”.

MATCH_OPERATOR_NOT_IN

This operator checks whether the mutated value is not present in the original value. If it is not, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_NOT_IN",  "${mutated.http.response.body.message}", "[\”FAILED\”,\”ERRORED\”]")

This evaluates to True if the message in the body of the response received for the mutated request does not include either “Failed” or “Errored”.

MATCH_OPERATOR_KEYS_EQUALS

This operator checks whether the mutated and original values are JSON/URL-encoded parseable and have the same keys. If they do, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_KEYS_EQUALS",  "${mutated.http.response.body}", "${original.http.response.body}",  data={“level”:5,“content-type”: “application/json”})

This evaluates to True if the keys in the body of the response received for the mutated request has the same set of keys in the body of the response received for the original request - up to a depth of 5 for the JSON encoded tree.

MATCH_OPERATOR_KEYS_NOT_EQUALS

This operator checks whether the mutated and original values are JSON/URL-encoded parseable and have the same keys. If they do, the assertion evaluates to False/Not Vulnerable; otherwise, it evaluates to True/Vulnerable.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_KEYS_NOT_EQUALS",  "${mutated.http.response.body}", "${original.http.response.body}",  data={“level”:5,“content-type”: “application/json”})

This evaluates to True if the keys in the body of the response received for the mutated request does not have the same set of keys in the body of the response received for the original request - up to a depth of 5 for the JSON encoded tree.

MATCH_OPERATOR_FUZZY_EQUALS

This operator checks whether the mutated and original values match up to the specified threshold. If they do, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_FUZZY_EQUALS",  "${mutated.http.response.body}", "${original.http.response.body}",  data={“level”:5,“content-type”: “application/json”, “threshold”: 0.8})

This evaluates to True if the body of the response received for the mutated request matches (at least up to the threshold value of 80%) with the response received for the original request - up to a depth of 5 for the JSON encoded tree.

MATCH_OPERATOR_FUZZY_NOT_EQUALS

This operator checks whether the mutated and original values do not match up to the specified threshold. If they do not, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_FUZZY_NOT_EQUALS",  "${mutated.http.response.body}", "${original.http.response.body}",  data={“level”:5,“content-type”: “application/json”, “threshold”: 0.8})

This evaluates to True if the body of the response received for the mutated request does not match (at least up to the threshold value of 80%) with the response received for the original request - up to a depth of 5 for the JSON encoded tree.

MATCH_OPERATOR_FUZZY_KEYS_EQUALS

This operator checks whether the mutated and original values are JSON/URL-encoded parseable and the keys match up to the specified threshold. If they do, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_FUZZY_KEYS_EQUALS",  "${mutated.http.response.body}", "${original.http.response.body}",  data={“level”:5,“content-type”: “application/json”, “threshold”: 0.8})

This evaluates to True if the keys in the body of the response received for the mutated request matches (at least up to the threshold value of 80%) with the keys in the body of the response received for the original request - up to a depth of 5 for the JSON encoded tree.

MATCH_OPERATOR_FUZZY_KEYS_NOT_EQUALS

This operator checks whether the mutated and original values are JSON/URL-encoded parseable and the keys match up to the specified threshold. If they do, the assertion evaluates to False/Not Vulnerable; otherwise, it evaluates to True/Vulnerable.

Example:

Assertion.create("http.response.body",  "MATCH_OPERATOR_FUZZY_KEYS_NOT_EQUALS", "${mutated.http.response.body}",  "${original.http.response.body}", data={“level”:5,“content-type”: “application/json”, “threshold”: 0.8})

This evaluates to True if the keys in the body of the response received for the mutated request does not match (at least up to the threshold value of 80%) with the keys in the body of the response received for the original request - up to a depth of 5 for the JSON encoded tree.

MATCH_OPERATOR_VALUES_DIFFERENCE

This operator checks if the absolute difference between the mutated and original value is a certain amount.

Example:

Assertion.create("http.request.query.param.id",  "MATCH_OPERATOR_VALUES_DIFFERENCE", "${mutated.http.request.query.param.id}",  "${mutated.http.respose.param.id}", data={“level”:5,“content-type”: “application/json”, “amount”: 100})

This evaluates to True if the query param id of the mutated request differs from the response parameter id by a value of 100.

MATCH_OPERATOR_VALUES_SEARCH

This operator searches for the original value in the mutated value. The mutated value can be a dictionary or a URL-encoded payload. The level specified in this operator signifies the max depth till which Traceable should look at in the tree structure like JSON. If the original value is found in the mutated one, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.request.query.param.id",  "MATCH_OPERATOR_VALUES_SEARCH", "${mutated.http.response.body}", "15",  data={“level”:5,“content-type”: “application/json”})

This evaluates to True if the body of the response received for the mutated request contains the value 15 - up to the depth 5 for the JSON encoded tree.

MATCH_OPERATOR_VALUES_NOT_SEARCH

This operator searches for the original value in the mutated value. The mutated value can be a dictionary or a URL-encoded payload. The level specified in this operator signifies the max depth till which Traceable should look at in the tree structure like JSON. If the original value is not found in the mutated one, the assertion evaluates to True/Vulnerable; otherwise, it evaluates to False/Not Vulnerable.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_VALUES_NOT_SEARCH",  "${mutated.http.response.body}", "${mutated.http.request.query.param.id}",  data={“level”:5,“content-type”: “application/json”})

This evaluates to True if the body of the response received for the mutated request does not contain the query param id of the original request - up to a depth of 5 for the JSON encoded tree.

MATCH_OPERATOR_REGEXLOOKUP_EQUALS

This operator works in two steps:

  1. Lookup for all attributes matching the mutated regex and use them in step 2.

  2. If all attributes matched in step 1, match the original value, return True/Vulnerable; otherwise, return False/Not Vulnerable.

Example:

Assertion.create("http.response.body", "MATCH_OPERATOR_REGEXLOOKUP_EQUALS",  "mutated.http.response.body.*.carid", "15")

This evaluates to True if all parameters that match the specified regex in response to the mutated request contains the value 15.

MATCH_OPERATOR_REGEXLOOKUP_NOT_EQUALS

This operator works in two steps:

  1. Lookup for all attributes matching the mutated regex and use them in step 2.

  2. If all attributes matched in step 1, do not match the original value, return True/Vulnerable; otherwise, return False/Not Vulnerable.

Example:

Assertion.create("http.response.body",  "MATCH_OPERATOR_REGEXLOOKUP_NOT_EQUALS",  "mutated.http.response.body.*.carid", "15")

This evaluates to True if all parameters that match the specified regex in response to the mutated request does not contain the value 15.

MATCH_OPERATOR_REGEXLOOKUP_MATCHES_REGEX

This operator works in two steps:

  1. Lookup for all attributes matching the mutated regex and use them in step 2.

  2. If all attributes matched in step 1, match the original regex, return True/Vulnerable; otherwise, return False/Not Vulnerable.

Example:

Assertion.create("http.response.body",  "MATCH_OPERATOR_REGEXLOOKUP_MATCHES_REGEX",  "mutated.http.response.body.*.carid", "141[a-z0-9]{1,10}411")

This evaluates to True if all parameters that match the specified regex in response to the mutated request contains a value of that matches the regex ("141[a-z0-9]{1,10}411").

MATCH_OPERATOR_REGEXLOOKUP_NOT_MATCHES_REGEX

This operator works in two steps:

  1. Lookup for all attributes matching the mutated regex and use them in step 2.

  2. If all attributes matched in step 1, do not match the original regex, return True/Vulnerable; otherwise, return False/Not Vulnerable.

Example:

Assertion.create("http.response.body",  "MATCH_OPERATOR_REGEXLOOKUP_NOT_MATCHES_REGEX",  "mutated.http.response.body.*.carid", "141[a-z0-9]{1,10}411")

This evaluates to True if all parameters that match the specified regex in response to the mutated request does not contain a value of that matches the regex ("141[a-z0-9]{1,10}411").

MATCH_OPERATOR_ALWAYS_TRUE

This operator always returns True/Vulnerable. It is mostly useful when you are writing plugins and want to test dummy assertions.

Example:

Assertion.create("test.true", "MATCH_OPERATOR_ALWAYS_TRUE", "xyz", "abc")

MATCH_OPERATOR_ALWAYS_FALSE

This operator always returns False/Not Vulnerable. It is mostly useful when you are writing plugins and want to test dummy assertions.

Example:

Assertion.create("test.false", "MATCH_OPERATOR_ALWAYS_FALSE", "xyz", "abc")

MATCH_OPERATOR_COLLABORATOR_LOOKUP_CONTAINS

This operator is used for SSRF-based scans, where if the application reaches out to Traceable collaborator, it looks out for the presence of test id in the payload received by the collaborator.

Example:

Assertion.create("http.response.body",  "MATCH_OPERATOR_COLLABORATOR_LOOKUP_CONTAINS", "${mutated.test.id}", "${mutated.test.id}")

This evaluates to True if the payload (from the application) received by the collaborator contains the test id.

MATCH_OPERATOR_RAW

This operator executes raw lambda code supplying it with mutated and original values. If lambda returns True, it is marked as Vulnerable, else Not Vulnerable.

Example:

Assertion.create("tls.protocol.ciphers", "MATCH_OPERATOR_RAW", "${mutated.tls.protocol.ciphers}", "RSA", data={ "code": 'lambda x,y: any([y in n for n in x.split(" ")])' })

Uploading a Custom Plugin

You can upload a plugin on the Traceable platform to add it to the Testing Plugins page. To do so, navigate to TestingTest Plugins, and complete the following steps:

  1. In the page’s top right corner, click on Upload Plugin.

    Upload Plugin
  2. In the Upload Custom Plugin window, complete the following:

    Upload Custom Plugin
    1. Specify the plugin Name.

    2. Specify your custom plugin code in the code block or click on Import from file and select the code file according to your requirements.

    3. Click on Save.


Was this article helpful?

What's Next