Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add custom codeql queries #2919

Merged
merged 5 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/codeql.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ jobs:
- name: Setup go
uses: actions/setup-go@v5
with:
go-version: '1.21'
go-version: '1.21.0'
check-latest: true
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: ./codeql/
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
Expand Down
52 changes: 52 additions & 0 deletions codeql/addipamconfig-to-exec.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* @name Command Injection From CNS ipam add result / CNS multitenancy ipam add result
* @description Flow exists from CNS ipam add result / CNS multitenancy ipam add result (untrusted) to exec command
* @kind path-problem
* @problem.severity error
* @id go/cmd-inject-ipam-add-result
* @tags security
* @security-severity 9.8
* @precision high
*/

// Detect inputs from CNS add ipam result / CNS multitenancy ipam add result to command injection
import go

private class Sink extends DataFlow2::Node {
Sink() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("os/exec", "CommandContext") and
(c.getArgument(2) = this or c.getArgument(1) = this)
or
c.getTarget().hasQualifiedName("os/exec", "Command") and
(c.getArgument(0) = this or c.getArgument(1) = this)
)
}
}

private class Source extends DataFlow2::Node {
Source() {
exists(DataFlow::CallNode c, Method m |
//m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
// "addIpamInvoker") or // this is not necessary since we call GetAllNetworkContainers right next to this = duplicated results, but if this call moves, uncomment this
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "Multitenancy",
"GetAllNetworkContainers") and
c = m.getACall() and
c.getResult(0) = this
)
}
}

module MyConfiguration implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

predicate isSource(DataFlow::Node source) { source instanceof Source }
}

module Flow = TaintTracking::Global<MyConfiguration>;

import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "potential command injection"
58 changes: 58 additions & 0 deletions codeql/cni-args-to-exec.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @name Command Injection From CNI Args
* @description Flow exists from CNI Args (untrusted) to exec command
* @kind path-problem
* @problem.severity error
* @id go/cmd-inject-cni
* @tags security
* @security-severity 9.8
* @precision high
*/

// Detect inputs from CNI ARGS to command injection
import go

private class Sink extends DataFlow2::Node {
Sink() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("os/exec", "CommandContext") and
(c.getArgument(2) = this or c.getArgument(1) = this)
or
c.getTarget().hasQualifiedName("os/exec", "Command") and
(c.getArgument(0) = this or c.getArgument(1) = this)
)
}
}

private class Source extends DataFlow2::Node {
Source() {
exists(DataFlow::CallNode c, Method m |
(
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
"Add") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
"Delete") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
"Update") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cni/network", "NetPlugin",
"Get")
) and
c = m.getACall() and
c.getArgument(0) = this
)
}
}

module MyConfiguration implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

predicate isSource(DataFlow::Node source) { source instanceof Source }
}

module Flow = TaintTracking::Global<MyConfiguration>;

import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "potential command injection"
59 changes: 59 additions & 0 deletions codeql/cns-invoker-to-exec.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @name Command Injection From CNS Invoker
* @description Flow exists from CNS Invoker (untrusted) to exec command
* @kind path-problem
* @problem.severity error
* @id go/cmd-inject-cns-invoker
* @tags security
* @security-severity 9.8
* @precision high
*/

// Detect inputs from CNS Invoker to command injection
// Does not detect flow to outside the enclosed method (which is why we analyze addIpamInvoker's results too)
import go

private class Sink extends DataFlow2::Node {
Sink() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("os/exec", "CommandContext") and
(c.getArgument(2) = this or c.getArgument(1) = this)
or
c.getTarget().hasQualifiedName("os/exec", "Command") and
(c.getArgument(0) = this or c.getArgument(1) = this)
)
}
}

private class Source extends DataFlow2::Node {
Source() {
exists(DataFlow::CallNode c, Method m |
(
m.hasQualifiedName("github.com/Azure/azure-container-networking/cns/client", "Client",
"RequestIPs") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cns/client", "Client",
"RequestIPAddress") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cns/client", "Client",
"GetNetworkContainer") or
m.hasQualifiedName("github.com/Azure/azure-container-networking/cns/client", "Client",
"GetAllNetworkContainers")
) and
c = m.getACall() and
c.getResult(0) = this
)
}
}

module MyConfiguration implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

predicate isSource(DataFlow::Node source) { source instanceof Source }
}

module Flow = TaintTracking::Global<MyConfiguration>;

import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "potential command injection"
4 changes: 4 additions & 0 deletions codeql/codeql-pack.lock.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
lockVersion: 1.0.0
dependencies: {}
compiled: false
48 changes: 48 additions & 0 deletions codeql/decode-to-exec.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @name Command Injection From Decode
* @description Flow exists from decodes (untrusted) to exec command
* @kind path-problem
* @problem.severity error
* @id go/cmd-inject-decode
* @tags security
* @security-severity 9.8
* @precision high
*/

// Detect flow from the DECODE method (which decodes http requests) to a command execution
import go

private class Sink extends DataFlow2::Node {
Sink() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("os/exec", "CommandContext") and
(c.getArgument(2) = this or c.getArgument(1) = this)
or
c.getTarget().hasQualifiedName("os/exec", "Command") and
(c.getArgument(0) = this or c.getArgument(1) = this)
)
}
}

private class Source extends DataFlow2::Node {
Source() {
exists(DataFlow::CallNode c |
c.getTarget().hasQualifiedName("github.com/Azure/azure-container-networking/common", "Decode") and
c.getArgument(2) = this
)
}
}

module MyConfiguration implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

predicate isSource(DataFlow::Node source) { source instanceof Source }
}

module Flow = TaintTracking::Global<MyConfiguration>;

import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "potential command injection"
7 changes: 7 additions & 0 deletions codeql/qlpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
library: false
warnOnImplicitThis: false
name: codeql
version: 0.0.1
dependencies:
codeql/go-all: ^1.1.3
Loading