Skip to content

Commit 51a10de

Browse files
committed
Go: Add flow sources for AWS Lambda function handlers
1 parent cda2ef4 commit 51a10de

File tree

8 files changed

+324
-0
lines changed

8 files changed

+324
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Support for flow sources in [AWS Lambda function handlers](https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html) has been added.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Provides classes for working with untrusted flow sources, sinks and taint propagators
3+
* from the `github.com/aws/aws-lambda-go/lambda` package.
4+
*/
5+
6+
import go
7+
8+
/** A source of input data in an AWS Lambda. */
9+
private class LambdaInput extends UntrustedFlowSource::Range {
10+
LambdaInput() {
11+
exists(Parameter p | p = this.asParameter() |
12+
p = any(HandlerFunction hf).getAParameter() and
13+
not p.getType().hasQualifiedName("context", "Context") and
14+
not p instanceof ReceiverVariable
15+
)
16+
}
17+
}
18+
19+
private class HandlerFunction extends FuncDef {
20+
HandlerFunction() {
21+
exists(StartOrNewHandlerFunction f, Expr e |
22+
f.getACall().getArgument(f.getHandlerArgPos()).asExpr() = e
23+
|
24+
this = e.(FunctionName).getTarget().getFuncDecl() or
25+
this = e.(FuncLit)
26+
)
27+
or
28+
this = any(Method m | m.implements(awsLambdaPkg(), "Handler", "Invoke")).getFuncDecl()
29+
or
30+
exists(ConversionExpr ce |
31+
ce.getTypeExpr().getType() instanceof HandlerImpl and
32+
this = ce.getOperand().(FunctionName).getTarget().getFuncDecl()
33+
)
34+
}
35+
}
36+
37+
private class StartOrNewHandlerFunction extends Function {
38+
int handlerArgPos;
39+
40+
StartOrNewHandlerFunction() {
41+
this.hasQualifiedName(awsLambdaPkg(),
42+
[
43+
"Start", "StartHandler", "StartHandlerFunc", "StartWithOptions", "NewHandler",
44+
"NewHandlerWithOptions"
45+
]) and
46+
handlerArgPos = 0
47+
or
48+
this.hasQualifiedName(awsLambdaPkg(), "StartHandlerWithContext") and
49+
handlerArgPos = 1
50+
}
51+
52+
int getHandlerArgPos() { result = handlerArgPos }
53+
}
54+
55+
private class HandlerImpl extends Type {
56+
HandlerImpl() { this.implements(awsLambdaPkg(), "Handler") }
57+
}
58+
59+
private string awsLambdaPkg() { result = "github.com/aws/aws-lambda-go/lambda" }
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module AwsLambda
2+
3+
go 1.21
4+
5+
require github.com/aws/aws-lambda-go v1.44.0

go/ql/test/library-tests/semmle/go/frameworks/AwsLambda/test.expected

Whitespace-only changes.
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package test
2+
3+
//go:generate depstubber -vendor github.com/aws/aws-lambda-go/lambda Handler,HandlerFunc,Option Start,StartHandler,StartHandlerFunc,StartHandlerWithContext,NewHandler
4+
// FIXME: ^ this currently fails for generic types and functions, like HandlerFunc or StartHandlerFunc. Stubs have been manually created for now.
5+
6+
import (
7+
"context"
8+
9+
"github.com/aws/aws-lambda-go/lambda"
10+
)
11+
12+
type Event struct {
13+
Name string `json:""`
14+
Age int `json:""`
15+
}
16+
17+
type Response struct {
18+
Message string `json:""`
19+
}
20+
21+
func sink(sink interface{}) {}
22+
23+
// func (TIn)
24+
func Handler0(event *Event) {
25+
sink(event.Name) // $ hasTaintFlow="selection of Name"
26+
sink(event.Age) // $ hasTaintFlow="selection of Age"
27+
sink(event) // $ hasTaintFlow="event"
28+
}
29+
30+
// func (TIn) error
31+
func Handler1(event *Event) error {
32+
sink(event.Name) // $ hasTaintFlow="selection of Name"
33+
sink(event.Age) // $ hasTaintFlow="selection of Age"
34+
sink(event) // $ hasTaintFlow="event"
35+
return nil
36+
}
37+
38+
// func (TIn) (TOut, error)
39+
func Handler2(event *Event) (*Response, error) {
40+
sink(event.Name) // $ hasTaintFlow="selection of Name"
41+
sink(event.Age) // $ hasTaintFlow="selection of Age"
42+
sink(event) // $ hasTaintFlow="event"
43+
return &Response{Message: ""}, nil
44+
}
45+
46+
// func (context.Context)
47+
func Handler3(ctx context.Context) {
48+
sink(ctx) // safe
49+
}
50+
51+
// func (context.Context) error
52+
func Handler4(ctx context.Context) error {
53+
sink(ctx) // safe
54+
return nil
55+
}
56+
57+
// func (context.Context) (TOut, error)
58+
func Handler5(ctx context.Context) (*Response, error) {
59+
sink(ctx) // safe
60+
return &Response{Message: ""}, nil
61+
}
62+
63+
// func (context.Context, TIn)
64+
func Handler6(ctx context.Context, event Event) {
65+
sink(ctx) // safe
66+
sink(event.Name) // $ hasTaintFlow="selection of Name"
67+
sink(event.Age) // $ hasTaintFlow="selection of Age"
68+
sink(event) // $ hasTaintFlow="event"
69+
}
70+
71+
// func (context.Context, TIn) error
72+
func Handler7(ctx context.Context, event Event) error {
73+
sink(ctx) // safe
74+
sink(event.Name) // $ hasTaintFlow="selection of Name"
75+
sink(event.Age) // $ hasTaintFlow="selection of Age"
76+
sink(event) // $ hasTaintFlow="event"
77+
return nil
78+
}
79+
80+
// func (context.Context, TIn) (TOut, error)
81+
func Handler8(ctx context.Context, event Event) (*Response, error) {
82+
sink(ctx) // safe
83+
sink(event.Name) // $ hasTaintFlow="selection of Name"
84+
sink(event.Age) // $ hasTaintFlow="selection of Age"
85+
sink(event) // $ hasTaintFlow="event"
86+
return &Response{Message: ""}, nil
87+
}
88+
89+
type MyHandlerFunc func(context.Context, []byte) ([]byte, error)
90+
91+
func (f MyHandlerFunc) Invoke(ctx context.Context, payload []byte) ([]byte, error) {
92+
return f(ctx, payload)
93+
}
94+
95+
func Handler9(ctx context.Context, payload []byte) ([]byte, error) {
96+
sink(payload) // $ hasTaintFlow="payload"
97+
return payload, nil
98+
}
99+
100+
type Handler10 struct{}
101+
102+
func (h *Handler10) Invoke(ctx context.Context, payload []byte) ([]byte, error) {
103+
sink(payload) // $ hasTaintFlow="payload"
104+
return payload, nil
105+
}
106+
107+
func Handler11(ctx context.Context, event Event) (*Response, error) {
108+
sink(ctx) // safe
109+
sink(event.Name) // $ hasTaintFlow="selection of Name"
110+
sink(event.Age) // $ hasTaintFlow="selection of Age"
111+
sink(event) // $ hasTaintFlow="event"
112+
return &Response{Message: ""}, nil
113+
}
114+
115+
func Handler12(ctx context.Context, payload []byte) ([]byte, error) {
116+
sink(payload) // $ hasTaintFlow="payload"
117+
return payload, nil
118+
}
119+
120+
type Handler13 struct{}
121+
122+
func (h *Handler13) Invoke(ctx context.Context, payload []byte) ([]byte, error) {
123+
sink(payload) // $ hasTaintFlow="payload"
124+
return payload, nil
125+
}
126+
127+
func Handler14(ctx context.Context, event Event) (*Response, error) {
128+
sink(ctx) // safe
129+
sink(event.Name) // $ hasTaintFlow="selection of Name"
130+
sink(event.Age) // $ hasTaintFlow="selection of Age"
131+
sink(event) // $ hasTaintFlow="event"
132+
return &Response{Message: ""}, nil
133+
}
134+
135+
func Handler15(ctx context.Context, event Event) (*Response, error) {
136+
sink(ctx) // safe
137+
sink(event.Name) // $ hasTaintFlow="selection of Name"
138+
sink(event.Age) // $ hasTaintFlow="selection of Age"
139+
sink(event) // $ hasTaintFlow="event"
140+
return &Response{Message: ""}, nil
141+
}
142+
143+
func Handler16(ctx context.Context, event Event) (*Response, error) {
144+
sink(ctx) // safe
145+
sink(event.Name) // $ hasTaintFlow="selection of Name"
146+
sink(event.Age) // $ hasTaintFlow="selection of Age"
147+
sink(event) // $ hasTaintFlow="event"
148+
return &Response{Message: ""}, nil
149+
}
150+
151+
func main() {
152+
lambda.Start(Handler0)
153+
lambda.Start(Handler1)
154+
lambda.Start(Handler2)
155+
lambda.Start(Handler3)
156+
lambda.Start(Handler4)
157+
lambda.Start(Handler5)
158+
lambda.Start(Handler6)
159+
lambda.Start(Handler7)
160+
lambda.Start(Handler8)
161+
lambda.Start(func(ctx context.Context, event Event) (*Response, error) {
162+
sink(ctx) // safe
163+
sink(event.Name) // $ hasTaintFlow="selection of Name"
164+
sink(event.Age) // $ hasTaintFlow="selection of Age"
165+
sink(event) // $ hasTaintFlow="event"
166+
return &Response{Message: ""}, nil
167+
})
168+
lambda.StartHandler(MyHandlerFunc(Handler9))
169+
lambda.StartHandler(&Handler10{})
170+
lambda.StartHandlerFunc(Handler11)
171+
lambda.StartHandlerFunc(func(ctx context.Context, event Event) (*Response, error) {
172+
sink(ctx) // safe
173+
sink(event.Name) // $ hasTaintFlow="selection of Name"
174+
sink(event.Age) // $ hasTaintFlow="selection of Age"
175+
sink(event) // $ hasTaintFlow="event"
176+
return &Response{Message: ""}, nil
177+
})
178+
lambda.StartHandlerWithContext(context.Background(), MyHandlerFunc(Handler12))
179+
lambda.StartHandlerWithContext(context.Background(), &Handler13{})
180+
lambda.StartWithOptions(Handler14)
181+
lambda.StartWithOptions(func(ctx context.Context, event Event) (*Response, error) {
182+
sink(ctx) // safe
183+
sink(event.Name) // $ hasTaintFlow="selection of Name"
184+
sink(event.Age) // $ hasTaintFlow="selection of Age"
185+
sink(event) // $ hasTaintFlow="event"
186+
return &Response{Message: ""}, nil
187+
})
188+
lambda.NewHandler(Handler15)
189+
lambda.NewHandler(func(ctx context.Context, event Event) (*Response, error) {
190+
sink(ctx) // safe
191+
sink(event.Name) // $ hasTaintFlow="selection of Name"
192+
sink(event.Age) // $ hasTaintFlow="selection of Age"
193+
sink(event) // $ hasTaintFlow="event"
194+
return &Response{Message: ""}, nil
195+
})
196+
lambda.NewHandlerWithOptions(Handler16)
197+
lambda.NewHandlerWithOptions(func(ctx context.Context, event Event) (*Response, error) {
198+
sink(ctx) // safe
199+
sink(event.Name) // $ hasTaintFlow="selection of Name"
200+
sink(event.Age) // $ hasTaintFlow="selection of Age"
201+
sink(event) // $ hasTaintFlow="event"
202+
return &Response{Message: ""}, nil
203+
})
204+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import go
2+
import semmle.go.frameworks.AwsLambda
3+
import TestUtilities.InlineFlowTest
4+
5+
module Config implements DataFlow::ConfigSig {
6+
predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }
7+
8+
predicate isSink(DataFlow::Node sink) {
9+
exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument())
10+
}
11+
}
12+
13+
import TaintFlowTest<Config>

go/ql/test/library-tests/semmle/go/frameworks/AwsLambda/vendor/github.com/aws/aws-lambda-go/lambda/stub.go

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# github.com/aws/aws-lambda-go v1.44.0
2+
## explicit
3+
github.com/aws/aws-lambda-go

0 commit comments

Comments
 (0)