mirror of
https://github.com/github/codeql.git
synced 2025-12-21 11:16:30 +01:00
Merge pull request #2429 from erik-krogh/typeAheadSink
Approved by esbena
This commit is contained in:
@@ -92,6 +92,7 @@ import semmle.javascript.frameworks.SQL
|
||||
import semmle.javascript.frameworks.SocketIO
|
||||
import semmle.javascript.frameworks.StringFormatters
|
||||
import semmle.javascript.frameworks.TorrentLibraries
|
||||
import semmle.javascript.frameworks.Typeahead
|
||||
import semmle.javascript.frameworks.UriLibraries
|
||||
import semmle.javascript.frameworks.Vue
|
||||
import semmle.javascript.frameworks.XmlParsers
|
||||
|
||||
135
javascript/ql/src/semmle/javascript/frameworks/Typeahead.qll
Normal file
135
javascript/ql/src/semmle/javascript/frameworks/Typeahead.qll
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Provides classes for working with typeahead.js code (https://www.npmjs.com/package/typeahead.js).
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
module Typeahead {
|
||||
/**
|
||||
* A reference to the Bloodhound class, which is a utility-class for generating auto-complete suggestions.
|
||||
*/
|
||||
private class Bloodhound extends DataFlow::SourceNode {
|
||||
Bloodhound() {
|
||||
this = DataFlow::moduleImport("typeahead.js/dist/bloodhound.js")
|
||||
or
|
||||
this = DataFlow::moduleImport("bloodhound-js")
|
||||
or
|
||||
this.accessesGlobal("Bloodhound")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of the Bloodhound class.
|
||||
*/
|
||||
private class BloodhoundInstance extends DataFlow::NewNode {
|
||||
BloodhoundInstance() { this = any(Bloodhound b).getAnInstantiation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of of the Bloodhound class that is used to fetch data from a remote server.
|
||||
*/
|
||||
private class RemoteBloodhoundClientRequest extends ClientRequest::Range, BloodhoundInstance {
|
||||
DataFlow::ValueNode option;
|
||||
|
||||
RemoteBloodhoundClientRequest() {
|
||||
exists(string optionName | optionName = "remote" or optionName = "prefetch" |
|
||||
option = this.getOptionArgument(0, optionName)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL for this Bloodhound instance.
|
||||
* The Bloodhound API specifies that the "remote" and "prefetch" options are either strings,
|
||||
* or an object containing an "url" property.
|
||||
*/
|
||||
override DataFlow::Node getUrl() {
|
||||
result = option.getALocalSource().getAPropertyWrite("url").getRhs()
|
||||
or
|
||||
result = option
|
||||
}
|
||||
|
||||
override DataFlow::Node getHost() { none() }
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
|
||||
/** Gets a Bloodhound instance that fetches remote server data. */
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
t.start() and result = this
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a Bloodhound instance that fetches remote server data. */
|
||||
private DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
|
||||
|
||||
override DataFlow::Node getAResponseDataNode(string responseType, boolean promise) {
|
||||
responseType = "json" and
|
||||
promise = false and
|
||||
exists(TypeaheadSource source |
|
||||
ref() = source.getALocalSource() or ref().getAMethodCall("ttAdapter") = source
|
||||
|
|
||||
result = source.getASuggestion()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of the `typeahead.js` library.
|
||||
*/
|
||||
private class TypeaheadCall extends DataFlow::CallNode {
|
||||
TypeaheadCall() {
|
||||
// Matches `$(...).typeahead(..)`
|
||||
this = JQuery::objectRef().getAMethodCall("typeahead")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that generates suggestions to typeahead.js.
|
||||
*/
|
||||
class TypeaheadSuggestionFunction extends DataFlow::FunctionNode {
|
||||
TypeaheadCall typeaheadCall;
|
||||
|
||||
TypeaheadSuggestionFunction() {
|
||||
// Matches `$(...).typeahead({..}, { templates: { suggestion: <this> } })`.
|
||||
this = typeaheadCall
|
||||
.getOptionArgument(1, "templates")
|
||||
.getALocalSource()
|
||||
.getAPropertyWrite("suggestion")
|
||||
.getRhs()
|
||||
.getAFunctionValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the call to typeahead.js where this suggestion function is used.
|
||||
*/
|
||||
TypeaheadCall getTypeaheadCall() { result = typeaheadCall }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `source` option for a typeahead.js plugin instance.
|
||||
*/
|
||||
private class TypeaheadSource extends DataFlow::ValueNode {
|
||||
TypeaheadCall typeaheadCall;
|
||||
|
||||
TypeaheadSource() { this = typeaheadCall.getOptionArgument(1, "source") }
|
||||
|
||||
/** Gets a node for a suggestion that this source motivates. */
|
||||
DataFlow::Node getASuggestion() {
|
||||
exists(TypeaheadSuggestionFunction suggestionCallback |
|
||||
suggestionCallback.getTypeaheadCall() = typeaheadCall and
|
||||
result = suggestionCallback.getParameter(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint step that models that a function in the `source` of typeahead.js is used to determine the input to the suggestion function.
|
||||
*/
|
||||
private class TypeaheadSourceTaintStep extends TypeaheadSource, TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// Matches `$(...).typeahead({..}, {source: function(q, cb) {..;cb(<pred>);..}, templates: { suggestion: function(<succ>) {} } })`.
|
||||
pred = this.getAFunctionValue().getParameter([1 .. 2]).getACall().getAnArgument() and
|
||||
succ = this.getASuggestion()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,8 @@ module DomBasedXss {
|
||||
this = mcn.getArgument(1)
|
||||
)
|
||||
or
|
||||
this = any(Typeahead::TypeaheadSuggestionFunction f).getAReturn()
|
||||
or
|
||||
this = any(Handlebars::SafeString s).getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user