|
| 1 | +/** |
| 2 | + * Provides classes for working with untrusted flow sources, taint propagators, and HTTP sinks |
| 3 | + * from the `github.com/labstack/echo` package. |
| 4 | + */ |
| 5 | + |
| 6 | +import go |
| 7 | + |
| 8 | +private module Echo { |
| 9 | + /** Gets an Echo package name. */ |
| 10 | + bindingset[result] |
| 11 | + private string packagePath() { result = package("github.com/labstack/echo", "") } |
| 12 | + |
| 13 | + /** |
| 14 | + * Data from a `Context` interface method, considered as a source of untrusted flow. |
| 15 | + */ |
| 16 | + private class EchoContextSource extends UntrustedFlowSource::Range { |
| 17 | + EchoContextSource() { |
| 18 | + exists(DataFlow::MethodCallNode call, string methodName | |
| 19 | + methodName = |
| 20 | + ["Param", "ParamValues", "QueryParam", "QueryParams", "QueryString", "FormValue", |
| 21 | + "FormParams", "FormFile", "MultipartForm", "Cookie", "Cookies"] and |
| 22 | + call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and |
| 23 | + this = call.getResult(0) |
| 24 | + ) |
| 25 | + } |
| 26 | + } |
| 27 | + |
| 28 | + /** |
| 29 | + * Data from a `Context` interface method that is not generally exploitable for open-redirect attacks. |
| 30 | + */ |
| 31 | + private class EchoContextRedirectUnexploitableSource extends HTTP::Redirect::UnexploitableSource { |
| 32 | + EchoContextRedirectUnexploitableSource() { |
| 33 | + exists(DataFlow::MethodCallNode call, string methodName | |
| 34 | + methodName = ["FormValue", "FormParams", "FormFile", "MultipartForm", "Cookie", "Cookies"] and |
| 35 | + call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and |
| 36 | + this = call.getResult(0) |
| 37 | + ) |
| 38 | + } |
| 39 | + } |
| 40 | + |
| 41 | + /** |
| 42 | + * Models of `Context.Get/Set`. `Context` behaves like a map, with corresponding taint propagation. |
| 43 | + */ |
| 44 | + private class ContextMapModels extends TaintTracking::FunctionModel, Method { |
| 45 | + string methodName; |
| 46 | + FunctionInput input; |
| 47 | + FunctionOutput output; |
| 48 | + |
| 49 | + ContextMapModels() { |
| 50 | + ( |
| 51 | + methodName = "Get" and input.isReceiver() and output.isResult() |
| 52 | + or |
| 53 | + methodName = "Set" and input.isParameter(1) and output.isReceiver() |
| 54 | + ) and |
| 55 | + this.hasQualifiedName(packagePath(), "Context", methodName) |
| 56 | + } |
| 57 | + |
| 58 | + override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) { |
| 59 | + inp = input and outp = output |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + /** |
| 64 | + * A call to a method on `Context` struct that unmarshals data into a target. |
| 65 | + */ |
| 66 | + private class EchoContextBinder extends UntrustedFlowSource::Range { |
| 67 | + EchoContextBinder() { |
| 68 | + exists(DataFlow::MethodCallNode call | |
| 69 | + call.getTarget().hasQualifiedName(packagePath(), "Context", "Bind") |
| 70 | + | |
| 71 | + this = FunctionOutput::parameter(0).getExitNode(call) |
| 72 | + ) |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + /** |
| 77 | + * `echo.Context` methods which set the content-type to `text/html` and write a result in one operation. |
| 78 | + */ |
| 79 | + private class EchoHtmlOutputs extends HTTP::ResponseBody::Range { |
| 80 | + EchoHtmlOutputs() { |
| 81 | + exists(Method m | m.hasQualifiedName(packagePath(), "Context", ["HTML", "HTMLBlob"]) | |
| 82 | + this = m.getACall().getArgument(1) |
| 83 | + ) |
| 84 | + } |
| 85 | + |
| 86 | + override HTTP::ResponseWriter getResponseWriter() { none() } |
| 87 | + |
| 88 | + override string getAContentType() { result = "text/html" } |
| 89 | + } |
| 90 | + |
| 91 | + /** |
| 92 | + * `echo.Context` methods which take a content-type as a parameter. |
| 93 | + */ |
| 94 | + private class EchoParameterizedOutputs extends HTTP::ResponseBody::Range { |
| 95 | + DataFlow::CallNode callNode; |
| 96 | + |
| 97 | + EchoParameterizedOutputs() { |
| 98 | + exists(Method m | m.hasQualifiedName(packagePath(), "Context", ["Blob", "Stream"]) | |
| 99 | + callNode = m.getACall() and this = callNode.getArgument(2) |
| 100 | + ) |
| 101 | + } |
| 102 | + |
| 103 | + override HTTP::ResponseWriter getResponseWriter() { none() } |
| 104 | + |
| 105 | + override DataFlow::Node getAContentTypeNode() { result = callNode.getArgument(1) } |
| 106 | + } |
| 107 | + |
| 108 | + /** |
| 109 | + * The `echo.Context.Redirect` method. |
| 110 | + */ |
| 111 | + private class EchoRedirectMethod extends HTTP::Redirect::Range, DataFlow::CallNode { |
| 112 | + EchoRedirectMethod() { |
| 113 | + exists(Method m | m.hasQualifiedName(packagePath(), "Context", "Redirect") | |
| 114 | + this = m.getACall() |
| 115 | + ) |
| 116 | + } |
| 117 | + |
| 118 | + override DataFlow::Node getUrl() { result = this.getArgument(1) } |
| 119 | + |
| 120 | + override HTTP::ResponseWriter getResponseWriter() { none() } |
| 121 | + } |
| 122 | +} |
0 commit comments