-
Notifications
You must be signed in to change notification settings - Fork 1.8k
JS: Add XSS-through-dom query #3191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
exists(DataFlow::PropRead read | read = this.getCalleeNode() | | ||
read.getBase().getALocalSource() = [dollar(), objectRef()] and | ||
read.getPropertyNameExpr().flow().mayHaveStringValue(name) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we merge this with the above case? It seems to almost subsume it with the only caveat about this only recognizing method calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the current version we loose support for non-method calls, which I think is OK.
I can quickly add it back by adding a .getLocalSource()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, we'd lose quite a few calls. They don't look very interesting I'll give you that, but I think we should keep them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm ok with loosing non-method calls, but I'm not comfortable with loosing the reflective calls.
I'll revert it back to having 2 cases.
bindingset[result] | ||
string unsafeAttributeName() { | ||
result.regexpMatch("data-.*") or | ||
result = ["name", "value"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest including aria-.*
and maybe title
and alt
as well.
Writing text from a webpage to the same webpage without properly sanitizing the | ||
input first, might allow for a cross-site scripting vulnerability. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This opening paragraph feels a little too generic. I'd try to be a little more concrete, for example:
Writing text from a webpage to the same webpage without properly sanitizing the | |
input first, might allow for a cross-site scripting vulnerability. | |
Extracting text from a DOM node and interpreting it as HTML can lead to a cross-site scripting vulnerability. |
and then in the next paragraph explain why that's the case, i.e. that it invalidates any escaping already performed on that data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@mchammer01 can we ask you for a doc review?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes of course @asgerf 😃. Here is my review:
@erik-krogh - this looks good 👍
There is a small number of minor comments for your consideration.
Also could you add an update to the change notes (the page with a table summarizing the new and updated queries, and that also reports whether query results are shown on LGTM by default or not)? Thanks.
Hope this helps.
@@ -0,0 +1,21 @@ | |||
/** | |||
* @name Cross-site scripting through DOM | |||
* @description Writing user controlled DOM to HTML can allow for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be user-controlled
(with an hyphen)?
</li> | ||
<li> | ||
OWASP | ||
<a href="https://www.owasp.org/index.php/DOM_Based_XSS">DOM Based XSS</a>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This reference and the one below have a note at the top of the page, saying that their content hasn't been moved to a new platform yet. Just wondering whether we're still happy to link to these URLs (I am not sure whether the URLs will change once the content is ported onto the new platform, so feel free to ignore if my comment is irrelevant).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The links already redirect to the new wiki.
(https://www.owasp.org/index.php/DOM_Based_XSS redirects to https://owasp.org/www-community/attacks/DOM_Based_XSS).
I'll update the links, and the warning will hopefully disappear from their site eventually.
Co-Authored-By: mc <42146119+mchammer01@users.noreply.github.com>
I've added the change note.
It did 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see some potential for reuse. Please let me know if they are invalid.
.getAnArgument() | ||
.(StringOps::ConcatenationRoot) | ||
.getConstantStringParts() | ||
.substring(0, 1) = "<" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you look into reusing some of this logic, or at least using a regexp instead of substring? https://github.com/Semmle/ql/blob/1b88c9768827c37355eea473cba4e24796be310a/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll#L80-L83
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't reuse all of it.
But I can use isPrefixOfJQueryHtmlString
, and I'll copy-paste the regexp.
read.getPropertyName() = propName or | ||
read.getPropertyNameExpr().flow().mayHaveStringValue(propName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created https://github.com/github/codeql-javascript-team/issues/98 for this pattern just now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets do that in a later PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely.
* | ||
* This sanitizer helps prune infeasible paths in type-overloaded functions. | ||
*/ | ||
class TypeTestGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered the sanitizers for js/unsafe-jquery-plugin?https://github.com/Semmle/ql/blob/1b88c9768827c37355eea473cba4e24796be310a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPluginCustomizations.qll#L187-L190
They are also used to eliminate some non-string values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sanitizer is a generalization of one of my sanitizers, so that is perfect 👍
@mchammer01: Can you approve the changes? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies @erik-krogh, I had the day off yesterday.
LGTM, thanks for the documentation updates ✨
Adds a new query
js/xss-through-dom
.Relevant CVEs:
All of the above CVEs reads a string from a DOM node (attribute or text-node), where that string is controlled by someone else (e.g. a client, another library, rendered by a server...).
This string then ends up in an XSS sink, and a safe text thereby becomes unsafe HTML.
So the query does not flag vulnerabilities that are immediately exploitable. An attacker needs a write primitive to the relevant location.
But the point is that the write primitive is safe on its own, and an
xss-through-dom
vulnerability will escalate the attack by unescaping the text.The query currently flags a lot of results, many of these are just bad style and not necessarily exploitable.
Here are some results: https://lgtm.com/query/2351488529036590711/
Many of the results are the old bootstrap CVE (CVE-2016-10735).
(That CVE existed across many versions of bootstrap. The query does not currently flag all versions).
With this PR we get 2 CVE TPs, and one step closer to 4 more CVE TPs.
(I don't know how many of the last 4 we can reasonably get. 3 of them miss have missing type/data-flow, and the last is a missing
$.jGrowl
sink)An evaluation shows that the performance overhead is not that big.
TODO:
SanitizerGuardNode
s toDomBasedXss
?