Skip to content

Commit 02c41f3

Browse files
committed
JavaScript: Use shared library for serverless
1 parent 4d2ce6b commit 02c41f3

File tree

1 file changed

+40
-108
lines changed

1 file changed

+40
-108
lines changed
Lines changed: 40 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,52 @@
11
/**
22
* Provides classes and predicates for working with serverless handlers.
33
* E.g. [AWS](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html) or [serverless](https://npmjs.com/package/serverless)
4-
*/
5-
6-
import javascript
7-
8-
/**
9-
* Provides classes and predicates for working with serverless handlers.
104
* In particular a `RemoteFlowSource` is added for AWS, Alibaba, and serverless.
115
*/
12-
private module ServerLess {
13-
/**
14-
* Holds if the `.yml` file `ymlFile` contains a serverless configuration with `handler` and `codeURI` properties.
15-
* `codeURI` defaults to the empty string if no explicit value is set in the configuration.
16-
*/
17-
private predicate hasServerlessHandler(File ymlFile, string handler, string codeUri) {
18-
exists(YamlMapping resource | ymlFile = resource.getFile() |
19-
// There exists at least "AWS::Serverless::Function" and "Aliyun::Serverless::Function"
20-
resource.lookup("Type").(YamlScalar).getValue().regexpMatch(".*::Serverless::Function") and
21-
exists(YamlMapping properties | properties = resource.lookup("Properties") |
22-
handler = properties.lookup("Handler").(YamlScalar).getValue() and
23-
if exists(properties.lookup("CodeUri"))
24-
then codeUri = properties.lookup("CodeUri").(YamlScalar).getValue()
25-
else codeUri = ""
26-
)
27-
or
28-
// The `serverless` library, which specifies a top-level `functions` property
29-
exists(YamlMapping functions |
30-
functions = resource.lookup("functions") and
31-
not exists(resource.getParentNode()) and
32-
handler = functions.getValue(_).(YamlMapping).lookup("handler").(YamlScalar).getValue() and
33-
codeUri = ""
34-
)
35-
)
36-
}
37-
38-
/**
39-
* Gets a string where an ending "/." is simplified to "/" (if it exists).
40-
*/
41-
bindingset[base]
42-
private string removeTrailingDot(string base) {
43-
if base.regexpMatch(".*/\\.")
44-
then result = base.substring(0, base.length() - 1)
45-
else result = base
46-
}
476

48-
/**
49-
* Gets a string where a leading "./" is simplified to "" (if it exists).
50-
*/
51-
bindingset[base]
52-
private string removeLeadingDotSlash(string base) {
53-
if base.regexpMatch("\\./.*") then result = base.substring(2, base.length()) else result = base
54-
}
7+
import javascript
8+
import codeql.serverless.ServerLess
559

56-
/**
57-
* Gets a path to a file from a `codeURI` property and a file name from a serverless configuration.
58-
*
59-
* For example if `codeURI` is "function/." and `file` is "index", then the result becomes "function/index.js".
60-
*/
61-
bindingset[codeUri, file]
62-
private string getPathFromHandlerProperties(string codeUri, string file) {
63-
exists(string folder | folder = removeLeadingDotSlash(removeTrailingDot(codeUri)) |
64-
result = folder + file + ".js"
65-
)
66-
}
10+
private module YamlImpl implements Input {
11+
import semmle.javascript.Files
12+
import semmle.javascript.YAML
13+
}
6714

68-
/**
69-
* Holds if `file` has a serverless handler function with name `func`.
70-
*/
71-
private predicate hasServerlessHandler(File file, string func) {
72-
exists(File ymlFile, string handler, string codeUri, string fileName |
73-
hasServerlessHandler(ymlFile, handler, codeUri) and
74-
// Splits a `handler` into two components. The `fileName` to the left of the dot, and the `func` to the right.
75-
// E.g. if `handler` is "index.foo", then `fileName` is "index" and `func` is "foo".
76-
exists(string pattern | pattern = "(.*)\\.(.*)" |
77-
fileName = handler.regexpCapture(pattern, 1) and
78-
func = handler.regexpCapture(pattern, 2)
79-
)
80-
|
81-
file.getAbsolutePath() =
82-
ymlFile.getParentContainer().getAbsolutePath() + "/" +
83-
getPathFromHandlerProperties(codeUri, fileName)
84-
)
85-
}
15+
module SL = ServerLess<YamlImpl>;
8616

87-
/**
88-
* Gets a function that is a serverless request handler.
89-
*
90-
* For example: if an AWS serverless resource contains the following properties (in the "template.yml" file):
91-
* ```yaml
92-
* Handler: mylibrary.handler
93-
* Runtime: nodejs12.x
94-
* CodeUri: backend/src/
95-
* ```
96-
*
97-
* And a file "mylibrary.js" exists in the folder "backend/src" (relative to the "template.yml" file).
98-
* Then the result of this predicate is a function exported as "handler" from "mylibrary.js".
99-
* The "mylibrary.js" file could for example look like:
100-
*
101-
* ```JavaScript
102-
* module.exports.handler = function (event) { ... }
103-
* ```
104-
*/
105-
private DataFlow::FunctionNode getAServerlessHandler() {
106-
exists(File file, string handler, Module mod | hasServerlessHandler(file, handler) |
107-
mod.getFile() = file and
108-
result = mod.getAnExportedValue(handler).getAFunctionValue()
109-
)
110-
}
17+
/**
18+
* Gets a function that is a serverless request handler.
19+
*
20+
* For example: if an AWS serverless resource contains the following properties (in the "template.yml" file):
21+
* ```yaml
22+
* Handler: mylibrary.handler
23+
* Runtime: nodejs12.x
24+
* CodeUri: backend/src/
25+
* ```
26+
*
27+
* And a file "mylibrary.js" exists in the folder "backend/src" (relative to the "template.yml" file).
28+
* Then the result of this predicate is a function exported as "handler" from "mylibrary.js".
29+
* The "mylibrary.js" file could for example look like:
30+
*
31+
* ```JavaScript
32+
* module.exports.handler = function (event) { ... }
33+
* ```
34+
*/
35+
private DataFlow::FunctionNode getAServerlessHandler() {
36+
exists(File file, string stem, string handler, Module mod |
37+
SL::hasServerlessHandler(stem, handler, _, _) and
38+
file.getAbsolutePath() = stem + ".js"
39+
|
40+
mod.getFile() = file and
41+
result = mod.getAnExportedValue(handler).getAFunctionValue()
42+
)
43+
}
11144

112-
/**
113-
* A serverless request handler event, seen as a RemoteFlowSource.
114-
*/
115-
private class ServerlessHandlerEventAsRemoteFlow extends RemoteFlowSource {
116-
ServerlessHandlerEventAsRemoteFlow() { this = getAServerlessHandler().getParameter(0) }
45+
/**
46+
* A serverless request handler event, seen as a RemoteFlowSource.
47+
*/
48+
private class ServerlessHandlerEventAsRemoteFlow extends RemoteFlowSource {
49+
ServerlessHandlerEventAsRemoteFlow() { this = getAServerlessHandler().getParameter(0) }
11750

118-
override string getSourceType() { result = "Serverless event" }
119-
}
51+
override string getSourceType() { result = "Serverless event" }
12052
}

0 commit comments

Comments
 (0)