JS: Add unsafe-html-construction query#5769
Conversation
ec82340 to
27b718f
Compare
b4fbde1 to
f662ddd
Compare
esbena
left a comment
There was a problem hiding this comment.
Generally LGTM. Here's half a review. I would very much like to give it a second read-through, but in the interest of progress, I am submitting some comments now.
| class ExternalInputSource extends Source, DataFlow::ParameterNode { | ||
| ExternalInputSource() { | ||
| this = Exports::getALibraryInputParameter() and | ||
| not this = JQuery::dollarSource() |
There was a problem hiding this comment.
Please add a comment about why we need to exclude dollarSource
|
|
||
| <p> | ||
| Alternatively, use a HTML sanitizer to escape/remove unsafe content. | ||
| </p> |
There was a problem hiding this comment.
Unlike the command-injection variant of this query, I think it is (more) reasonable to do some ad hoc HTML concatenation in a library.
Experience from the unsafe jquery plugins tells that some library maintainers do not feel responsible for these kinds of XSS vectors, and some of them just update their README to tell clients that they are responsible for sanitizing HTML before using the library.
Can we angle the qhelp such that additional documentation is a known fix type (a fix that won't remove the "FP").
Perhaps some qhelp fragments should be shared between this query and js/unsafe-jquery-plugin.
There was a problem hiding this comment.
I like the idea, but I don't think we can share fragments as the terms used are different
(plugin vs library and options vs input).
I'm rewriting the qhelp to focus more on unsafe endpoints being properly documented.
Co-authored-by: Esben Sparre Andreasen <esbena@github.com>
Co-authored-by: Esben Sparre Andreasen <esbena@github.com>
esbena
left a comment
There was a problem hiding this comment.
Thanks.
Here are some additional comments.
| Dynamically constructing HTML with inputs from library functions that are | ||
| available to external clients may inadvertently leave a client open to XSS attacks. | ||
|
|
||
| Clients using the exported function may use inputs containing unsafe HTML, | ||
| and if these inputs end up in the DOM, the client may be vulnerable to | ||
| cross-site scripting attacks. |
There was a problem hiding this comment.
XSS attacks
cross-site scripting attacks
Pick one.
| Dynamically constructing HTML with inputs from library functions that are | ||
| available to external clients may inadvertently leave a client open to XSS attacks. | ||
|
|
||
| Clients using the exported function may use inputs containing unsafe HTML, |
There was a problem hiding this comment.
| Clients using the exported function may use inputs containing unsafe HTML, | |
| Clients using the exported function may use inputs containing unsafe HTML fragments, |
| <recommendation> | ||
|
|
||
| <p> | ||
| If possible, use safe APIs when inserting HTML into the DOM. |
There was a problem hiding this comment.
| If possible, use safe APIs when inserting HTML into the DOM. | |
| If possible, use safe APIs when modifying the DOM. |
When the suggestion below is to use innerText, it is confusion to see "when inserting HTML".
| } | ||
| module Markdown { | ||
| /** | ||
| * A taint-step that parses a markdown document, but preserves taint.import |
There was a problem hiding this comment.
| * A taint-step that parses a markdown document, but preserves taint.import | |
| * A taint-step that parses a markdown document, but preserves taint. |
?
| call = markdownIt().getMember(["use", "set", "configure", "enable", "disable"]).getACall() and | ||
| result = call.getReturn() and | ||
| not call.getParameter(0).getAValueReachingRhs() = | ||
| DataFlow::moduleImport("markdown-it-sanitizer") |
There was a problem hiding this comment.
Should this negation be in the preservesHtml predicate?
There was a problem hiding this comment.
I think that would require adding a boolean isSanitized parameter to the markdownIt() predicate.
The MarkdownStep::step predicate is documented as being a taint-step.
And if markdown-it-sanitizer is used, then there is no taint-step.
So I think it's fine.
| sink = mid.getASuccessor() and | ||
| mid.getPathSummary().hasReturn() = false | ||
| ) | ||
| UnsafeHtmlConstruction::requireMatchedReturn(source, sink) |
There was a problem hiding this comment.
Can we move this step to a shared library.
I the step already existed in UnsafeShellCommandConstruction.qll , and was simply referenced from UnsafeHtmlConstruction.qll I would be more inclined to shrug this off, but now we are introducing a dependency on a new query instead.
Gets a TP for CVE-2020-7750
The idea (and structure) of the query is the same as
js/shell-command-constructed-from-input:The TP rate is good.
I had some FPs from a run-on-all, but almost all where removed with the
remove flowpaths that has a return without a matching callcommit and thenot this = JQuery::dollarSource()check.I'll look into promoting
classFieldStepto aSharedTaintStepafter this PR has been merged.Performance evaluation:
part 1 looks good
part 2 (and a redo) looks good. It seems performance on the workers got more unstable 🤷
The new results look like TPs (or at least bad style).