mirror of
https://github.com/github/codeql.git
synced 2026-02-25 03:13:43 +01:00
Modelled on the javascript serverless module, but - The predicate that reports YAML files is now public so languages can implement their own file conventions. - It also reports framework and runtime. - The conveninece predicates with files still exist, but they only report the path. - Handler mapping conventions are now documented. - Use parameterised serverless module in Python, tests now pass.
161 lines
5.2 KiB
Plaintext
161 lines
5.2 KiB
Plaintext
/**
|
|
* Provides classes and predicates for working with serverless handlers.
|
|
* E.g. [AWS](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html) or [serverless](https://npmjs.com/package/serverless)
|
|
*/
|
|
|
|
/**
|
|
* Provides the input for the `ServerLess` module.
|
|
* Most of these should be provided by the `yaml` library.
|
|
*/
|
|
signature module Input {
|
|
// --------------------------------------------------
|
|
// The below should be provided by the `yaml` library.
|
|
// --------------------------------------------------
|
|
class Container {
|
|
string getAbsolutePath();
|
|
|
|
Container getParentContainer();
|
|
}
|
|
|
|
class File extends Container;
|
|
|
|
class YamlNode {
|
|
File getFile();
|
|
|
|
YamlCollection getParentNode();
|
|
}
|
|
|
|
class YamlValue extends YamlNode;
|
|
|
|
class YamlCollection extends YamlValue;
|
|
|
|
class YamlScalar extends YamlValue {
|
|
string getValue();
|
|
}
|
|
|
|
class YamlMapping extends YamlCollection {
|
|
YamlValue lookup(string key);
|
|
|
|
YamlValue getValue(int i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides classes and predicates for working with serverless handlers.
|
|
* Supports AWS, Alibaba, and serverless.
|
|
*
|
|
* Common usage is to interpret the handlers as functions and add the
|
|
* first argument of these as remote flow sources.
|
|
*/
|
|
module ServerLess<Input I> {
|
|
import I
|
|
|
|
/**
|
|
* Gets the looked up value as a convenience.
|
|
*/
|
|
pragma[inline]
|
|
private string lookupValue(YamlMapping mapping, string property) {
|
|
result = mapping.lookup(property).(YamlScalar).getValue()
|
|
}
|
|
|
|
/**
|
|
* Gets a string where an ending "/." is simplified to "/" (if it exists).
|
|
*/
|
|
bindingset[base]
|
|
private string removeTrailingDot(string base) {
|
|
if base.regexpMatch(".*/\\.")
|
|
then result = base.substring(0, base.length() - 1)
|
|
else result = base
|
|
}
|
|
|
|
/**
|
|
* Gets a string where a leading "./" is simplified to "" (if it exists).
|
|
*/
|
|
bindingset[base]
|
|
private string removeLeadingDotSlash(string base) {
|
|
if base.regexpMatch("\\./.*") then result = base.substring(2, base.length()) else result = base
|
|
}
|
|
|
|
/**
|
|
* Gets a string suitable as part of a file path.
|
|
*/
|
|
bindingset[base]
|
|
private string normalise(string base) { result = removeLeadingDotSlash(removeTrailingDot(base)) }
|
|
|
|
/**
|
|
* Holds if the `.yml` file `ymlFile` contains a serverless configuration fro `framework` with
|
|
* `handler`, `codeURI`, and `runtime` properties.
|
|
* `codeURI` and `runtime` default to the empty string if no explicit value is set in the configuration.
|
|
*
|
|
* `handler` should be interpreted in a language specific way, see `mapping.md`.
|
|
*/
|
|
predicate hasServerlessHandler(
|
|
File ymlFile, string framework, string handler, string codeUri, string runtime
|
|
) {
|
|
exists(YamlMapping resource | ymlFile = resource.getFile() |
|
|
// There exists at least "AWS::Serverless::Function" and "Aliyun::Serverless::Function"
|
|
resource.lookup("Type").(YamlScalar).getValue().regexpMatch(".*::Serverless::Function") and
|
|
framework = lookupValue(resource, "Type") and
|
|
exists(YamlMapping properties | properties = resource.lookup("Properties") |
|
|
(
|
|
handler = lookupValue(properties, "Handler") and
|
|
(
|
|
if exists(properties.lookup("CodeUri"))
|
|
then codeUri = normalise(lookupValue(properties, "CodeUri"))
|
|
else codeUri = ""
|
|
) and
|
|
(
|
|
if exists(properties.lookup("Runtime"))
|
|
then runtime = lookupValue(properties, "Runtime")
|
|
else runtime = ""
|
|
)
|
|
)
|
|
)
|
|
or
|
|
// The `serverless` library, which specifies a top-level `functions` property
|
|
framework = "Serverless" and
|
|
exists(YamlMapping functions |
|
|
functions = resource.lookup("functions") and
|
|
not exists(resource.getParentNode()) and
|
|
handler = lookupValue(functions.getValue(_), "handler") and
|
|
codeUri = "" and
|
|
(
|
|
if exists(functions.lookup("Runtime"))
|
|
then runtime = lookupValue(functions, "Runtime")
|
|
else runtime = ""
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `handler` = `filePart . astPart` and `filePart` does not contain a `.`.
|
|
* This is a convenience predicate, as in many cases the first part of the handler property
|
|
* should be interpreted as (the stem of) a file name.
|
|
*/
|
|
bindingset[handler]
|
|
predicate splitHandler(string handler, string filePart, string astPart) {
|
|
exists(string pattern | pattern = "(.*?)\\.(.*)" |
|
|
filePart = handler.regexpCapture(pattern, 1) and
|
|
astPart = handler.regexpCapture(pattern, 2)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if a file with stem `fileStem` has a serverless handler denoted by `func`.
|
|
*
|
|
* This is a convenience predicate for the common case where the first part of the
|
|
* handler property is the file name.
|
|
*
|
|
* `func` should be interpreted in a language specific way, see `mapping.md`.
|
|
*/
|
|
predicate hasServerlessHandler(string fileStem, string func, string framework, string runtime) {
|
|
exists(File ymlFile, string handler, string codeUri, string filePart |
|
|
hasServerlessHandler(ymlFile, framework, handler, codeUri, runtime)
|
|
|
|
|
splitHandler(handler, filePart, func) and
|
|
fileStem = ymlFile.getParentContainer().getAbsolutePath() + "/" + codeUri + filePart
|
|
)
|
|
}
|
|
}
|