From 3915305361648f378bbaf5d7fe2de6269eecb25c Mon Sep 17 00:00:00 2001 From: Slavomir Date: Tue, 9 Feb 2021 17:18:52 +0100 Subject: [PATCH] Refactor and improve HTTP:ResponseBody models and tests --- ql/src/semmle/go/frameworks/CleverGo.qll | 216 ++++++++++++------ .../CleverGo/HeaderWrite/Model-HeaderWrite.go | 6 +- .../Model-HttpResponseBody.go | 90 +++++++- 3 files changed, 236 insertions(+), 76 deletions(-) diff --git a/ql/src/semmle/go/frameworks/CleverGo.qll b/ql/src/semmle/go/frameworks/CleverGo.qll index 760eddb2932..841ae4cd5c2 100644 --- a/ql/src/semmle/go/frameworks/CleverGo.qll +++ b/ql/src/semmle/go/frameworks/CleverGo.qll @@ -200,84 +200,158 @@ private module CleverGo { */ private class HttpResponseBody extends HTTP::ResponseBody::Range { string package; - DataFlow::CallNode call; + DataFlow::CallNode bodySetterCall; string contentType; HttpResponseBody() { // HTTP ResponseBody models for package: clevergo.tech/clevergo@v0.5.2 package = packagePath() and - // Receiver type: Context ( - // signature: func (*Context).Error(code int, msg string) error - call = any(Method m | m.hasQualifiedName(package, "Context", "Error")).getACall() and - this = call.getArgument(1) and - contentType = "text/plain" + // 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" + ) or - // signature: func (*Context).HTML(code int, html string) error - call = any(Method m | m.hasQualifiedName(package, "Context", "HTML")).getACall() and - this = call.getArgument(1) and - contentType = "text/html" + // 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() + | + // signature: func (*Context).Blob(code int, contentType string, bs []byte) (err error) + methodName = "Blob" and + this = bodySetterCall.getArgument(2) and + contentType = bodySetterCall.getArgument(1).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).getStringValue() + ) or - // signature: func (*Context).HTMLBlob(code int, bs []byte) error - call = any(Method m | m.hasQualifiedName(package, "Context", "HTMLBlob")).getACall() and - this = call.getArgument(1) and - contentType = "text/html" - or - // signature: func (*Context).JSON(code int, data interface{}) error - call = any(Method m | m.hasQualifiedName(package, "Context", "JSON")).getACall() and - this = call.getArgument(1) and - contentType = "application/json" - or - // signature: func (*Context).JSONBlob(code int, bs []byte) error - call = any(Method m | m.hasQualifiedName(package, "Context", "JSONBlob")).getACall() and - this = call.getArgument(1) and - contentType = "application/json" - or - // signature: func (*Context).JSONP(code int, data interface{}) error - call = any(Method m | m.hasQualifiedName(package, "Context", "JSONP")).getACall() and - this = call.getArgument(1) and - contentType = "application/javascript" - or - // signature: func (*Context).JSONPBlob(code int, bs []byte) error - call = any(Method m | m.hasQualifiedName(package, "Context", "JSONPBlob")).getACall() and - this = call.getArgument(1) and - contentType = "application/javascript" - or - // signature: func (*Context).JSONPCallback(code int, callback string, data interface{}) error - call = any(Method m | m.hasQualifiedName(package, "Context", "JSONPCallback")).getACall() and - this = call.getArgument(2) and - contentType = "application/javascript" - or - // signature: func (*Context).JSONPCallbackBlob(code int, callback string, bs []byte) (err error) - call = - any(Method m | m.hasQualifiedName(package, "Context", "JSONPCallbackBlob")).getACall() and - this = call.getArgument(2) and - contentType = "application/javascript" - or - // signature: func (*Context).String(code int, s string) error - call = any(Method m | m.hasQualifiedName(package, "Context", "String")).getACall() and - this = call.getArgument(1) and - contentType = "text/plain" - or - // signature: func (*Context).StringBlob(code int, bs []byte) error - call = any(Method m | m.hasQualifiedName(package, "Context", "StringBlob")).getACall() and - this = call.getArgument(1) and - contentType = "text/plain" - or - // signature: func (*Context).Stringf(code int, format string, a ...interface{}) error - call = any(Method m | m.hasQualifiedName(package, "Context", "Stringf")).getACall() and - this = call.getArgument([1, any(int i | i >= 2)]) and - contentType = "text/plain" - or - // signature: func (*Context).XML(code int, data interface{}) error - call = any(Method m | m.hasQualifiedName(package, "Context", "XML")).getACall() and - this = call.getArgument(1) and - contentType = "text/xml" - or - // signature: func (*Context).XMLBlob(code int, bs []byte) error - call = any(Method m | m.hasQualifiedName(package, "Context", "XMLBlob")).getACall() and - this = call.getArgument(1) and - contentType = "text/xml" + // 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).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" + ) + ) ) } @@ -287,7 +361,7 @@ private module CleverGo { } /** - * Models HTTP header writers model for package: clevergo.tech/clevergo@v0.5.2 + * Models a HTTP header writer model for package: clevergo.tech/clevergo@v0.5.2 */ private class HeaderWrite extends HTTP::HeaderWrite::Range, DataFlow::CallNode { HeaderWrite() { diff --git a/ql/test/library-tests/semmle/go/frameworks/CleverGo/HeaderWrite/Model-HeaderWrite.go b/ql/test/library-tests/semmle/go/frameworks/CleverGo/HeaderWrite/Model-HeaderWrite.go index 88e9a2a4289..5bcad719eb0 100644 --- a/ql/test/library-tests/semmle/go/frameworks/CleverGo/HeaderWrite/Model-HeaderWrite.go +++ b/ql/test/library-tests/semmle/go/frameworks/CleverGo/HeaderWrite/Model-HeaderWrite.go @@ -18,10 +18,10 @@ func ClevergoTechClevergov052() { { // func (*Context).SetHeader(key string, value string) { - keyString839 := source().(string) - valString273 := source().(string) + keyString566 := source().(string) + valString497 := source().(string) var rece clevergo.Context - rece.SetHeader(keyString839, valString273) // $headerKey=keyString839 $headerVal=valString273 + rece.SetHeader(keyString566, valString497) // $headerKey=keyString566 $headerVal=valString497 } } } diff --git a/ql/test/library-tests/semmle/go/frameworks/CleverGo/HttpResponseBody/Model-HttpResponseBody.go b/ql/test/library-tests/semmle/go/frameworks/CleverGo/HttpResponseBody/Model-HttpResponseBody.go index 05ffba487e0..8967560c93f 100644 --- a/ql/test/library-tests/semmle/go/frameworks/CleverGo/HttpResponseBody/Model-HttpResponseBody.go +++ b/ql/test/library-tests/semmle/go/frameworks/CleverGo/HttpResponseBody/Model-HttpResponseBody.go @@ -12,9 +12,9 @@ func source() interface{} { // Package clevergo.tech/clevergo@v0.5.2 func ClevergoTechClevergov052() { - // Set ResponseBody via method calls. + // Response body is set via a method call (the content-type is implicit in the method name). { - // Set ResponseBody via method calls on clevergo.tech/clevergo.Context. + // Response body is set via a method call on the clevergo.tech/clevergo.Context type (the content-type is implicit in the method name). { // func (*Context).Error(code int, msg string) error { @@ -103,4 +103,90 @@ func ClevergoTechClevergov052() { } } } + // Response body and content-type are both set via a single call of a method. + { + // Response body and content-type are both set via a single call of a method on the clevergo.tech/clevergo.Context type. + { + // func (*Context).Blob(code int, contentType string, bs []byte) (err error) + { + bodyByte839 := source().([]byte) + var rece clevergo.Context + rece.Blob(0, "application/json", bodyByte839) // $contentType=application/json $responseBody=bodyByte839 + } + // func (*Context).Emit(code int, contentType string, body string) (err error) + { + bodyString273 := source().(string) + var rece clevergo.Context + rece.Emit(0, "application/json", bodyString273) // $contentType=application/json $responseBody=bodyString273 + } + } + } + // Response body and content-type are set via calls of different methods. + { + // Response body and content-type are set via calls of different methods on the clevergo.tech/clevergo.Context type. + { + // func (*Context).Write(data []byte) (int, error) + { + bodyByte982 := source().([]byte) + var rece clevergo.Context + rece.SetContentType("application/json") + rece.Write(bodyByte982) // $contentType=application/json $responseBody=bodyByte982 + } + { + bodyByte458 := source().([]byte) + var rece clevergo.Context + rece.SetContentTypeHTML() + rece.Write(bodyByte458) // $contentType=text/html $responseBody=bodyByte458 + } + { + bodyByte506 := source().([]byte) + var rece clevergo.Context + rece.SetContentTypeJSON() + rece.Write(bodyByte506) // $contentType=application/json $responseBody=bodyByte506 + } + { + bodyByte213 := source().([]byte) + var rece clevergo.Context + rece.SetContentTypeText() + rece.Write(bodyByte213) // $contentType=text/plain $responseBody=bodyByte213 + } + { + bodyByte468 := source().([]byte) + var rece clevergo.Context + rece.SetContentTypeXML() + rece.Write(bodyByte468) // $contentType=text/xml $responseBody=bodyByte468 + } + // func (*Context).WriteString(data string) (int, error) + { + bodyString219 := source().(string) + var rece clevergo.Context + rece.SetContentType("application/json") + rece.WriteString(bodyString219) // $contentType=application/json $responseBody=bodyString219 + } + { + bodyString265 := source().(string) + var rece clevergo.Context + rece.SetContentTypeHTML() + rece.WriteString(bodyString265) // $contentType=text/html $responseBody=bodyString265 + } + { + bodyString971 := source().(string) + var rece clevergo.Context + rece.SetContentTypeJSON() + rece.WriteString(bodyString971) // $contentType=application/json $responseBody=bodyString971 + } + { + bodyString320 := source().(string) + var rece clevergo.Context + rece.SetContentTypeText() + rece.WriteString(bodyString320) // $contentType=text/plain $responseBody=bodyString320 + } + { + bodyString545 := source().(string) + var rece clevergo.Context + rece.SetContentTypeXML() + rece.WriteString(bodyString545) // $contentType=text/xml $responseBody=bodyString545 + } + } + } }