Skip to content

Commit 8a573cc

Browse files
authored
Merge pull request github#13625 from GeekMasher/go-micro
[Go] GoMicro framework support
2 parents 5567d4d + 8d110ca commit 8a573cc

File tree

24 files changed

+2011
-0
lines changed

24 files changed

+2011
-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+
* Add support for v4 of the [Go Micro framework](https://github.com/go-micro/go-micro).

go/ql/lib/go.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import semmle.go.frameworks.Email
4040
import semmle.go.frameworks.Encoding
4141
import semmle.go.frameworks.Gin
4242
import semmle.go.frameworks.Glog
43+
import semmle.go.frameworks.GoMicro
4344
import semmle.go.frameworks.GoRestfulHttp
4445
import semmle.go.frameworks.K8sIoApimachineryPkgRuntime
4546
import semmle.go.frameworks.K8sIoApiCoreV1
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/**
2+
* Provides models of the [Go Micro library](https://github.com/go-micro/go-micro).
3+
*/
4+
5+
import go
6+
private import semmle.go.security.RequestForgeryCustomizations
7+
8+
/**
9+
* Module for Go Micro framework.
10+
*/
11+
module GoMicro {
12+
/**
13+
* A GoMicro server type.
14+
*/
15+
class GoMicroServerType extends Type {
16+
GoMicroServerType() { this.hasQualifiedName("go-micro.dev/v4/server", "Server") }
17+
}
18+
19+
/**
20+
* A GoMicro client type.
21+
*/
22+
class GoMicroClientType extends Type {
23+
GoMicroClientType() { this.hasQualifiedName("go-micro.dev/v4/client", "Client") }
24+
}
25+
26+
/**
27+
* A file that is generated by the protobuf compiler.
28+
*/
29+
class ProtocGeneratedFile extends File {
30+
ProtocGeneratedFile() { this.getBaseName().regexpMatch(".*\\.pb(\\.micro)?\\.go$") }
31+
}
32+
33+
/**
34+
* A type that is generated by the protobuf compiler.
35+
*/
36+
class ProtocMessageType extends Type {
37+
ProtocMessageType() {
38+
this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _) and
39+
exists(MethodDecl md |
40+
md.getName() = "ProtoMessage" and
41+
this = md.getReceiverDecl().getTypeExpr().getAChild().(TypeName).getType()
42+
)
43+
}
44+
}
45+
46+
/**
47+
* A Server Interface type.
48+
*/
49+
class ServiceInterfaceType extends InterfaceType {
50+
NamedType namedType;
51+
52+
ServiceInterfaceType() {
53+
this = namedType.getUnderlyingType() and
54+
namedType.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _)
55+
}
56+
57+
/**
58+
* Gets the name of the interface.
59+
*/
60+
override string getName() { result = namedType.getName() }
61+
62+
/**
63+
* Gets the named type on top of this interface type.
64+
*/
65+
NamedType getNamedType() { result = namedType }
66+
}
67+
68+
/**
69+
* A Service server handler type.
70+
*/
71+
class ServiceServerType extends NamedType {
72+
ServiceServerType() {
73+
this.implements(any(ServiceInterfaceType i)) and
74+
this.getName().regexpMatch("(?i).*Handler") and
75+
this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _)
76+
}
77+
}
78+
79+
/**
80+
* A Client server handler type.
81+
*/
82+
class ClientServiceType extends NamedType {
83+
ClientServiceType() {
84+
this.implements(any(ServiceInterfaceType i)) and
85+
this.getName().regexpMatch("(?i).*Service") and
86+
this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _)
87+
}
88+
}
89+
90+
/**
91+
* A service register handler.
92+
*/
93+
class ServiceRegisterHandler extends Function {
94+
ServiceRegisterHandler() {
95+
this.getName().regexpMatch("(?i)register" + any(ServiceServerType c).getName()) and
96+
this.getParameterType(0) instanceof GoMicroServerType and
97+
this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _)
98+
}
99+
}
100+
101+
/**
102+
* A service handler.
103+
*/
104+
class ServiceHandler extends Method {
105+
ServiceHandler() {
106+
exists(DataFlow::CallNode call |
107+
call.getTarget() instanceof ServiceRegisterHandler and
108+
this = call.getArgument(1).getType().getMethod(_) and
109+
this.implements(any(ServiceInterfaceType i).getNamedType().getMethod(_))
110+
)
111+
}
112+
}
113+
114+
/**
115+
* A client service function.
116+
*/
117+
class ClientService extends Function {
118+
ClientService() {
119+
this.getName().regexpMatch("(?i)new" + any(ClientServiceType c).getName()) and
120+
this.getParameterType(0) instanceof StringType and
121+
this.getParameterType(1) instanceof GoMicroClientType and
122+
this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _)
123+
}
124+
}
125+
126+
/**
127+
* An SSRF sink for the Client service function.
128+
*/
129+
class ClientRequestUrlAsSink extends RequestForgery::Sink {
130+
ClientRequestUrlAsSink() {
131+
exists(DataFlow::CallNode call |
132+
call.getArgument(0) = this and
133+
call.getTarget() instanceof ClientService
134+
)
135+
}
136+
137+
override DataFlow::Node getARequest() { result = this }
138+
139+
override string getKind() { result = "URL" }
140+
}
141+
142+
/**
143+
* A set of remote requests from a service handler.
144+
*/
145+
class Request extends UntrustedFlowSource::Range instanceof DataFlow::ParameterNode {
146+
Request() {
147+
exists(ServiceHandler handler |
148+
this.asParameter().isParameterOf(handler.getFuncDecl(), 1) and
149+
handler.getParameterType(0).hasQualifiedName("context", "Context") and
150+
this.getType().(PointerType).getBaseType() instanceof ProtocMessageType
151+
)
152+
}
153+
}
154+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
edges
2+
| main.go:18:46:18:48 | definition of req | main.go:18:46:18:48 | definition of req |
3+
| main.go:18:46:18:48 | definition of req | main.go:18:46:18:48 | definition of req |
4+
| main.go:18:46:18:48 | definition of req | main.go:21:28:21:31 | name |
5+
| main.go:18:46:18:48 | definition of req | main.go:21:28:21:31 | name |
6+
| main.go:18:46:18:48 | definition of req | proto/Hello.pb.micro.go:85:53:85:54 | definition of in |
7+
| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | proto/Hello.pb.micro.go:85:53:85:54 | definition of in |
8+
| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | proto/Hello.pb.micro.go:86:37:86:38 | in |
9+
| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | proto/Hello.pb.micro.go:86:37:86:38 | in |
10+
| proto/Hello.pb.micro.go:86:37:86:38 | in | main.go:18:46:18:48 | definition of req |
11+
| proto/Hello.pb.micro.go:86:37:86:38 | in | main.go:18:46:18:48 | definition of req |
12+
| proto/Hello.pb.micro.go:86:37:86:38 | in | proto/Hello.pb.micro.go:85:53:85:54 | definition of in |
13+
| proto/Hello.pb.micro.go:86:37:86:38 | in | proto/Hello.pb.micro.go:85:53:85:54 | definition of in |
14+
nodes
15+
| main.go:18:46:18:48 | definition of req | semmle.label | definition of req |
16+
| main.go:18:46:18:48 | definition of req | semmle.label | definition of req |
17+
| main.go:21:28:21:31 | name | semmle.label | name |
18+
| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | semmle.label | definition of in |
19+
| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | semmle.label | definition of in |
20+
| proto/Hello.pb.micro.go:86:37:86:38 | in | semmle.label | in |
21+
| proto/Hello.pb.micro.go:86:37:86:38 | in | semmle.label | in |
22+
subpaths
23+
#select
24+
| main.go:21:28:21:31 | name | main.go:18:46:18:48 | definition of req | main.go:21:28:21:31 | name | This log entry depends on a $@. | main.go:18:46:18:48 | definition of req | user-provided value |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE-117/LogInjection.ql
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import (
4+
pb "codeql-go-tests/frameworks/GoMicro/proto"
5+
"context"
6+
"fmt"
7+
"log"
8+
9+
micro "go-micro.dev/v4"
10+
)
11+
12+
func main() {
13+
// service
14+
service := micro.NewService()
15+
service.Init()
16+
// context
17+
ctx := context.Background()
18+
19+
greeterService := pb.NewGreeterService("http://localhost:8000", service.Client()) // $ clientRequest="http:\/\/localhost:8000"
20+
// request
21+
req := pb.Request{Name: "Mona"}
22+
resp, err := greeterService.Hello(ctx, &req)
23+
24+
if err != nil {
25+
log.Fatal(err)
26+
}
27+
28+
fmt.Println("Hello :: %s", resp.Greeting)
29+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module codeql-go-tests/frameworks/GoMicro
2+
3+
go 1.15
4+
5+
require (
6+
go-micro.dev/v4 v4.10.2
7+
google.golang.org/protobuf v1.28.1
8+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
failures
2+
testFailures
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import go
2+
import TestUtilities.InlineExpectationsTest
3+
4+
module GoMicroTest implements TestSig {
5+
string getARelevantTag() { result = ["serverRequest", "clientRequest"] }
6+
7+
predicate hasActualResult(Location location, string element, string tag, string value) {
8+
exists(DataFlow::Node node |
9+
node.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
10+
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
11+
(
12+
node instanceof GoMicro::Request and
13+
element = node.toString() and
14+
value = "\"" + node.toString() + "\"" and
15+
tag = "serverRequest"
16+
or
17+
node instanceof GoMicro::ClientRequestUrlAsSink and
18+
element = node.toString() and
19+
value = node.toString().replaceAll("/", "\\/") and
20+
tag = "clientRequest"
21+
)
22+
)
23+
}
24+
}
25+
26+
import MakeTest<GoMicroTest>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package main
2+
3+
//go:generate depstubber -vendor go-micro.dev/v4 Service,Option,Options NewService,Name,Handle,Server,Client
4+
//go:generate depstubber -vendor go-micro.dev/v4/server Server Handle
5+
//go:generate depstubber -vendor go-micro.dev/v4/client Client Call
6+
7+
import (
8+
pb "codeql-go-tests/frameworks/GoMicro/proto"
9+
"context"
10+
"fmt"
11+
"log"
12+
13+
micro "go-micro.dev/v4"
14+
)
15+
16+
type Greeter struct{}
17+
18+
func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error { // $ serverRequest="definition of req"
19+
// var access
20+
name := req.Name
21+
fmt.Println("Name :: %s", name)
22+
return nil
23+
}
24+
25+
func main() {
26+
// service
27+
service := micro.NewService(
28+
micro.Name("helloworld"),
29+
micro.Handle(":8080"),
30+
)
31+
32+
service.Init()
33+
34+
pb.RegisterGreeterHandler(service.Server(), new(Greeter))
35+
36+
if err := service.Run(); err != nil {
37+
log.Fatal(err)
38+
}
39+
}

0 commit comments

Comments
 (0)