Custom Detector Definition
Overview
A custom detector definition is a configuration file that defines what a custom detector is. An example of a custom detector definition is shown below.
Each custom detector definition is a script written in the
Lua programming language, but you do not need to know
anything about Lua to write one.
In this script, you can use the Detector.register function to define a custom
detector.
Most custom detector definitions will look like the example, broken
up into a metadata section and a patterns section.
The former describes the detector, and the latter defines what the search
patterns that the custom detector uses to detect findings.
Detector.register {
-- metadata section
name = "Unsafe ERC20 transfer/approve",
id = "erc20-unsafe-transfer-approve",
language = "solidity",
description = [[
This custom detector finds all ERC20 transfer/approves that do not use
SafeERC20.
]],
-- pattern section
pattern = [[
FIND
ExternalCall call IN Contract c,
Function target IN call.callees
WHERE
c.name != "SafeErc20",
target.name == "transfer" || target.name == "approve"
]],
title = "Unsafe raw ERC20 transfer/approve found in $c",
message = [[
Contract $c has a potentially unsafe ERC20 token call to $target at $call.
]],
}
Lua API Reference
The Lua API for custom detector definitions is organized into the following functionality:
- Detector registration with
Detector.register - Lower-level custom detector customization using
QueryDefandPattern
Detector.register
This function is called to register a new Vanguard custom detector, returning a
QueryDef object representing the custom detector.
Most custom detectors definitions will consist of a single call to
Detector.register.
It takes a single table as an argument, where the table has the following keys:
name(string): the name of the custom detector, displayed in the Findings table on AuditHub and in AuditHub library listings.id(string): a human-friendly name that uniquely identifies your custom detector. You can set this to whatever you want. AuditHub uses the custom detector ID to deduplicate similar findings.description(string): describes what your custom detector does, such as what bugs it can find, and situations that it should be used for.language(string): indicates what programming language the custom detector is meant to operate on. Currently, the only language supported by Vanguard issolidity.pattern(string): a string containing the source code of the PAQL query for this custom detector. This will be used as the search pattern for this query.message(string, optional): a template for the description of the finding reported for each result found by thepattern. Variables that are declared in theFIND(orASsection, if it exists) of thepatternwill be substituted into themessageby replacing all strings matching the format$variableName.title(string, optional): a template for the title of the finding reported for each result found by thepattern. Variables are also substituted into thetitle.
Multiple detectors can be defined in the same custom detector definition file, but it is typically not recommended to do so, as they cannot be run separately when the custom detector definition is chosen for a Vanguard task.
QueryDef
A QueryDef represents a custom detector that has been registered.
Every QueryDef is required to have at least one search pattern. The search
pattern can be added either by specifying a pattern field in
Detector.register, or by explicitly calling the QueryDef:add_pattern method.
QueryDef:add_pattern
Attaches a new search pattern to the custom detector.
It takes a single string argument containing the PAQL source code
for the pattern, and returns a Pattern object.
Example:
local d = Detector.register {
-- ... parameters go here ...
}
local p = d:add_pattern([[
FIND Contract c
]])
Pattern
A Pattern represents a parsed search pattern attached to a custom detector.
Pattern:report
Sets metadata for a search pattern, such as finding title, message, etc.
It takes a single table argument with the following keys:
title(string, optional): a template for the title of the finding reported for each result found by the pattern.message(string): a template for the description of the finding reported for each result found by the pattern. This can be a multiline string.
Example:
local p = d:add_pattern([[
FIND Contract c
]])
p:report {
title = "Found contract $c",
message = [[
The custom detector found the contract $c in the project.
]],
}
You can chain together method calls to avoid defining local variables for patterns.
d:add_pattern([[
FIND Contract c
]]):report {
title = "Found contract $c",
message = [[
The custom detector found the contract $c in the project.
]],
}