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

Add models for Revel and HTML templates #473

Merged
merged 11 commits into from
Mar 19, 2021
Merged

Add models for Revel and HTML templates #473

merged 11 commits into from
Mar 19, 2021

Conversation

sauyon
Copy link
Contributor

@sauyon sauyon commented Feb 10, 2021

The revel stubs are very hand-written, so I've excluded the go generate commands.

I tried to update depstubber to do the sensible thing, but I'm still running into the limits of the reflection approach. One of these days...

  • dist-compare
  • LGTM eval for performance

@sauyon sauyon requested a review from a team as a code owner February 10, 2021 22:18
@smowton smowton changed the base branch from sauyon/html-tracing to main February 11, 2021 13:36
@smowton
Copy link
Contributor

smowton commented Feb 11, 2021

Also retargeted this one at main. Reviewers note that the first commit is #472, only review the later 3 commits here.

* TODO: For review: this name conflicts heavily with `getBaseVariable` above, with different and probably confusing results. Perhaps rename above to `getRootVariable`? Suggestions welcome.
* I do like using `getBase` here because it lines up nicely with the `getBase` on selector exprs and write targets.
*/
VariableWithFields getBase() {
Copy link
Contributor

Choose a reason for hiding this comment

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

How about simply getParent?

* corresponding to `a.b[c]`.
*/
Variable getBaseVariable() {
this = TVariableRoot(result)
Copy link
Contributor

Choose a reason for hiding this comment

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

getParent()*.(VariableRoot)?

* I do like using `getBase` here because it lines up nicely with the `getBase` on selector exprs and write targets.
*/
VariableWithFields getBase() {
exists(VariableWithFields base, Field f | this = TVariableFieldStep(base, f) | result = base)
Copy link
Contributor

Choose a reason for hiding this comment

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

this = TVariableFieldStep(result, _)?

MethodModels() {
// signature: func (*Template).Execute(wr io.Writer, data interface{}) error
hasQualifiedName("html/template", "Template", "Execute") and
(inp.isParameter(1) and outp.isParameter(0))
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for parens

or
// signature: func (*Template).ExecuteTemplate(wr io.Writer, name string, data interface{}) error
hasQualifiedName("html/template", "Template", "ExecuteTemplate") and
(inp.isParameter(2) and outp.isParameter(0))
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for parens

RawTemplateArgument() {
exists(TemplateRender render, RawTemplateRead read, VariableWithFields var |
render.getRenderedFile() = read.getFile() and
this = var.getBase*().getAWrite().getRhs()
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's have some comments showing what sort of expressions are represented here -- looks like var is somevar.somefield.someotherfield and this is being bound to rhs in somevar = rhs or somevar.somefield = rhs or somevar.somefield.someotherfield = rhs?

render.getRenderedFile() = read.getFile() and
this = var.getBase*().getAWrite().getRhs()
|
var.getBase*() = render.getArgumentVariable() and
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be the same getBase* as above? If so, bind it and give it a name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, this is just ensuring that the render argument is a base of the result we give, because otherwise we find all variables that have the same name.

e.g.

blah.Render(arg)

var arg
arg.field = bar // we don't want to flag this

var = read.getReadVariable(render.getArgumentVariable())
or
// if no write or use of that variable exists, no VariableWithFields will be generated
// so we try to find a parent VariableWithFields
Copy link
Contributor

Choose a reason for hiding this comment

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

Elaborate on when this can happen considering all the getBase stuff above?

// if no write or use of that variable exists, no VariableWithFields will be generated
// so we try to find a parent VariableWithFields
not exists(read.getReadVariable(render.getArgumentVariable())) and
exists(string fieldName | fieldName = read.getFieldName() |
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this only go one level back (i.e. one getBase), or can it go further?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can go further. I wrote this assuming any write to a parent field should count. We could probably bound it to 1 or 2 and still have pretty similar results with possibly fewer FPs, if you want to do that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's see how the results look

exists(string controllerRe, string handlerRe, string pathRe |
controllerRe = "\\Q" + this.getReceiver().getType().getName() + "\\E" and
handlerRe = "\\Q" + this.getEnclosingCallable().getName() + "\\E" and
pathRe = "/views/" + controllerRe + "/" + handlerRe + "(\\..*)?\\.html?"
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment on what the \\..* part of the regex is doing? Perhaps give an example of the form of the view html filename?

@smowton
Copy link
Contributor

smowton commented Feb 11, 2021

Similary let's have an eval ticklist here; perhaps only here, or perhaps on both PRs if they have independent eval plans

@sauyon sauyon changed the base branch from main to sauyon/html-tracing February 11, 2021 18:35
@sauyon sauyon force-pushed the sauyon/html-tracing branch 2 times, most recently from b3c08d5 to 715bbab Compare February 11, 2021 18:40
@sauyon sauyon force-pushed the revel branch 2 times, most recently from 1e5bd7b to 24724bd Compare February 11, 2021 18:55
@sauyon sauyon changed the base branch from sauyon/html-tracing to main February 11, 2021 18:55
@sauyon sauyon force-pushed the revel branch 3 times, most recently from 1894f49 to 781f3ab Compare February 18, 2021 09:13
@sauyon sauyon force-pushed the revel branch 3 times, most recently from 69d6681 to f2a10c2 Compare March 5, 2021 12:56
@sauyon
Copy link
Contributor Author

sauyon commented Mar 7, 2021

Seems like it'll be more effort than this to port over SSAWithFields, so I'm going to revert that commit and store it in another branch for later.

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.

Looks good! Two minor requests:

  1. Add in the doc comments for SsaWithField and VariableWithField when one would want to use SsaWithField.similar() and when VariableWithField is better. What distinguishes the two?
  2. In cases where we're highlighting an XSS sink due to an ultimate sink actually in an HTML file, can we expose that somehow? I imagine path steps into the HTML document are too ambitious, but could we mention "template argument $@ is output raw here"?

@sauyon
Copy link
Contributor Author

sauyon commented Mar 10, 2021

  1. Add in the doc comments for SsaWithField and VariableWithField when one would want to use SsaWithField.similar() and when VariableWithField is better. What distinguishes the two?

Sure. similar only exists because we didn't have a VariableWithField before; I'm going to deprecate the predicate and note that in the qldoc.

  1. In cases where we're highlighting an XSS sink due to an ultimate sink actually in an HTML file, can we expose that somehow? I imagine path steps into the HTML document are too ambitious, but could we mention "template argument $@ is output raw here"?

Can a query have optional arguments?

@sauyon sauyon force-pushed the revel branch 4 times, most recently from 898dfd1 to 305221b Compare March 16, 2021 14:09
@sauyon sauyon force-pushed the revel branch 4 times, most recently from 423355f to de7ceaf Compare March 18, 2021 00:33
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.

I think we shouldn't be committing unexpected frontend errors? Otherwise looks great!

@@ -0,0 +1,2 @@
| Revel.go:7:2:7:2 | "mime/multipart" imported but not used |
Copy link
Contributor

Choose a reason for hiding this comment

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

These seem bad

@sauyon
Copy link
Contributor Author

sauyon commented Mar 18, 2021

Whoops, that should be resolved now.

@sauyon
Copy link
Contributor Author

sauyon commented Mar 18, 2021

If we merge this all at once we should close #472, probably.

@sauyon sauyon merged commit 104f581 into github:main Mar 19, 2021
@sauyon sauyon deleted the revel branch March 19, 2021 01:21
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants