mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Remove experimental query
This commit is contained in:
@@ -1,245 +0,0 @@
|
||||
import go
|
||||
|
||||
private class NetHttpCookieType extends Type {
|
||||
NetHttpCookieType() { this.hasQualifiedName(package("net/http", ""), "Cookie") }
|
||||
}
|
||||
|
||||
private class GinContextSetCookieMethod extends Method {
|
||||
GinContextSetCookieMethod() {
|
||||
this.hasQualifiedName(package("github.com/gin-gonic/gin", ""), "Context", "SetCookie")
|
||||
}
|
||||
}
|
||||
|
||||
private class GorillaSessionOptionsField extends Field {
|
||||
GorillaSessionOptionsField() {
|
||||
this.hasQualifiedName(package("github.com/gorilla/sessions", ""), "Session", "Options")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simplistic points-to alternative: given a struct creation and a field name, get the values that field can be assigned.
|
||||
*
|
||||
* Assumptions:
|
||||
* - we don't reassign the variable that the creation is stored in
|
||||
* - we always access the creation through the same variable it is initially assigned to
|
||||
*
|
||||
* This should cover most typical patterns...
|
||||
*/
|
||||
private DataFlow::Node getValueForFieldWrite(StructLit sl, string field) {
|
||||
exists(Write w, DataFlow::Node base, Field f |
|
||||
f.getName() = field and
|
||||
w.writesFieldPreUpdate(base, f, result) and
|
||||
(
|
||||
sl = base.asExpr()
|
||||
or
|
||||
base.asExpr() instanceof VariableName and
|
||||
base.getAPredecessor*().asExpr() = sl
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression or its value has a sensitive name
|
||||
*/
|
||||
private predicate isAuthVariable(Expr expr) {
|
||||
exists(string val |
|
||||
(
|
||||
val = expr.getStringValue() or
|
||||
val = expr.(Name).getTarget().getName()
|
||||
) and
|
||||
val.regexpMatch("(?i).*(session|login|token|user|auth|credential).*") and
|
||||
not val.regexpMatch("(?i).*(xsrf|csrf|forgery).*")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A cookie passed as the second parameter to `net/http.SetCookie`.
|
||||
*/
|
||||
private class SetCookieSink extends DataFlow::Node {
|
||||
SetCookieSink() {
|
||||
exists(DataFlow::CallNode cn |
|
||||
cn.getTarget().hasQualifiedName(package("net/http", ""), "SetCookie") and
|
||||
this = cn.getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module NameToNetHttpCookieTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isAuthVariable(source.asExpr()) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof SetCookieSink }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(StructLit sl |
|
||||
sl.getType() instanceof NetHttpCookieType and
|
||||
getValueForFieldWrite(sl, "Name") = pred and
|
||||
sl = succ.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Tracks taint flow from sensitive names to `net/http.SetCookie`. */
|
||||
module NameToNetHttpCookieTrackingFlow = TaintTracking::Global<NameToNetHttpCookieTrackingConfig>;
|
||||
|
||||
private module BoolToNetHttpCookieTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.getType().getUnderlyingType() instanceof BoolType
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof SetCookieSink }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(StructLit sl |
|
||||
sl.getType() instanceof NetHttpCookieType and
|
||||
getValueForFieldWrite(sl, "HttpOnly") = pred and
|
||||
sl = succ.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks taint flow from a `bool` assigned to `HttpOnly` to
|
||||
* `net/http.SetCookie`.
|
||||
*/
|
||||
module BoolToNetHttpCookieTrackingFlow = TaintTracking::Global<BoolToNetHttpCookieTrackingConfig>;
|
||||
|
||||
private module BoolToGinSetCookieTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source.getBoolValue() = false }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(DataFlow::MethodCallNode mcn |
|
||||
mcn.getTarget() instanceof GinContextSetCookieMethod and
|
||||
mcn.getArgument(6) = sink and
|
||||
exists(DataFlow::Node nameArg |
|
||||
NameToGinSetCookieTrackingFlow::flowTo(nameArg) and
|
||||
mcn.getArgument(0) = nameArg
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() {
|
||||
any() // Merged with other flows in CookieWithoutHttpOnly.ql
|
||||
}
|
||||
|
||||
Location getASelectedSourceLocation(DataFlow::Node source) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks data flow from `HttpOnly` set to `false` to
|
||||
* `gin-gonic/gin.Context.SetCookie`.
|
||||
*/
|
||||
module BoolToGinSetCookieTrackingFlow = DataFlow::Global<BoolToGinSetCookieTrackingConfig>;
|
||||
|
||||
private module NameToGinSetCookieTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isAuthVariable(source.asExpr()) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(DataFlow::MethodCallNode mcn |
|
||||
mcn.getTarget() instanceof GinContextSetCookieMethod and
|
||||
mcn.getArgument(0) = sink
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks taint flow from sensitive names to `gin-gonic/gin.Context.SetCookie`.
|
||||
*/
|
||||
private module NameToGinSetCookieTrackingFlow = DataFlow::Global<NameToGinSetCookieTrackingConfig>;
|
||||
|
||||
/**
|
||||
* The receiver of `gorilla/sessions.Session.Save` call.
|
||||
*/
|
||||
private class GorillaSessionSaveSink extends DataFlow::Node {
|
||||
GorillaSessionSaveSink() {
|
||||
exists(DataFlow::MethodCallNode mcn |
|
||||
this = mcn.getReceiver() and
|
||||
mcn.getTarget()
|
||||
.hasQualifiedName(package("github.com/gorilla/sessions", ""), "Session", "Save")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class GorillaStoreSaveSink extends DataFlow::Node {
|
||||
GorillaStoreSaveSink() {
|
||||
exists(DataFlow::MethodCallNode mcn |
|
||||
this = mcn.getArgument(2) and
|
||||
mcn.getTarget()
|
||||
.hasQualifiedName(package("github.com/gorilla/sessions", ""), "CookieStore", "Save")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module GorillaCookieStoreSaveTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source
|
||||
.(DataFlow::CallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName(package("github.com/gorilla/sessions", ""), "NewCookieStore")
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof GorillaSessionSaveSink or
|
||||
sink instanceof GorillaStoreSaveSink
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::MethodCallNode cn |
|
||||
cn.getTarget()
|
||||
.hasQualifiedName(package("github.com/gorilla/sessions", ""), "CookieStore", "Get") and
|
||||
pred = cn.getReceiver() and
|
||||
succ = cn.getResult(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks data flow from gorilla cookie store creation to
|
||||
* `gorilla/sessions.Session.Save`.
|
||||
*/
|
||||
module GorillaCookieStoreSaveTrackingFlow = DataFlow::Global<GorillaCookieStoreSaveTrackingConfig>;
|
||||
|
||||
private module GorillaSessionOptionsTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(StructLit sl |
|
||||
sl.getType().hasQualifiedName(package("github.com/gorilla/sessions", ""), "Options") and
|
||||
source.asExpr() = sl
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof GorillaSessionSaveSink }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(GorillaSessionOptionsField f, DataFlow::Write w | w.writesField(succ, f, pred))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks taint flow from session options to
|
||||
* `gorilla/sessions.Session.Save`.
|
||||
*/
|
||||
module GorillaSessionOptionsTrackingFlow =
|
||||
TaintTracking::Global<GorillaSessionOptionsTrackingConfig>;
|
||||
|
||||
private module BoolToGorillaSessionOptionsTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.getType().getUnderlyingType() instanceof BoolType
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof GorillaSessionSaveSink }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(StructLit sl |
|
||||
getValueForFieldWrite(sl, "HttpOnly") = pred and
|
||||
sl = succ.asExpr()
|
||||
)
|
||||
or
|
||||
exists(GorillaSessionOptionsField f, DataFlow::Write w | w.writesField(succ, f, pred))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks taint flow from a `bool` assigned to `HttpOnly` to
|
||||
* `gorilla/sessions.Session.Save`.
|
||||
*/
|
||||
module BoolToGorillaSessionOptionsTrackingFlow =
|
||||
TaintTracking::Global<BoolToGorillaSessionOptionsTrackingConfig>;
|
||||
@@ -1,42 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Cookies without <code>HttpOnly</code> attribute are accessible to JavaScript running in the same origin. In case of
|
||||
Cross-Site Scripting (XSS) vulnerability the cookie can be stolen by malicious script.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Protect sensitive cookies, such as related to authentication, by setting <code>HttpOnly</code> to <code>true</code> to make
|
||||
them not accessible to JavaScript.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
In the following example the default <code>HttpOnly</code> value is <code>false</code>.
|
||||
</p>
|
||||
|
||||
<sample src="CookieWithoutHttpOnlyBad.go" />
|
||||
|
||||
<p>
|
||||
In the example below <code>HttpOnly</code> is set to <code>true</code>.
|
||||
</p>
|
||||
|
||||
<sample src="CookieWithoutHttpOnlyGood.go" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
<li><a href="https://golang.org/pkg/net/http/#Cookie">type Cookie,</a></li>
|
||||
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie">Set-Cookie</a> Header,</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* @name 'HttpOnly' attribute is not set to true
|
||||
* @description Omitting the 'HttpOnly' attribute for security sensitive data allows
|
||||
* malicious JavaScript to steal it in case of XSS vulnerability. Always set
|
||||
* 'HttpOnly' to 'true' to authentication related cookie to make it
|
||||
* not accessible by JavaScript.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id go/cookie-httponly-not-set
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-1004
|
||||
*/
|
||||
|
||||
import go
|
||||
import AuthCookie
|
||||
|
||||
module NetHttpCookieTrackingFlow =
|
||||
DataFlow::MergePathGraph<NameToNetHttpCookieTrackingFlow::PathNode,
|
||||
BoolToNetHttpCookieTrackingFlow::PathNode, NameToNetHttpCookieTrackingFlow::PathGraph,
|
||||
BoolToNetHttpCookieTrackingFlow::PathGraph>;
|
||||
|
||||
module GorillaTrackingFlow =
|
||||
DataFlow::MergePathGraph3<GorillaCookieStoreSaveTrackingFlow::PathNode,
|
||||
GorillaSessionOptionsTrackingFlow::PathNode, BoolToGorillaSessionOptionsTrackingFlow::PathNode,
|
||||
GorillaCookieStoreSaveTrackingFlow::PathGraph, GorillaSessionOptionsTrackingFlow::PathGraph,
|
||||
BoolToGorillaSessionOptionsTrackingFlow::PathGraph>;
|
||||
|
||||
module MergedFlow =
|
||||
DataFlow::MergePathGraph3<NetHttpCookieTrackingFlow::PathNode,
|
||||
BoolToGinSetCookieTrackingFlow::PathNode, GorillaTrackingFlow::PathNode,
|
||||
NetHttpCookieTrackingFlow::PathGraph, BoolToGinSetCookieTrackingFlow::PathGraph,
|
||||
GorillaTrackingFlow::PathGraph>;
|
||||
|
||||
import MergedFlow::PathGraph
|
||||
|
||||
/** Holds if `HttpOnly` of `net/http.SetCookie` is set to `false` or not set (default value is used). */
|
||||
predicate isNetHttpCookieFlow(
|
||||
NetHttpCookieTrackingFlow::PathNode source, NetHttpCookieTrackingFlow::PathNode sink
|
||||
) {
|
||||
exists(
|
||||
NameToNetHttpCookieTrackingFlow::PathNode sensitiveName,
|
||||
NameToNetHttpCookieTrackingFlow::PathNode setCookieSink
|
||||
|
|
||||
NameToNetHttpCookieTrackingFlow::flowPath(sensitiveName, setCookieSink) and
|
||||
(
|
||||
not BoolToNetHttpCookieTrackingFlow::flowTo(sink.getNode()) and
|
||||
source.asPathNode1() = sensitiveName and
|
||||
sink.asPathNode1() = setCookieSink
|
||||
or
|
||||
BoolToNetHttpCookieTrackingFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) and
|
||||
source.getNode().getBoolValue() = false and
|
||||
setCookieSink.getNode() = sink.getNode()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is gorilla cookie store creation to `Save` path and
|
||||
* `HttpOnly` is set to `false` or not set (default value is used).
|
||||
*/
|
||||
predicate isGorillaSessionsCookieFlow(
|
||||
GorillaTrackingFlow::PathNode source, GorillaTrackingFlow::PathNode sink
|
||||
) {
|
||||
exists(
|
||||
GorillaCookieStoreSaveTrackingFlow::PathNode cookieStoreCreate,
|
||||
GorillaCookieStoreSaveTrackingFlow::PathNode sessionSave
|
||||
|
|
||||
GorillaCookieStoreSaveTrackingFlow::flowPath(cookieStoreCreate, sessionSave) and
|
||||
(
|
||||
not GorillaSessionOptionsTrackingFlow::flowTo(sink.getNode()) and
|
||||
source.asPathNode1() = cookieStoreCreate and
|
||||
sink.asPathNode1() = sessionSave
|
||||
or
|
||||
exists(GorillaTrackingFlow::PathNode options, GorillaTrackingFlow::PathNode sessionSave2 |
|
||||
GorillaSessionOptionsTrackingFlow::flowPath(options.asPathNode2(),
|
||||
sessionSave2.asPathNode2()) and
|
||||
(
|
||||
not BoolToGorillaSessionOptionsTrackingFlow::flowTo(sink.getNode()) and
|
||||
sink = sessionSave2 and
|
||||
source = options and
|
||||
sessionSave.getNode() = sessionSave2.getNode()
|
||||
or
|
||||
BoolToGorillaSessionOptionsTrackingFlow::flowPath(source.asPathNode3(), sink.asPathNode3()) and
|
||||
source.getNode().getBoolValue() = false and
|
||||
sink.getNode() = sessionSave.getNode()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from MergedFlow::PathNode source, MergedFlow::PathNode sink
|
||||
where
|
||||
isNetHttpCookieFlow(source.asPathNode1(), sink.asPathNode1()) or
|
||||
BoolToGinSetCookieTrackingFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) or
|
||||
isGorillaSessionsCookieFlow(source.asPathNode3(), sink.asPathNode3())
|
||||
select sink.getNode(), source, sink, "Cookie attribute 'HttpOnly' is not set to true."
|
||||
@@ -1,17 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
c := http.Cookie{
|
||||
Name: "session",
|
||||
Value: "secret",
|
||||
}
|
||||
http.SetCookie(w, &c)
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
c := http.Cookie{
|
||||
Name: "session",
|
||||
Value: "secret",
|
||||
HttpOnly: true,
|
||||
}
|
||||
http.SetCookie(w, &c)
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
}
|
||||
Reference in New Issue
Block a user