Skip to content
This repository has been archived by the owner on Jan 5, 2023. It is now read-only.

Pilot #0: Add web framework clevergo #438

Merged
merged 28 commits into from
Apr 9, 2021
Merged

Conversation

gagliardetto
Copy link
Contributor

@gagliardetto gagliardetto commented Dec 20, 2020

This is a "pilot" batch (of 1 element) for a project targeting web frameworks.

Let me know your thoughts.

@gagliardetto
Copy link
Contributor Author

Right now the tool can handle the UntrustedFlowSource and TaintTracking models.

Given that I don't want to open more than one PR (ever) for any given web framework, I want to be sure I get the most important models right from the start. It's like going to the grocery store; I want to get everything the first time I go there.

This is the list of models I want to add:

  1. HTTP::Redirect::Range
  2. HTTP::ResponseBody::Range

Those two seem like the most impactful. Anything else?

@gagliardetto
Copy link
Contributor Author

Maybe also:

  1. HTTP::Redirect::UnexploitableSource
  2. TaintTracking::DefaultTaintSanitizer

I'm aiming at having the most impact, rather than completeness. The most bang for the buck.

@smowton
Copy link
Contributor

smowton commented Dec 21, 2020

Most of the Go team is out for the holidays now, so I'll look on the 4th January or later

@gagliardetto
Copy link
Contributor Author

Sounds great! Thanks! Happy holidays

@smowton
Copy link
Contributor

smowton commented Dec 21, 2020

You too :)

Copy link
Contributor

@sauyon sauyon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again, I should really try to get the tool working so I can use it myself.

I haven't actually looked at the models themselves, but a few comments on the generator.

ql/src/semmle/go/frameworks/CleverGo.qll Outdated Show resolved Hide resolved
ql/src/semmle/go/frameworks/CleverGo.qll Outdated Show resolved Hide resolved
ql/src/semmle/go/frameworks/CleverGo.qll Outdated Show resolved Hide resolved
ql/src/semmle/go/frameworks/CleverGo.qll Outdated Show resolved Hide resolved
ql/src/semmle/go/frameworks/CleverGo.qll Outdated Show resolved Hide resolved
ql/src/semmle/go/frameworks/CleverGo.qll Outdated Show resolved Hide resolved
ql/src/semmle/go/frameworks/CleverGo.qll Outdated Show resolved Hide resolved
@gagliardetto
Copy link
Contributor Author

Thanks again, I should really try to get the tool working so I can use it myself.

Thanks for the review! This is actually not codebox but another tool I recently wrote from (almost) scratch. There are a few things I want to do with it before I opensource it.

Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can see some of the other common ::Range classes used in the newly-added Beego models (https://github.com/github/codeql-go/blob/main/ql/src/semmle/go/frameworks/Beego.qll)

Particularly HeaderWrite and ResponseWriter, which are used to pair up a response body with its content-type. Note that there are methods that set both simultaneously (e.g. response.JSON(xyz)), those that explicitly set content-type (response.SetContentType("text/html"); response.Write(xyz)) and those that set a generic HTTP header which could be Content-Type (response.SetHeader("Content-Type", "text/html"); response.Write(xyz))

ql/src/semmle/go/frameworks/CleverGo.qll Outdated Show resolved Hide resolved
ql/src/semmle/go/frameworks/CleverGo.qll Outdated Show resolved Hide resolved
@gagliardetto
Copy link
Contributor Author

Next I'm going to add HTTP::Redirect.

I'm thinking it could look like this:

  private class RedirectMethods2 extends HTTP::Redirect::Range, DataFlow::CallNode {
    string package;
    DataFlow::Node urlNode;

    RedirectMethods2() {
      package = packagePath() and
      (
        // Function call:
        this.getTarget().hasQualifiedName(package, "DoRedirect") and
        urlNode = this.getArgument(2)
        or
        // Method on type call:
        this = any(Method m | m.hasQualifiedName(package, "SomeReceiver", "Redirect")).getACall() and
        urlNode = this.getArgument(1)
        or
        // Method on interface call:
        this = any(Method m | m.implements(package, "SomeInterface", "Redirect")).getACall() and
        urlNode = this.getArgument(1)
      )
    }

    override DataFlow::Node getUrl() { result = urlNode }

    //    override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() }
    override HTTP::ResponseWriter getResponseWriter() { none() }
  }

Questions:

  1. What is getResponseWriter() exactly for?
  2. Is having getResponseWriter() impactful?

Thanks!

@smowton
Copy link
Contributor

smowton commented Jan 25, 2021

For header writes, getResponseWriter is for pairing up a header-set operation with the corresponding response body, which the XSS query uses to exclude bodies accompanied by a non-HTML content-type. For HTTP::Redirect it doesn't appear to be used at the moment, so can be safely set to none() and perhaps ought to be removed.

@gagliardetto
Copy link
Contributor Author

Thanks @smowton

And how does the proposed model look to you?

@smowton
Copy link
Contributor

smowton commented Jan 25, 2021

Looks good to me -- @sauyon you have any further comments?

@gagliardetto
Copy link
Contributor Author

gagliardetto commented Jan 25, 2021

Added the modelling for HTTP redirects.

Not sure about the correctness of the inline tests query; does https://github.com/github/codeql-go/blob/9d6ac53acff98b33aacc1c7824670518e83eb4f7/ql/test/library-tests/semmle/go/frameworks/CleverGo/HttpRedirect/Test.ql actually catch what it is supposed to catch? Do they make sense?

@gagliardetto
Copy link
Contributor Author

gagliardetto commented Jan 25, 2021

Once the HTTP::Redirect model is approved, I'll move on to adding another kind of model (haven't decided which yet).

Once a number of model kinds have been implemented for this web framework, I'll tidy up this PR to be merged (things like removing duplicate vendoring, etc.).

@gagliardetto
Copy link
Contributor Author

Next up: HTTP::ResponseBody

  private class ResponseBody extends HTTP::ResponseBody::Range {
    string package;
    DataFlow::CallNode call;
    string contentType;

    ResponseBody() {
      package = packagePath() and
      (
        // Function call:
        call.getTarget().hasQualifiedName(package, "JSON") and
        this = call.getArgument(1) and
        contentType = "application/json"
        or
        // Method on type call:
        call = any(Method m | m.hasQualifiedName(package, "SomeReceiver", "JSON")).getACall() and
        this = call.getArgument(0) and
        contentType = "application/json"
        or
        // Method on interface call:
        call = any(Method m | m.implements(package, "SomeInterface", "JSON")).getACall() and
        this = call.getArgument(0) and
        contentType = "application/json"
      )
    }

    override string getAContentType() { result = contentType }

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

Questions:

  1. What is getResponseWriter() exactly for?
  2. Is having getResponseWriter() impactful?

@sauyon
Copy link
Contributor

sauyon commented Jan 27, 2021

1. What is `getResponseWriter()` exactly for?

For linking response writes with other response writes and headers. If the model has another way of achieving this, omitting it is fine.

@sauyon
Copy link
Contributor

sauyon commented Jan 27, 2021

Not sure about the correctness of the inline tests query; does https://github.com/github/codeql-go/blob/9d6ac53acff98b33aacc1c7824670518e83eb4f7/ql/test/library-tests/semmle/go/frameworks/CleverGo/HttpRedirect/Test.ql actually catch what it is supposed to catch? Do they make sense?

The inline test query makes sense to me; was there a problem with it?

@gagliardetto
Copy link
Contributor Author

Not sure about the correctness of the inline tests query; does https://github.com/github/codeql-go/blob/9d6ac53acff98b33aacc1c7824670518e83eb4f7/ql/test/library-tests/semmle/go/frameworks/CleverGo/HttpRedirect/Test.ql actually catch what it is supposed to catch? Do they make sense?

The inline test query makes sense to me; was there a problem with it?

Nope. Just wanted to makes sure it made sense.

@gagliardetto
Copy link
Contributor Author

Added HTTP::ResponseBody modelling.

Next will think about HTTP::ResponseWriter. Still not sure about how it works and relates to the getResponseWriter() methods.

@gagliardetto
Copy link
Contributor Author

Got a few more scenarios of ResponseBody without using HTTP::ResponseWriter (I hope that's OK).

@gagliardetto

This comment has been minimized.

@gagliardetto gagliardetto marked this pull request as draft February 8, 2021 20:30
@gagliardetto
Copy link
Contributor Author

gagliardetto commented Feb 9, 2021

I added more HTTP::ResponseBody models and tests which consider all possible combinations.

TODO:

  • To find whether methods are called on the same receiver, I use: contentTypeSetterCall.getReceiver().getAPredecessor*() = bodySetterCall.getReceiver().getAPredecessor*(); is that correct and a good way to do it?
  • How would you implement override HTTP::ResponseWriter getResponseWriter()?
  • Add tests for the case where the contentType is set via a HeaderWrite. DONE
  • Remove duplicate vendoring. DONE

@gagliardetto gagliardetto marked this pull request as ready for review February 9, 2021 16:26
@gagliardetto
Copy link
Contributor Author

gagliardetto commented Feb 9, 2021

One thing to note is that the Content-Type header value might look something like text/html; charset=UTF-8, and to match that you'd need some regex.


A second thing to note is that this code snippet from the tests does not work (using a variable for the contentType string):

			// func (*Context).Blob(code int, contentType string, bs []byte) (err error)
			{
				bodyByte839 := source().([]byte)
				contentTypeString458 := "application/json"
				var rece clevergo.Context
				rece.Blob(0, contentTypeString458, bodyByte839) // $contentType=application/json $responseBody=bodyByte839
			}

I had to change it to this (the content-type is a string passed directly as parameter in the call):

			// 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
			}

Maybe there's something wrong in the codeql models?

@smowton smowton force-pushed the clevergo branch 2 times, most recently from 4282bd4 to 8e839f3 Compare April 9, 2021 07:39
@smowton
Copy link
Contributor

smowton commented Apr 9, 2021

Rebased and merging on green

@smowton smowton enabled auto-merge April 9, 2021 07:40
@smowton smowton merged commit 46b5f11 into github:main Apr 9, 2021
@gagliardetto
Copy link
Contributor Author

gagliardetto commented Apr 9, 2021 via email

@gagliardetto
Copy link
Contributor Author

gagliardetto commented Apr 9, 2021 via email

@gagliardetto
Copy link
Contributor Author

For future reference, here is the codemill spec file for clevergo:

CleverGo.json:

{
    "Name": "CleverGo",
    "Models": [
    {
        "Name": "UntrustedSources",
        "Kind": "UntrustedFlowSource",
        "Methods": [
        {
            "Name": "Self",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-BasicAuth",
                    "Pos": [false, true, true, false],
                    "Flows": null,
                    "Name": "BasicAuth"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Decode",
                    "Pos": [false, true, false],
                    "Flows": null,
                    "Name": "Decode"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-DefaultQuery",
                    "Pos": [false, false, false, true],
                    "Flows": null,
                    "Name": "DefaultQuery"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-FormValue",
                    "Pos": [false, false, true],
                    "Flows": null,
                    "Name": "FormValue"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-GetHeader",
                    "Pos": [false, false, true],
                    "Flows": null,
                    "Name": "GetHeader"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-PostFormValue",
                    "Pos": [false, false, true],
                    "Flows": null,
                    "Name": "PostFormValue"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-QueryParam",
                    "Pos": [false, false, true],
                    "Flows": null,
                    "Name": "QueryParam"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-QueryString",
                    "Pos": [false, true],
                    "Flows": null,
                    "Name": "QueryString"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Params-String",
                    "Pos": [false, false, true],
                    "Flows": null,
                    "Name": "String"
                }
            },
            {
                "Kind": "Struct",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "Struct-Context",
                    "TypeName": "Context",
                    "Fields":
                    {
                        "Params": null
                    }
                }
            },
            {
                "Kind": "Struct",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "Struct-Param",
                    "TypeName": "Param",
                    "Fields":
                    {
                        "Key": null,
                        "Value": null
                    }
                }
            },
            {
                "Kind": "Type",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "Type-Params",
                    "TypeName": "Params",
                    "Value": true
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "InterfaceMethod-Decoder-Decode",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "Decode"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-QueryParams",
                    "Pos": [false, true],
                    "Flows": null,
                    "Name": "QueryParams"
                }
            }]
        }]
    },
    {
        "Name": "TaintTracking",
        "Kind": "TaintTracking",
        "Methods": [
        {
            "Name": "Self",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "Function-CleanPath",
                    "Pos": null,
                    "Flows":
                    {
                        "Blocks": [
                        {
                            "Inp": [true, false],
                            "Out": [false, true]
                        }],
                        "Enabled": true
                    },
                    "Name": "CleanPath"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "InterfaceMethod-Decoder-Decode",
                    "Pos": null,
                    "Flows":
                    {
                        "Blocks": [
                        {
                            "Inp": [false, true, false, false],
                            "Out": [false, false, true, false]
                        }],
                        "Enabled": true
                    },
                    "Name": "Decode"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "InterfaceMethod-Renderer-Render",
                    "Pos": null,
                    "Flows":
                    {
                        "Blocks": [
                        {
                            "Inp": [false, false, false, true, false, false],
                            "Out": [false, true, false, false, false, false]
                        }],
                        "Enabled": true
                    },
                    "Name": "Render"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Application-RouteURL",
                    "Pos": null,
                    "Flows":
                    {
                        "Blocks": [
                        {
                            "Inp": [false, true, true, false, false],
                            "Out": [false, false, false, true, false]
                        }],
                        "Enabled": true
                    },
                    "Name": "RouteURL"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Context",
                    "Pos": null,
                    "Flows":
                    {
                        "Blocks": [
                        {
                            "Inp": [true, false],
                            "Out": [false, true]
                        }],
                        "Enabled": true
                    },
                    "Name": "Context"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Params-String",
                    "Pos": null,
                    "Flows":
                    {
                        "Blocks": [
                        {
                            "Inp": [true, false, false],
                            "Out": [false, false, true]
                        }],
                        "Enabled": true
                    },
                    "Name": "String"
                }
            }]
        }]
    },
    {
        "Name": "HttpRedirect",
        "Kind": "HTTP::Redirect",
        "Methods": [
        {
            "Name": "GetUrl",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Redirect",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "Redirect"
                }
            }]
        }]
    },
    {
        "Name": "HttpResponseBody",
        "Kind": "HTTP::ResponseBody",
        "Methods": [
        {
            "Name": "(body+ctFromFuncName):body",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Error",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "Error"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-HTML",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "HTML"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-HTMLBlob",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "HTMLBlob"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-JSON",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "JSON"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-JSONBlob",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "JSONBlob"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-JSONP",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "JSONP"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-JSONPBlob",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "JSONPBlob"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-JSONPCallback",
                    "Pos": [false, false, false, true, false],
                    "Flows": null,
                    "Name": "JSONPCallback"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-JSONPCallbackBlob",
                    "Pos": [false, false, false, true, false],
                    "Flows": null,
                    "Name": "JSONPCallbackBlob"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-String",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "String"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-StringBlob",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "StringBlob"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Stringf",
                    "Pos": [false, false, true, true, false],
                    "Flows": null,
                    "Name": "Stringf"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-XML",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "XML"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-XMLBlob",
                    "Pos": [false, false, true, false],
                    "Flows": null,
                    "Name": "XMLBlob"
                }
            }]
        },
        {
            "Name": "(body+ct):body",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Blob",
                    "Pos": [false, false, false, true, false],
                    "Flows": null,
                    "Name": "Blob"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Emit",
                    "Pos": [false, false, false, true, false],
                    "Flows": null,
                    "Name": "Emit"
                }
            }]
        },
        {
            "Name": "(body+ct):ct",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Blob",
                    "Pos": [false, false, true, false, false],
                    "Flows": null,
                    "Name": "Blob"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Emit",
                    "Pos": [false, false, true, false, false],
                    "Flows": null,
                    "Name": "Emit"
                }
            }]
        },
        {
            "Name": ":body",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-Write",
                    "Pos": [false, true, false, false],
                    "Flows": null,
                    "Name": "Write"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-WriteString",
                    "Pos": [false, true, false, false],
                    "Flows": null,
                    "Name": "WriteString"
                }
            }]
        },
        {
            "Name": ":ct",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-SetContentType",
                    "Pos": [false, true],
                    "Flows": null,
                    "Name": "SetContentType"
                }
            }]
        },
        {
            "Name": ":ctFromFuncName",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-SetContentTypeHTML",
                    "Pos": [true],
                    "Flows": null,
                    "Name": "SetContentTypeHTML"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-SetContentTypeJSON",
                    "Pos": [true],
                    "Flows": null,
                    "Name": "SetContentTypeJSON"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-SetContentTypeText",
                    "Pos": [true],
                    "Flows": null,
                    "Name": "SetContentTypeText"
                }
            },
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-SetContentTypeXML",
                    "Pos": [true],
                    "Flows": null,
                    "Name": "SetContentTypeXML"
                }
            }]
        }]
    },
    {
        "Name": "HeaderWrite",
        "Kind": "HTTP::HeaderWrite",
        "Methods": [
        {
            "Name": "WriteKey",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-SetHeader",
                    "Pos": [false, true, false],
                    "Flows": null,
                    "Name": "SetHeader"
                }
            }]
        },
        {
            "Name": "WriteVal",
            "Selectors": [
            {
                "Kind": "Func",
                "Qualifier":
                {
                    "Path": "clevergo.tech/clevergo",
                    "Version": "v0.5.2",
                    "ID": "TypeMethod-Context-SetHeader",
                    "Pos": [false, false, true],
                    "Flows": null,
                    "Name": "SetHeader"
                }
            }]
        }]
    }]
}

@sauyon
Copy link
Contributor

sauyon commented Apr 12, 2021

One thing to note is that the Content-Type header value might look something like text/html; charset=UTF-8, and to match that you'd need some regex.

We just use .*html.*.

private predicate htmlTypeSpecified(HTTP::ResponseBody body) {
body.getAContentType().regexpMatch("(?i).*html.*")
}

  • To find whether methods are called on the same receiver, I use: contentTypeSetterCall.getReceiver().getAPredecessor*() = bodySetterCall.getReceiver().getAPredecessor*(); is that correct and a good way to do it?

That's fine. Elsewhere we use VariableWithFields as well as .getAPredecessor*, but I'm not sure it really adds much.

  • How would you implement override HTTP::ResponseWriter getResponseWriter()?

As this is only currently used in a not-very-nice way to link response writes and header writes together, possibly not at all. Perhaps a better interface here could be something like .getAHeaderWrite and / or .getAnotherWrite or something along those lines.

  • Add tests for the case where the contentType is set via a HeaderWrite.

No comments here.

  • Remove duplicate vendoring.

At some point I'd like to do this, but it's very much not a priority.

@gagliardetto
Copy link
Contributor Author

One thing to note is that the Content-Type header value might look something like text/html; charset=UTF-8, and to match that you'd need some regex.

We just use .*html.*.

Makes sense!

  • To find whether methods are called on the same receiver, I use: contentTypeSetterCall.getReceiver().getAPredecessor*() = bodySetterCall.getReceiver().getAPredecessor*(); is that correct and a good way to do it?

That's fine. Elsewhere we use VariableWithFields as well as .getAPredecessor*, but I'm not sure it really adds much.

👍

  • How would you implement override HTTP::ResponseWriter getResponseWriter()?

As this is only currently used in a not-very-nice way to link response writes and header writes together, possibly not at all. Perhaps a better interface here could be something like .getAHeaderWrite and / or .getAnotherWrite or something along those lines.

I'll continue to leave override HTTP::ResponseWriter getResponseWriter() out of my models.

  • Add tests for the case where the contentType is set via a HeaderWrite.

No comments here.

Forgot to cross that out. (Done)

  • Remove duplicate vendoring.

At some point I'd like to do this, but it's very much not a priority.

Forgot to cross that out. (Done) (Was referring to the vendor duplication in clevergo tests)

Thanks @sauyon !

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants