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

Go: New File System Access Sinks #14064

Merged
merged 22 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 3 additions & 1 deletion go/ql/lib/go.qll
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,6 @@ import semmle.go.frameworks.XPath
import semmle.go.frameworks.Yaml
import semmle.go.frameworks.Zap
import semmle.go.security.FlowSources
import semmle.go.security.FileSystemAccess
import semmle.go.frameworks.Afero
import semmle.go.frameworks.Iris
import semmle.go.frameworks.Fiber
am0o0 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,132 +1,17 @@
import go

/**
* The File system access sinks of `net/http` package
*/
class HttpServeFile extends FileSystemAccess::Range, DataFlow::CallNode {
HttpServeFile() {
exists(Function f |
f.hasQualifiedName("net/http", "ServeFile") and
this = f.getACall()
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(2) }
}

/**
* The File system access sinks of [beego](https://github.com/beego/beego) web framework
*/
class BeegoFileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

BeegoFileSystemAccess() {
exists(Method m |
(
m.hasQualifiedName(package("github.com/beego/beego", "server/web/context"), "BeegoOutput",
"Download") and
pathArg = 0
or
m.hasQualifiedName(package("github.com/beego/beego", "server/web"), "Controller",
"SaveToFileWithBuffer") and
pathArg = 1
) and
this = m.getACall()
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
}

/**
* The File system access sinks of [beego](https://github.com/beego/beego) web framework
*/
class EchoFileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

EchoFileSystemAccess() {
exists(Method m |
m.hasQualifiedName(package("github.com/labstack/echo", ""), "Context", ["Attachment", "File"]) and
this = m.getACall() and
pathArg = 0
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
}

/**
* The File system access sinks of [gin](https://github.com/gin-gonic/gin) web framework
* Provides classes for working with sinks and taint propagators
* from the `github.com/spf13/afero` package.
*/
class GinFileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

GinFileSystemAccess() {
exists(Method m |
(
m.hasQualifiedName(package("github.com/gin-gonic/gin", ""), "Context",
["File", "FileAttachment"]) and
pathArg = 0
or
m.hasQualifiedName(package("github.com/gin-gonic/gin", ""), "Context", "SaveUploadedFile") and
pathArg = 1
) and
this = m.getACall()
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
}

/**
* The File system access sinks of [iris](https://github.com/kataras/iris) web framework
*/
class IrisFileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

IrisFileSystemAccess() {
exists(Method m |
(
m.hasQualifiedName(package("github.com/kataras/iris", "context"), "Context",
["SendFile", "ServeFile", "SendFileWithRate", "ServeFileWithRate", "UploadFormFiles"]) and
pathArg = 0
or
m.hasQualifiedName(package("github.com/kataras/iris", "context"), "Context", "SaveFormFile") and
pathArg = 1
) and
this = m.getACall()
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
}

/**
* The File system access sinks of [fiber](https://github.com/gofiber/fiber) web framework
*/
class FiberSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

FiberSystemAccess() {
exists(Method m |
(
m.hasQualifiedName(package("github.com/gofiber/fiber", ""), "Ctx", "SendFile") and
pathArg = 0
or
m.hasQualifiedName(package("github.com/gofiber/fiber", ""), "Ctx", "SaveFile") and
pathArg = 1
) and
this = m.getACall()
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
}
import go

/**
* Provide File system access sinks of [afero](https://github.com/spf13/afero) framework
*/
module Afero {
/**
* Gets all versions of `github.com/spf13/afero`
*/
string aferoPackage() { result = package("github.com/spf13/afero", "") }

/**
Expand Down Expand Up @@ -207,7 +92,7 @@ module Afero {
predicate aferoSanitizer(DataFlow::Node n) {
exists(Function f |
f.hasQualifiedName(aferoPackage(), ["NewBasePathFs", "NewIOFS"]) and
TaintTracking::localTaint(f.getACall(), n)
DataFlow::localFlow(f.getACall(), n)
am0o0 marked this conversation as resolved.
Show resolved Hide resolved
)
}

Expand All @@ -221,7 +106,7 @@ module Afero {
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
am0o0 marked this conversation as resolved.
Show resolved Hide resolved
exists(StructLit st | st.getType().hasQualifiedName(aferoPackage(), "Afero") |
n1.asExpr() = st.getAChildExpr().(KeyValueExpr).getAChildExpr() and
am0o0 marked this conversation as resolved.
Show resolved Hide resolved
n2.asExpr() = st.getParent()
n2.asExpr() = st
)
}
}
26 changes: 18 additions & 8 deletions go/ql/lib/semmle/go/frameworks/Beego.qll
Original file line number Diff line number Diff line change
Expand Up @@ -278,21 +278,31 @@ module Beego {
}
}

/**
* The File system access sinks
*/
private class FsOperations extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

FsOperations() {
this.getTarget().hasQualifiedName(packagePath(), "Walk")
this.getTarget().hasQualifiedName(packagePath(), "Walk") and pathArg = 1
or
exists(Method m | this = m.getACall() |
m.hasQualifiedName(packagePath(), "FileSystem", "Open") or
m.hasQualifiedName(packagePath(), "Controller", "SaveToFile")
m.hasQualifiedName(packagePath(), "FileSystem", "Open") and pathArg = 0
or
m.hasQualifiedName(packagePath(), "Controller", "SaveToFile") and pathArg = 1
or
m.hasQualifiedName(contextPackagePath(), "BeegoOutput", "Download") and
pathArg = 0
or
// SaveToFileWithBuffer only available in v2
m.hasQualifiedName("github.com/beego/beego/v2/server/web", "Controller",
"SaveToFileWithBuffer") and
pathArg = 1
)
}

override DataFlow::Node getAPathArgument() {
this.getTarget().getName() = ["Walk", "SaveToFile"] and result = this.getArgument(1)
or
this.getTarget().getName() = "Open" and result = this.getArgument(0)
}
override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
}

private class RedirectMethods extends Http::Redirect::Range, DataFlow::CallNode {
Expand Down
17 changes: 17 additions & 0 deletions go/ql/lib/semmle/go/frameworks/Echo.qll
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,21 @@ private module Echo {

override Http::ResponseWriter getResponseWriter() { none() }
}

/**
* The File system access sinks
*/
class FsOperations extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

FsOperations() {
exists(Method m |
m.hasQualifiedName(packagePath(), "Context", ["Attachment", "File"]) and
this = m.getACall() and
pathArg = 0
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
am0o0 marked this conversation as resolved.
Show resolved Hide resolved
}
}
38 changes: 38 additions & 0 deletions go/ql/lib/semmle/go/frameworks/Fiber.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Provides classes for working the `github.com/gofiber/fiber` package.
*/

import go

private module Gin {
am0o0 marked this conversation as resolved.
Show resolved Hide resolved
/** Gets the package name `github.com/gofiber/fiber`. */
string packagePath() { result = package("github.com/gofiber/fiber", "") }

/** Gets the v2 module path `github.com/gofiber/fiber/v2` */
string v2modulePath() { result = "github.com/gofiber/fiber/v2" }

/**
* The File system access sinks
*/
class FsOperations extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

FsOperations() {
exists(Method m |
(
m.hasQualifiedName(packagePath(), "Ctx", ["SendFile", "Download"]) and
pathArg = 0
or
m.hasQualifiedName(packagePath(), "Ctx", "SaveFile") and
pathArg = 1
or
m.hasQualifiedName(v2modulePath(), "Ctx", "SaveFileToStorage") and
pathArg = 1
) and
this = m.getACall()
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
}
}
22 changes: 22 additions & 0 deletions go/ql/lib/semmle/go/frameworks/Gin.qll
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,26 @@ private module Gin {
)
}
}

/**
* The File system access sinks
*/
class FsOperations extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

FsOperations() {
exists(Method m |
(
m.hasQualifiedName(packagePath(), "Context", ["File", "FileAttachment"]) and
pathArg = 0
or
m.hasQualifiedName(packagePath(), "Context", "SaveUploadedFile") and
pathArg = 1
) and
this = m.getACall()
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
}
}
49 changes: 49 additions & 0 deletions go/ql/lib/semmle/go/frameworks/Iris.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Provides classes for working the `github.com/kataras/iris` package.
*/

import go

private module Gin {
am0o0 marked this conversation as resolved.
Show resolved Hide resolved
/** Gets the v1 module path `github.com/gofiber/fiber`. */
am0o0 marked this conversation as resolved.
Show resolved Hide resolved
string v1modulePath() { result = "github.com/kataras/iris" }

/** Gets the v12 module path `github.com/gofiber/fiber/v12` */
am0o0 marked this conversation as resolved.
Show resolved Hide resolved
string v12modulePath() { result = "github.com/kataras/iris/v12" }

/** Gets the path for the context package of all versions of beego. */
string contextPackagePath() {
result = v12contextPackagePath()
or
result = v1contextPackagePath()
}

/** Gets the path for the context package of beego v12. */
string v12contextPackagePath() { result = v12modulePath() + "/context" }

/** Gets the path for the context package of beego v1. */
string v1contextPackagePath() { result = v1modulePath() + "/server/web/context" }

/**
* The File system access sinks
*/
class FsOperations extends FileSystemAccess::Range, DataFlow::CallNode {
int pathArg;

FsOperations() {
exists(Method m |
(
m.hasQualifiedName(contextPackagePath(), "Context",
["SendFile", "ServeFile", "SendFileWithRate", "ServeFileWithRate", "UploadFormFiles"]) and
pathArg = 0
or
m.hasQualifiedName(v12contextPackagePath(), "Context", "SaveFormFile") and
pathArg = 1
) and
this = m.getACall()
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(pathArg) }
}
}
14 changes: 14 additions & 0 deletions go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,18 @@ module NetHttp {

override predicate guardedBy(DataFlow::Node check) { check = handlerReg.getArgument(0) }
}

/**
* The File system access sinks
*/
class HttpServeFile extends FileSystemAccess::Range, DataFlow::CallNode {
HttpServeFile() {
exists(Function f |
f.hasQualifiedName("net/http", "ServeFile") and
this = f.getACall()
)
}

override DataFlow::Node getAPathArgument() { result = this.getArgument(2) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module FileSystemAccessTest implements TestSig {
succ.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
element = succ.toString() and
value = succ.toString() and
value = succ.asExpr().(StructLit).getType().getName() and
tag = "succ"
or
pred.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
Expand Down
Loading