diff --git a/ql/src/experimental/frameworks/CleverGo.qll b/ql/src/experimental/frameworks/CleverGo.qll index 08e8219dc7b..fc9e3e08bdb 100644 --- a/ql/src/experimental/frameworks/CleverGo.qll +++ b/ql/src/experimental/frameworks/CleverGo.qll @@ -198,168 +198,249 @@ private module CleverGo { /** * Models HTTP ResponseBody. */ - private class HttpResponseBody extends HTTP::ResponseBody::Range { - string package; - DataFlow::CallNode bodySetterCall; - string contentType; + private class HttpResponseBodyContentTypeString extends HTTP::ResponseBody::Range { + string contentTypeString; - HttpResponseBody() { - // HTTP ResponseBody models for package: clevergo.tech/clevergo@v0.5.2 - package = packagePath() and - ( - // One call sets both body and content-type (which is implicit in the func name). - // Receiver type: Context - exists(string methodName, Method m | - m.hasQualifiedName(package, "Context", methodName) and - bodySetterCall = m.getACall() - | - // signature: func (*Context).Error(code int, msg string) error - methodName = "Error" and - this = bodySetterCall.getArgument(1) and - contentType = "text/plain" - or - // signature: func (*Context).HTML(code int, html string) error - methodName = "HTML" and - this = bodySetterCall.getArgument(1) and - contentType = "text/html" - or - // signature: func (*Context).HTMLBlob(code int, bs []byte) error - methodName = "HTMLBlob" and - this = bodySetterCall.getArgument(1) and - contentType = "text/html" - or - // signature: func (*Context).JSON(code int, data interface{}) error - methodName = "JSON" and - this = bodySetterCall.getArgument(1) and - contentType = "application/json" - or - // signature: func (*Context).JSONBlob(code int, bs []byte) error - methodName = "JSONBlob" and - this = bodySetterCall.getArgument(1) and - contentType = "application/json" - or - // signature: func (*Context).JSONP(code int, data interface{}) error - methodName = "JSONP" and - this = bodySetterCall.getArgument(1) and - contentType = "application/javascript" - or - // signature: func (*Context).JSONPBlob(code int, bs []byte) error - methodName = "JSONPBlob" and - this = bodySetterCall.getArgument(1) and - contentType = "application/javascript" - or - // signature: func (*Context).JSONPCallback(code int, callback string, data interface{}) error - methodName = "JSONPCallback" and - this = bodySetterCall.getArgument(2) and - contentType = "application/javascript" - or - // signature: func (*Context).JSONPCallbackBlob(code int, callback string, bs []byte) (err error) - methodName = "JSONPCallbackBlob" and - this = bodySetterCall.getArgument(2) and - contentType = "application/javascript" - or - // signature: func (*Context).String(code int, s string) error - methodName = "String" and - this = bodySetterCall.getArgument(1) and - contentType = "text/plain" - or - // signature: func (*Context).StringBlob(code int, bs []byte) error - methodName = "StringBlob" and - this = bodySetterCall.getArgument(1) and - contentType = "text/plain" - or - // signature: func (*Context).Stringf(code int, format string, a ...interface{}) error - methodName = "Stringf" and - this = bodySetterCall.getArgument([1, any(int i | i >= 2)]) and - contentType = "text/plain" - or - // signature: func (*Context).XML(code int, data interface{}) error - methodName = "XML" and - this = bodySetterCall.getArgument(1) and - contentType = "text/xml" - or - // signature: func (*Context).XMLBlob(code int, bs []byte) error - methodName = "XMLBlob" and - this = bodySetterCall.getArgument(1) and - contentType = "text/xml" - ) + HttpResponseBodyContentTypeString() { + exists(string package, string receiverName | + holdsBodyAndContentTypeString(package, receiverName, this, contentTypeString) or - // One call sets both body and content-type (both are parameters in the func call). - // Receiver type: Context - exists(string methodName, Method m | - m.hasQualifiedName(package, "Context", methodName) and - bodySetterCall = m.getACall() + exists(DataFlow::CallNode bodySetterCall, DataFlow::CallNode contentTypeSetterCall | + holdsBodyOnly(package, receiverName, bodySetterCall, this) and + holdsContentTypeString(package, receiverName, contentTypeSetterCall, contentTypeString) | - // signature: func (*Context).Blob(code int, contentType string, bs []byte) (err error) - methodName = "Blob" and - this = bodySetterCall.getArgument(2) and - contentType = bodySetterCall.getArgument(1).getAPredecessor*().getStringValue() - or - // signature: func (*Context).Emit(code int, contentType string, body string) (err error) - methodName = "Emit" and - this = bodySetterCall.getArgument(2) and - contentType = bodySetterCall.getArgument(1).getAPredecessor*().getStringValue() - ) - or - // Two calls, one to set the response body and one to set the content-type. - // Receiver type: Context - exists(string methodName, Method m | - m.hasQualifiedName(package, "Context", methodName) and - bodySetterCall = m.getACall() - | - // signature: func (*Context).Write(data []byte) (int, error) - methodName = "Write" and - this = bodySetterCall.getArgument(0) - or - // signature: func (*Context).WriteString(data string) (int, error) - methodName = "WriteString" and - this = bodySetterCall.getArgument(0) - ) and - ( - // Receiver type: Context - exists(string methodName, Method m, DataFlow::CallNode contentTypeSetterCall | - m.hasQualifiedName(package, "Context", methodName) and - contentTypeSetterCall = m.getACall() and - contentTypeSetterCall.getReceiver().getAPredecessor*() = - bodySetterCall.getReceiver().getAPredecessor*() - | - // signature: func (*Context).SetContentType(v string) - methodName = "SetContentType" and - contentType = contentTypeSetterCall.getArgument(0).getAPredecessor*().getStringValue() - ) - or - // Receiver type: Context - exists(string methodName, Method m, DataFlow::CallNode contentTypeSetterCall | - m.hasQualifiedName(package, "Context", methodName) and - contentTypeSetterCall = m.getACall() and - contentTypeSetterCall.getReceiver().getAPredecessor*() = - bodySetterCall.getReceiver().getAPredecessor*() - | - // signature: func (*Context).SetContentTypeHTML() - methodName = "SetContentTypeHTML" and - contentType = "text/html" - or - // signature: func (*Context).SetContentTypeJSON() - methodName = "SetContentTypeJSON" and - contentType = "application/json" - or - // signature: func (*Context).SetContentTypeText() - methodName = "SetContentTypeText" and - contentType = "text/plain" - or - // signature: func (*Context).SetContentTypeXML() - methodName = "SetContentTypeXML" and - contentType = "text/xml" - ) + contentTypeSetterCall.getReceiver().getAPredecessor*() = + bodySetterCall.getReceiver().getAPredecessor*() ) ) } - override string getAContentType() { result = contentType } + override string getAContentType() { result = contentTypeString } override HTTP::ResponseWriter getResponseWriter() { none() } } + /** + * Models HTTP ResponseBody. + */ + private class HttpResponseBodyContentTypeNode extends HTTP::ResponseBody::Range { + DataFlow::Node contentTypeNode; + + HttpResponseBodyContentTypeNode() { + exists(string package, string receiverName | + holdsBodyAndContentTypeNode(package, receiverName, this, contentTypeNode) + or + exists(DataFlow::CallNode bodySetterCall, DataFlow::CallNode contentTypeSetterCall | + holdsBodyOnly(package, receiverName, bodySetterCall, this) and + holdsContentTypeNode(package, receiverName, contentTypeSetterCall, contentTypeNode) + | + contentTypeSetterCall.getReceiver().getAPredecessor*() = + bodySetterCall.getReceiver().getAPredecessor*() + ) + ) + } + + override DataFlow::Node getAContentTypeNode() { result = contentTypeNode.getAPredecessor*() } + + override HTTP::ResponseWriter getResponseWriter() { none() } + } + + // Holds for a call that sets the body. + private predicate holdsBodyOnly( + string package, string receiverName, DataFlow::CallNode bodySetterCall, DataFlow::Node bodyNode + ) { + exists(string methodName, Method m | + m.hasQualifiedName(package, receiverName, methodName) and + bodySetterCall = m.getACall() + | + package = packagePath() and + ( + // Receiver type: Context + receiverName = "Context" and + ( + // signature: func (*Context).Write(data []byte) (int, error) + methodName = "Write" and + bodyNode = bodySetterCall.getArgument(0) + or + // signature: func (*Context).WriteString(data string) (int, error) + methodName = "WriteString" and + bodyNode = bodySetterCall.getArgument(0) + ) + ) + ) + } + + // Holds for a call that sets the body; the content-type is implicit. + private predicate holdsBodyAndContentTypeString( + string package, string receiverName, DataFlow::Node bodyNode, string contentTypeString + ) { + // One call sets both body and content-type (which is implicit in the func name). + exists(string methodName, Method m, DataFlow::CallNode bodySetterCall | + m.hasQualifiedName(package, receiverName, methodName) and + bodySetterCall = m.getACall() + | + package = packagePath() and + ( + // Receiver type: Context + receiverName = "Context" and + ( + // signature: func (*Context).Error(code int, msg string) error + methodName = "Error" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "text/plain" + or + // signature: func (*Context).HTML(code int, html string) error + methodName = "HTML" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "text/html" + or + // signature: func (*Context).HTMLBlob(code int, bs []byte) error + methodName = "HTMLBlob" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "text/html" + or + // signature: func (*Context).JSON(code int, data interface{}) error + methodName = "JSON" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "application/json" + or + // signature: func (*Context).JSONBlob(code int, bs []byte) error + methodName = "JSONBlob" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "application/json" + or + // signature: func (*Context).JSONP(code int, data interface{}) error + methodName = "JSONP" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "application/javascript" + or + // signature: func (*Context).JSONPBlob(code int, bs []byte) error + methodName = "JSONPBlob" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "application/javascript" + or + // signature: func (*Context).JSONPCallback(code int, callback string, data interface{}) error + methodName = "JSONPCallback" and + bodyNode = bodySetterCall.getArgument(2) and + contentTypeString = "application/javascript" + or + // signature: func (*Context).JSONPCallbackBlob(code int, callback string, bs []byte) (err error) + methodName = "JSONPCallbackBlob" and + bodyNode = bodySetterCall.getArgument(2) and + contentTypeString = "application/javascript" + or + // signature: func (*Context).String(code int, s string) error + methodName = "String" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "text/plain" + or + // signature: func (*Context).StringBlob(code int, bs []byte) error + methodName = "StringBlob" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "text/plain" + or + // signature: func (*Context).Stringf(code int, format string, a ...interface{}) error + methodName = "Stringf" and + bodyNode = bodySetterCall.getArgument([1, any(int i | i >= 2)]) and + contentTypeString = "text/plain" + or + // signature: func (*Context).XML(code int, data interface{}) error + methodName = "XML" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "text/xml" + or + // signature: func (*Context).XMLBlob(code int, bs []byte) error + methodName = "XMLBlob" and + bodyNode = bodySetterCall.getArgument(1) and + contentTypeString = "text/xml" + ) + ) + ) + } + + // Holds for a call that sets the body; the content-type is a parameter. + private predicate holdsBodyAndContentTypeNode( + string package, string receiverName, DataFlow::Node bodyNode, DataFlow::Node contentTypeNode + ) { + exists(string methodName, Method m, DataFlow::CallNode bodySetterCall | + m.hasQualifiedName(package, receiverName, methodName) and + bodySetterCall = m.getACall() + | + package = packagePath() and + ( + // Receiver type: Context + receiverName = "Context" and + ( + // signature: func (*Context).Blob(code int, contentType string, bs []byte) (err error) + methodName = "Blob" and + bodyNode = bodySetterCall.getArgument(2) and + contentTypeNode = bodySetterCall.getArgument(1) + or + // signature: func (*Context).Emit(code int, contentType string, body string) (err error) + methodName = "Emit" and + bodyNode = bodySetterCall.getArgument(2) and + contentTypeNode = bodySetterCall.getArgument(1) + ) + ) + ) + } + + // Holds for a call that sets the content-type (implicit). + private predicate holdsContentTypeString( + string package, string receiverName, DataFlow::CallNode contentTypeSetterCall, + string contentType + ) { + exists(string methodName, Method m | + m.hasQualifiedName(package, receiverName, methodName) and + contentTypeSetterCall = m.getACall() + | + package = packagePath() and + ( + // Receiver type: Context + receiverName = "Context" and + ( + // signature: func (*Context).SetContentTypeHTML() + methodName = "SetContentTypeHTML" and + contentType = "text/html" + or + // signature: func (*Context).SetContentTypeJSON() + methodName = "SetContentTypeJSON" and + contentType = "application/json" + or + // signature: func (*Context).SetContentTypeText() + methodName = "SetContentTypeText" and + contentType = "text/plain" + or + // signature: func (*Context).SetContentTypeXML() + methodName = "SetContentTypeXML" and + contentType = "text/xml" + ) + ) + ) + } + + // Holds for a call that sets the content-type via a parameter. + private predicate holdsContentTypeNode( + string package, string receiverName, DataFlow::CallNode contentTypeSetterCall, + DataFlow::Node contentTypeNode + ) { + exists(string methodName, Method m | + m.hasQualifiedName(package, receiverName, methodName) and + contentTypeSetterCall = m.getACall() + | + package = packagePath() and + ( + // Receiver type: Context + receiverName = "Context" and + ( + // signature: func (*Context).SetContentType(v string) + methodName = "SetContentType" and + contentTypeNode = contentTypeSetterCall.getArgument(0) + ) + ) + ) + } + /** * Models a HTTP header writer model for package: clevergo.tech/clevergo@v0.5.2 */ diff --git a/ql/test/experimental/frameworks/CleverGo/HttpResponseBody.go b/ql/test/experimental/frameworks/CleverGo/HttpResponseBody.go index d3c1b6ea3c0..94d8f8f50cc 100644 --- a/ql/test/experimental/frameworks/CleverGo/HttpResponseBody.go +++ b/ql/test/experimental/frameworks/CleverGo/HttpResponseBody.go @@ -105,7 +105,8 @@ func HttpResponseBody_ClevergoTechClevergov052() { { bodyByte839 := source().([]byte) var rece clevergo.Context - rece.Blob(0, "application/json", bodyByte839) // $contentType=application/json $responseBody=bodyByte839 + ct := "application/json" + rece.Blob(0, ct, bodyByte839) // $contentType=application/json $responseBody=bodyByte839 } // func (*Context).Emit(code int, contentType string, body string) (err error) {