Merge pull request #6574 from asgerf/js/vue-api-graphs

Approved by erik-krogh
This commit is contained in:
CodeQL CI
2021-09-07 05:53:30 -07:00
committed by GitHub
10 changed files with 321 additions and 232 deletions

View File

@@ -313,6 +313,19 @@ class AmdModule extends Module {
name = pwn.getPropertyName()
)
}
override DataFlow::Node getABulkExportedNode() {
// Assigned to `module.exports` via the factory's `module` parameter
exists(AbstractModuleObject m, DataFlow::PropWrite write |
m.getModule() = this and
write.getPropertyName() = "exports" and
write.getBase().analyze().getAValue() = m and
result = write.getRhs()
)
or
// Returned from factory function
result = getDefine().getModuleExpr().flow()
}
}
/**

View File

@@ -177,6 +177,10 @@ module Closure {
)
)
}
override DataFlow::Node getABulkExportedNode() {
result = getExportsVariable().getAnAssignedExpr().flow()
}
}
/**

View File

@@ -111,6 +111,23 @@ abstract class Module extends TopLevel {
cached
abstract DataFlow::Node getAnExportedValue(string name);
/**
* Gets a value that is exported as the whole exports object of this module.
*/
cached
DataFlow::Node getABulkExportedNode() { none() } // overridden in subclasses
/**
* Gets the ES2015 `default` export from this module, or for other types of modules,
* gets a bulk exported node.
*
* This can be used to determine which value a default-import will likely refer to,
* as the interaction between different module types is not standardized.
*/
DataFlow::Node getDefaultOrBulkExport() {
result = [getAnExportedValue("default"), getABulkExportedNode()]
}
/**
* Gets the root folder relative to which the given import path (which must
* appear in this module) is resolved.

View File

@@ -99,6 +99,14 @@ class NodeModule extends Module {
)
}
override DataFlow::Node getABulkExportedNode() {
exists(DataFlow::PropWrite write |
write.getBase().asExpr() = getModuleVariable().getAnAccess() and
write.getPropertyName() = "exports" and
result = write.getRhs()
)
}
/** Gets a symbol that the module object inherits from its prototypes. */
private string getAnImplicitlyExportedSymbol() {
exists(ExternalConstructor ec | ec = getPrototypeOfExportedExpr() |

View File

@@ -164,11 +164,9 @@ private DataFlow::Node getAValueExportedByPackage() {
* Gets an exported node from the module `mod`.
*/
private DataFlow::Node getAnExportFromModule(Module mod) {
result.analyze().getAValue() = mod.(NodeModule).getAModuleExportsValue()
or
result = mod.(Closure::ClosureModule).getExportsVariable().getAnAssignedExpr().flow()
or
result.analyze().getAValue() = mod.(AmdModule).getDefine().getAModuleExportsValue()
or
result = mod.getAnExportedValue(_)
or
result = mod.getABulkExportedNode()
or
result.analyze().getAValue() = TAbstractModuleObject(mod)
}

View File

@@ -5,29 +5,69 @@
import javascript
module Vue {
/** The global variable `Vue`, as an API graph entry point. */
private class GlobalVueEntryPoint extends API::EntryPoint {
GlobalVueEntryPoint() { this = "VueEntryPoint" }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("Vue") }
override DataFlow::Node getARhs() { none() }
}
/**
* A value exported from a `.vue` file.
*
* This `EntryPoint` is used by `SingleFileComponent::getOwnOptions()`.
*/
private class VueExportEntryPoint extends API::EntryPoint {
VueExportEntryPoint() { this = "VueExportEntryPoint" }
override DataFlow::SourceNode getAUse() { none() }
override DataFlow::Node getARhs() {
result = any(SingleFileComponent c).getModule().getDefaultOrBulkExport()
}
}
/**
* Gets a reference to the `Vue` object.
*/
API::Node vueLibrary() {
result = API::moduleImport("vue")
or
result = API::root().getASuccessor(any(GlobalVueEntryPoint e))
}
/**
* Gets a reference to the 'Vue' object.
*/
DataFlow::SourceNode vue() {
result = DataFlow::globalVarRef("Vue")
DataFlow::SourceNode vue() { result = vueLibrary().getAnImmediateUse() }
/** An API node referring to a component or `Vue`. */
private API::Node component() {
result = vueLibrary()
or
result = DataFlow::moduleImport("vue")
result = component().getMember("extend").getReturn()
or
result = vueLibrary().getMember("component").getReturn()
or
result = API::root().getASuccessor(any(VueFileImportEntryPoint e))
}
/**
* A call to `vue.extend`.
* A call to `Vue.extend` or `extend` on a component.
*/
private class VueExtend extends DataFlow::CallNode {
VueExtend() { this = vue().getAMemberCall("extend") }
private class VueExtendCall extends API::CallNode {
VueExtendCall() { this = component().getMember("extend").getACall() }
}
/** A component created by an explicit or implicit call to `Vue.extend`. */
private newtype TComponent =
MkVueInstance(DataFlow::NewNode def) { def = vue().getAnInstantiation() } or
MkExtendedVue(VueExtend extend) or
MkExtendedInstance(VueExtend extend, DataFlow::NewNode sub) {
sub = extend.getAnInstantiation()
MkComponentExtension(VueExtendCall extend) or
MkComponentInstantiation(API::NewNode sub) { sub = component().getAnInstantiation() } or
MkComponentRegistration(API::CallNode def) {
def = vueLibrary().getMember("component").getACall()
} or
MkComponentRegistration(DataFlow::CallNode def) { def = vue().getAMemberCall("component") } or
MkSingleFileComponent(VueFile file)
/** Gets the name of a lifecycle hook method. */
@@ -50,7 +90,7 @@ module Vue {
/**
* A class with a `@Component` decorator, making it usable as an "options" object in Vue.
*/
private class ClassComponent extends DataFlow::ClassNode {
class ClassComponent extends DataFlow::ClassNode {
DataFlow::Node decorator;
ClassComponent() {
@@ -66,13 +106,11 @@ module Vue {
}
/**
* Gets an option passed to the `@Component` decorator.
* Gets the options object passed to the `@Component` decorator, if any.
*
* These options correspond to the options one would pass to `new Vue({...})` or similar.
*/
DataFlow::Node getDecoratorOption(string name) {
result = decorator.(DataFlow::CallNode).getOptionArgument(0, name)
}
API::Node getDecoratorOptions() { result = decorator.(API::CallNode).getParameter(0) }
}
private string memberKindVerb(DataFlow::MemberKind kind) {
@@ -126,10 +164,43 @@ module Vue {
}
/**
* Gets an API node referring to the component itself.
*/
API::Node getComponentRef() { none() } // overridden in subclass
/**
* Gets an API node referring to the options passed to the Vue object,
* such as the object literal `{...}` in `new Vue{{...})` or the default export of a single-file component.
*/
API::Node getOwnOptions() { none() } // overridden in subclass
/** Gets a component which is extended by this one. */
Component getABaseComponent() {
result.getComponentRef().getAUse() =
getOwnOptions().getMember(["extends", "mixins"]).getARhs()
}
/**
* Gets an API node referring to the options passed to the Vue object or one
* of its base component.
*/
API::Node getOptions() {
result = getOwnOptions()
or
result = getOwnOptions().getMember(["extends", "mixins"]).getAMember()
or
result = getABaseComponent().getOptions()
or
result = getAsClassComponent().getDecoratorOptions()
}
/**
* DEPRECATED. Use `getOwnOptions().getARhs()`.
*
* Gets the options passed to the Vue object, such as the object literal `{...}` in `new Vue{{...})`
* or the default export of a single-file component.
*/
DataFlow::Node getOwnOptionsObject() { none() } // overridden in subclasses
deprecated DataFlow::Node getOwnOptionsObject() { result = getOwnOptions().getARhs() }
/**
* Gets the class implementing this Vue component, if any.
@@ -137,47 +208,28 @@ module Vue {
* Specifically, this is a class annotated with `@Component` which flows to the options
* object of this Vue component.
*/
ClassComponent getAsClassComponent() { result.flowsTo(getOwnOptionsObject()) }
ClassComponent getAsClassComponent() { result = getOwnOptions().getAValueReachingRhs() }
/**
* Gets the node for option `name` for this component, not including
* those from extended objects and mixins.
*/
DataFlow::Node getOwnOption(string name) {
result = getOwnOptionsObject().getALocalSource().getAPropertyWrite(name).getRhs()
}
DataFlow::Node getOwnOption(string name) { result = getOwnOptions().getMember(name).getARhs() }
/**
* Gets the node for option `name` for this component, including those from
* extended objects and mixins.
*/
DataFlow::Node getOption(string name) {
result = getOwnOption(name)
or
exists(DataFlow::SourceNode extendsVal | extendsVal.flowsTo(getOwnOption("extends")) |
result = extendsVal.(DataFlow::ObjectLiteralNode).getAPropertyWrite(name).getRhs()
or
exists(ExtendedVue extend |
MkExtendedVue(extendsVal) = extend and
result = extend.getOption(name)
)
)
or
exists(DataFlow::ArrayCreationNode mixins, DataFlow::ObjectLiteralNode mixin |
mixins.flowsTo(getOwnOption("mixins")) and
mixin.flowsTo(mixins.getAnElement()) and
result = mixin.getAPropertyWrite(name).getRhs()
)
or
result = getAsClassComponent().getDecoratorOption(name)
}
DataFlow::Node getOption(string name) { result = getOptions().getMember(name).getARhs() }
/**
* Gets a source node flowing into the option `name` of this component, including those from
* extended objects and mixins.
*/
pragma[nomagic]
DataFlow::SourceNode getOptionSource(string name) { result = getOption(name).getALocalSource() }
DataFlow::SourceNode getOptionSource(string name) {
result = getOptions().getMember(name).getAValueReachingRhs()
}
/**
* Gets the template element used by this component, if any.
@@ -188,15 +240,9 @@ module Vue {
* Gets the node for the `data` option object of this component.
*/
DataFlow::Node getData() {
exists(DataFlow::Node data | data = getOption("data") |
result = data
or
// a constructor variant is available for all component definitions
exists(DataFlow::FunctionNode f |
f.flowsTo(data) and
result = f.getAReturn()
)
)
result = getOption("data")
or
result = getOptionSource("data").(DataFlow::FunctionNode).getReturnNode()
or
result = getAsClassComponent().getAReceiverNode()
or
@@ -241,37 +287,12 @@ module Vue {
* Gets the function responding to changes to the given `propName`.
*/
DataFlow::FunctionNode getWatchHandler(string propName) {
exists(DataFlow::SourceNode watcher | watcher = getWatch().getAPropertySource(propName) |
result = watcher
or
result = watcher.getAPropertySource("handler")
exists(API::Node propWatch |
propWatch = getOptions().getMember("watch").getMember(propName) and
result = [propWatch, propWatch.getMember("handler")].getAValueReachingRhs()
)
}
/**
* Gets a node for a member of the `methods` option of this component.
*/
pragma[nomagic]
private DataFlow::SourceNode getAMethod() {
result = getMethods().getAPropertySource()
or
result = getAsClassComponent().getAnInstanceMethod() and
not result = getAsClassComponent().getInstanceMethod([lifecycleHookName(), "render", "data"])
}
/**
* Gets a node for a member of the `computed` option of this component that matches `kind`.
*/
pragma[nomagic]
private DataFlow::SourceNode getAnAccessor(DataFlow::MemberKind kind) {
result = getComputed().getAPropertySource() and kind = DataFlow::MemberKind::getter()
or
result = getComputed().getAPropertySource().getAPropertySource(memberKindVerb(kind))
or
result = getAsClassComponent().getAnInstanceMember(kind) and
kind.isAccessor()
}
/**
* Gets a node for a member `name` of the `computed` option of this component that matches `kind`.
*/
@@ -301,27 +322,22 @@ module Vue {
* Gets a node for a function that will be invoked with `this` bound to this component.
*/
DataFlow::FunctionNode getABoundFunction() {
result = getAMethod()
result = getOptions().getAMember+().getAValueReachingRhs()
or
result = getAnAccessor(_)
or
result = getALifecycleHook(_)
or
result = getOptionSource(_)
or
result = getOptionSource(_).getAPropertySource()
result = getAsClassComponent().getAnInstanceMember()
}
/** Gets an API node referring to an instance of this component. */
API::Node getInstance() { result.getAnImmediateUse() = getABoundFunction().getReceiver() }
/** Gets a data flow node referring to an instance of this component. */
DataFlow::SourceNode getAnInstanceRef() { result = getInstance().getAnImmediateUse() }
pragma[noinline]
private DataFlow::PropWrite getAPropertyValueWrite(string name) {
result = getData().getALocalSource().getAPropertyWrite(name)
or
result =
getABoundFunction()
.getALocalSource()
.(DataFlow::FunctionNode)
.getReceiver()
.getAPropertyWrite(name)
result = getAnInstanceRef().getAPropertyWrite(name)
}
/**
@@ -339,12 +355,12 @@ module Vue {
}
/**
* A Vue component from `new Vue({...})`.
* A Vue component created implicitly at an invocation of form `new Vue({...})` or `new CustomComponent({...})`.
*/
class VueInstance extends Component, MkVueInstance {
DataFlow::NewNode def;
private class ComponentInstantiation extends Component, MkComponentInstantiation {
API::NewNode def;
VueInstance() { this = MkVueInstance(def) }
ComponentInstantiation() { this = MkComponentInstantiation(def) }
override string toString() { result = def.toString() }
@@ -354,18 +370,47 @@ module Vue {
def.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override DataFlow::Node getOwnOptionsObject() { result = def.getArgument(0) }
override API::Node getComponentRef() {
// The Vue.extend call is made in the Vue framework; there is no explicit reference
// to the component in user code.
none()
}
override API::Node getOwnOptions() { result = def.getParameter(0) }
override Template::Element getTemplateElement() { none() }
override Component getABaseComponent() {
result = Component.super.getABaseComponent()
or
result.getComponentRef().getAnInstantiation() = def
}
}
/**
* An extended Vue from `Vue.extend({...})`.
* DEPRECATED. Use `Vue::Component` instead.
*
* A Vue component from `new Vue({...})`.
*/
class ExtendedVue extends Component, MkExtendedVue {
VueExtend extend;
deprecated class VueInstance extends Component {
VueInstance() {
// restrict charpred to match original behavior
this = MkComponentInstantiation(vueLibrary().getAnInstantiation())
}
}
ExtendedVue() { this = MkExtendedVue(extend) }
/**
* DEPRECATED. Use `Vue::ComponentExtension` or `Vue::Component` instead.
*/
deprecated class ExtendedVue = ComponentExtension;
/**
* A component created via an explicit call to `Vue.extend({...})` or `CustomComponent.extend({...})`.
*/
class ComponentExtension extends Component, MkComponentExtension {
VueExtendCall extend;
ComponentExtension() { this = MkComponentExtension(extend) }
override string toString() { result = extend.toString() }
@@ -375,44 +420,37 @@ module Vue {
extend.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override DataFlow::Node getOwnOptionsObject() { result = extend.getArgument(0) }
override API::Node getComponentRef() { result = extend.getReturn() }
override API::Node getOwnOptions() { result = extend.getParameter(0) }
override Template::Element getTemplateElement() { none() }
override Component getABaseComponent() {
result = Component.super.getABaseComponent()
or
result.getComponentRef().getMember("extend").getACall() = extend
}
}
/**
* DEPRECATED. Use `Vue::Component` instead.
*
* An instance of an extended Vue, for example `instance` of `var Ext = Vue.extend({...}); var instance = new Ext({...})`.
*/
class ExtendedInstance extends Component, MkExtendedInstance {
VueExtend extend;
DataFlow::NewNode sub;
ExtendedInstance() { this = MkExtendedInstance(extend, sub) }
override string toString() { result = sub.toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
sub.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
deprecated class ExtendedInstance extends Component {
ExtendedInstance() {
// restrict charpred to match original behavior
this =
MkComponentInstantiation(vueLibrary().getMember("extend").getReturn().getAnInstantiation())
}
override DataFlow::Node getOwnOptionsObject() { result = sub.getArgument(0) }
override DataFlow::Node getOption(string name) {
result = Component.super.getOption(name)
or
result = MkExtendedVue(extend).(ExtendedVue).getOption(name)
}
override Template::Element getTemplateElement() { none() }
}
/**
* A Vue component from `Vue.component("my-component", { ... })`.
*/
class ComponentRegistration extends Component, MkComponentRegistration {
DataFlow::CallNode def;
API::CallNode def;
ComponentRegistration() { this = MkComponentRegistration(def) }
@@ -424,11 +462,38 @@ module Vue {
def.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override DataFlow::Node getOwnOptionsObject() { result = def.getArgument(1) }
override API::Node getComponentRef() {
// The component can be obtained via 1-argument calls to `Vue.component()` with the
// same name, but we don't model this at the moment.
none()
}
override API::Node getOwnOptions() { result = def.getParameter(1) }
override Template::Element getTemplateElement() { none() }
}
/**
* An import referring to a `.vue` file, seen as an API entry point.
*
* Concretely, such an import receives the Vue component generated from the .vue file,
* not the actual exports of the script tag in the file.
*
* This entry point is used in `SingleFileComponent::getComponentRef()`.
*/
private class VueFileImportEntryPoint extends API::EntryPoint {
VueFileImportEntryPoint() { this = "VueFileImportEntryPoint" }
override DataFlow::SourceNode getAUse() {
exists(Import imprt |
imprt.getImportedPath().resolve() instanceof VueFile and
result = imprt.getImportedModuleNode()
)
}
override DataFlow::Node getARhs() { none() }
}
/**
* A single file Vue component in a `.vue` file.
*/
@@ -455,39 +520,26 @@ module Vue {
endcolumn = 0
}
private Module getModule() {
/** Gets the module defined by the `script` tag in this .vue file, if any. */
Module getModule() {
exists(HTML::ScriptElement elem |
xmlElements(elem, _, _, _, file) and // Avoid materializing all of Locatable.getFile()
result.getTopLevel() = elem.getScript()
)
}
override DataFlow::Node getOwnOptionsObject() {
exists(ExportDefaultDeclaration decl |
decl.getTopLevel() = getModule() and
result = DataFlow::valueNode(decl.getOperand())
override API::Node getComponentRef() {
// There is no explicit `new Vue()` call in .vue files, so instead get all the imports
// of the .vue file.
exists(Import imprt |
imprt.getImportedPath().resolve() = file and
result.getAnImmediateUse() = imprt.getImportedModuleNode()
)
}
override DataFlow::Node getOwnOption(string name) {
// The options of a single file component are defined by the exported object of the script element.
// Our current module model does not support reads on this object very well, so we use custom steps for the common cases for now.
exists(Module m, DefiniteAbstractValue abstractOptions |
any(AnalyzedPropertyWrite write).writes(abstractOptions, name, result) and
m = getModule()
|
// ES2015 exports
exists(ExportDeclaration export, DataFlow::AnalyzedNode exported |
export.getEnclosingModule() = m and
abstractOptions = exported.getAValue()
|
exported = export.(BulkReExportDeclaration).getSourceNode("default") or
exported.asExpr() = export.(ExportDefaultDeclaration).getOperand()
)
or
// Node.js exports
abstractOptions = m.(NodeModule).getAModuleExportsValue()
)
override API::Node getOwnOptions() {
// Use the entry point generated by `VueExportEntryPoint`
result.getARhs() = getModule().getDefaultOrBulkExport()
}
override string toString() { result = file.toString() }
@@ -500,20 +552,31 @@ module Vue {
VueFile() { getExtension() = "vue" }
}
pragma[nomagic]
private DataFlow::Node propStepPred(Component comp, string name) {
result = comp.getAPropertyValue(name)
}
pragma[nomagic]
private DataFlow::Node propStepSucc(Component comp, string name) {
result = comp.getAnInstanceRef().getAPropertyRead(name)
}
/**
* A taint propagating data flow edge through a Vue instance property.
*/
class InstanceHeapStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(Component i, string name, DataFlow::FunctionNode bound |
bound.flowsTo(i.getABoundFunction()) and
not bound.getFunction() instanceof ArrowFunctionExpr and
succ = bound.getReceiver().getAPropertyRead(name) and
pred = i.getAPropertyValue(name)
private class PropStep extends TaintTracking::SharedTaintStep {
override predicate viewComponentStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(Component comp, string name |
pred = propStepPred(comp, name) and
succ = propStepSucc(comp, name)
)
}
}
/** DEPRECATED. Do not use. */
deprecated class InstanceHeapStep = PropStep;
/**
* A Vue `v-html` attribute.
*/
@@ -538,11 +601,11 @@ module Vue {
* of `inst = new Vue({ ..., data: { prop: source } })`, if the
* `div` element is part of the template for `inst`.
*/
class VHtmlSourceWrite extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(Vue::Component component, string expr, VHtmlAttribute attr |
private class VHtmlAttributeStep extends TaintTracking::SharedTaintStep {
override predicate viewComponentStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(Component component, string expr, VHtmlAttribute attr |
attr.getAttr().getRoot() =
component.getTemplateElement().(Vue::Template::HtmlElement).getElement() and
component.getTemplateElement().(Template::HtmlElement).getElement() and
expr = attr.getAttr().getValue() and
// only support for simple identifier expressions
expr.regexpMatch("(?i)[a-z0-9_]+") and
@@ -552,6 +615,11 @@ module Vue {
}
}
/**
* DEPRECATED. Do not use.
*/
deprecated class VHtmlSourceWrite = VHtmlAttributeStep;
/*
* Provides classes for working with Vue templates.
*/

View File

@@ -5,38 +5,6 @@
private import javascript
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
/** A minimal adapter for the `vue` model based on API nodes. */
private module VueAPI {
/** A value exported from a `.vue` file. */
private class VueExportEntryPoint extends API::EntryPoint {
VueExportEntryPoint() { this = "VueExportEntryPoint" }
override DataFlow::SourceNode getAUse() { none() }
override DataFlow::Node getARhs() {
exists(Module mod |
mod.getFile() instanceof Vue::VueFile and
result = mod.getAnExportedValue("default")
)
}
}
/**
* An API node representing the object passed to the Vue constructor `new Vue({...})`
* or equivalent.
*/
class VueConfigObject extends API::Node {
VueConfigObject() { this.getARhs() = any(Vue::Component c).getOwnOptionsObject() }
/** Gets an API node representing `this` in the Vue component. */
API::Node getAnInstanceRef() {
result = getAMember().getReceiver()
or
result = getAMember().getAMember().getReceiver()
}
}
}
/**
* Provides classes and predicates for working with the `vuex` library.
*/
@@ -63,7 +31,7 @@ module Vuex {
API::Node storeRef(string namespace) {
result = vuex().getMember("Store").getInstance() and namespace = ""
or
result = any(VueAPI::VueConfigObject v).getAnInstanceRef().getMember("$store") and
result = any(Vue::Component v).getInstance().getMember("$store") and
namespace = ""
or
result =
@@ -146,13 +114,13 @@ module Vuex {
}
/** Gets the Vue component in which the generated functions are installed. */
VueAPI::VueConfigObject getVueConfigObject() {
Vue::Component getVueComponent() {
exists(DataFlow::ObjectLiteralNode obj |
obj.getASpreadProperty() = getReturn().getAUse() and
result.getAMember().getARhs() = obj
result.getOwnOptions().getAMember().getARhs() = obj
)
or
result.getAMember().getARhs() = this
result.getOwnOptions().getAMember().getARhs() = this
}
}
@@ -167,7 +135,7 @@ module Vuex {
exists(MapHelperCall call, string localName |
call.getHelperName() = helperName and
call.hasMapping(localName, storeName) and
result = call.getVueConfigObject().getAnInstanceRef().getMember(localName) and
result = call.getVueComponent().getInstance().getMember(localName) and
localName != "*"
)
}
@@ -353,10 +321,10 @@ module Vuex {
/**
* Gets the `x` in `mapState({name: () => x})`.
*/
DataFlow::Node mapStateHelperPred(VueAPI::VueConfigObject vue, string name) {
DataFlow::Node mapStateHelperPred(Vue::Component component, string name) {
exists(MapHelperCall call |
call.getHelperName() = "mapState" and
vue = call.getVueConfigObject() and
component = call.getVueComponent() and
result = call.getLastParameter().getMember(name).getReturn().getARhs()
)
}
@@ -366,9 +334,9 @@ module Vuex {
* corresponding property access.
*/
predicate mapStateHelperStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(VueAPI::VueConfigObject vue, string name |
pred = mapStateHelperPred(vue, name) and
succ = pragma[only_bind_out](vue).getAnInstanceRef().getMember(name).getAnImmediateUse()
exists(Vue::Component component, string name |
pred = mapStateHelperPred(component, name) and
succ = pragma[only_bind_out](component).getInstance().getMember(name).getAnImmediateUse()
)
}

View File

@@ -19,6 +19,7 @@ component_getAPropertyValue
| tst.js:48:1:50:2 | new Ext ... 42 }\\n}) | fromSuper | tst.js:44:18:44:19 | 42 |
| tst.js:51:17:57:2 | Vue.ext ... \\n }\\n}) | fromSuper | tst.js:54:18:54:19 | 42 |
| tst.js:58:1:61:2 | new Vue ... 42 }\\n}) | fromSub | tst.js:60:19:60:20 | 42 |
| tst.js:58:1:61:2 | new Vue ... 42 }\\n}) | fromSuper | tst.js:54:18:54:19 | 42 |
| tst.js:63:1:66:2 | new Vue ... 42 }\\n}) | fromMixin1 | tst.js:64:32:64:33 | 42 |
| tst.js:63:1:66:2 | new Vue ... 42 }\\n}) | fromMixin2 | tst.js:64:61:64:62 | 42 |
| tst.js:63:1:66:2 | new Vue ... 42 }\\n}) | fromSub | tst.js:65:19:65:20 | 42 |
@@ -28,6 +29,10 @@ component_getAPropertyValue
| tst.js:85:1:87:2 | new Vue ... e; }\\n}) | created | tst.js:86:38:86:41 | true |
| tst.js:94:2:96:3 | new Vue ... f,\\n\\t}) | dataA | tst.js:89:22:89:23 | 42 |
| tst.js:99:2:104:3 | new Vue ... \\t\\t}\\n\\t}) | dataA | tst.js:100:18:100:19 | 42 |
| tst.js:107:12:109:2 | Vue.ext ... 23 }\\n}) | fromBase | tst.js:108:20:108:22 | 123 |
| tst.js:110:16:112:2 | new Vue ... base\\n}) | fromBase | tst.js:108:20:108:22 | 123 |
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) | fromBase | tst.js:108:20:108:22 | 123 |
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) | fromSubclass2 | tst.js:115:18:115:20 | 100 |
component_getOption
| compont-with-route.vue:0:0:0:0 | compont-with-route.vue | watch | compont-with-route.vue:10:12:16:5 | {\\n ... }\\n } |
| single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue | data | single-component-file-1.vue:6:11:6:45 | functio ... 42 } } |
@@ -44,6 +49,7 @@ component_getOption
| tst.js:48:1:50:2 | new Ext ... 42 }\\n}) | data | tst.js:42:9:46:3 | functio ... };\\n } |
| tst.js:48:1:50:2 | new Ext ... 42 }\\n}) | data | tst.js:49:8:49:22 | { fromSub: 42 } |
| tst.js:51:17:57:2 | Vue.ext ... \\n }\\n}) | data | tst.js:52:9:56:3 | functio ... };\\n } |
| tst.js:58:1:61:2 | new Vue ... 42 }\\n}) | data | tst.js:52:9:56:3 | functio ... };\\n } |
| tst.js:58:1:61:2 | new Vue ... 42 }\\n}) | data | tst.js:60:8:60:22 | { fromSub: 42 } |
| tst.js:58:1:61:2 | new Vue ... 42 }\\n}) | mixins | tst.js:59:10:59:18 | Extended2 |
| tst.js:63:1:66:2 | new Vue ... 42 }\\n}) | data | tst.js:64:18:64:35 | { fromMixin1: 42 } |
@@ -58,6 +64,11 @@ component_getOption
| tst.js:94:2:96:3 | new Vue ... f,\\n\\t}) | data | tst.js:95:9:95:9 | f |
| tst.js:99:2:104:3 | new Vue ... \\t\\t}\\n\\t}) | data | tst.js:100:9:100:21 | { dataA: 42 } |
| tst.js:99:2:104:3 | new Vue ... \\t\\t}\\n\\t}) | methods | tst.js:101:12:103:3 | {\\n\\t\\t\\tm: ... ; }\\n\\t\\t} |
| tst.js:107:12:109:2 | Vue.ext ... 23 }\\n}) | data | tst.js:108:8:108:24 | { fromBase: 123 } |
| tst.js:110:16:112:2 | new Vue ... base\\n}) | data | tst.js:108:8:108:24 | { fromBase: 123 } |
| tst.js:110:16:112:2 | new Vue ... base\\n}) | extends | tst.js:111:11:111:14 | base |
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) | data | tst.js:108:8:108:24 | { fromBase: 123 } |
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) | data | tst.js:114:8:116:2 | {\\n\\t\\tfro ... 100\\n\\t} |
component
| compont-with-route.vue:0:0:0:0 | compont-with-route.vue |
| single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue |
@@ -80,13 +91,16 @@ component
| tst.js:85:1:87:2 | new Vue ... e; }\\n}) |
| tst.js:94:2:96:3 | new Vue ... f,\\n\\t}) |
| tst.js:99:2:104:3 | new Vue ... \\t\\t}\\n\\t}) |
instance_heapStep
| Unit | compont-with-route.vue:31:14:31:34 | this.$r ... ery.foo | compont-with-route.vue:2:8:2:21 | v-html=dataA |
| Unit | single-component-file-1.vue:6:40:6:41 | 42 | single-component-file-1.vue:2:8:2:21 | v-html=dataA |
| Unit | single-file-component-3-script.js:4:37:4:38 | 42 | single-file-component-3.vue:2:8:2:21 | v-html=dataA |
| Unit | single-file-component-4.vue:15:14:15:15 | 42 | single-file-component-4.vue:2:8:2:21 | v-html=dataA |
| Unit | single-file-component-5.vue:13:14:13:15 | 42 | single-file-component-5.vue:2:8:2:21 | v-html=dataA |
| Unit | tst.js:100:18:100:19 | 42 | tst.js:102:20:102:29 | this.dataA |
| tst.js:107:12:109:2 | Vue.ext ... 23 }\\n}) |
| tst.js:110:16:112:2 | new Vue ... base\\n}) |
| tst.js:113:17:117:2 | base.ex ... 0\\n\\t}\\n}) |
viewComponentStep
| compont-with-route.vue:31:14:31:34 | this.$r ... ery.foo | compont-with-route.vue:2:8:2:21 | v-html=dataA |
| single-component-file-1.vue:6:40:6:41 | 42 | single-component-file-1.vue:2:8:2:21 | v-html=dataA |
| single-file-component-3-script.js:4:37:4:38 | 42 | single-file-component-3.vue:2:8:2:21 | v-html=dataA |
| single-file-component-4.vue:15:14:15:15 | 42 | single-file-component-4.vue:2:8:2:21 | v-html=dataA |
| single-file-component-5.vue:13:14:13:15 | 42 | single-file-component-5.vue:2:8:2:21 | v-html=dataA |
| tst.js:100:18:100:19 | 42 | tst.js:102:20:102:29 | this.dataA |
templateElement
| compont-with-route.vue:1:1:3:11 | <template>...</> |
| compont-with-route.vue:2:5:51:9 | <p>...</> |
@@ -112,13 +126,6 @@ templateElement
| single-file-component-5.vue:2:5:18:9 | <p>...</> |
| single-file-component-5.vue:4:1:16:9 | <script>...</> |
| single-file-component-5.vue:17:1:18:8 | <style>...</> |
vhtmlSourceWrite
| Unit | compont-with-route.vue:31:14:31:34 | this.$r ... ery.foo | compont-with-route.vue:2:8:2:21 | v-html=dataA |
| Unit | single-component-file-1.vue:6:40:6:41 | 42 | single-component-file-1.vue:2:8:2:21 | v-html=dataA |
| Unit | single-file-component-3-script.js:4:37:4:38 | 42 | single-file-component-3.vue:2:8:2:21 | v-html=dataA |
| Unit | single-file-component-4.vue:15:14:15:15 | 42 | single-file-component-4.vue:2:8:2:21 | v-html=dataA |
| Unit | single-file-component-5.vue:13:14:13:15 | 42 | single-file-component-5.vue:2:8:2:21 | v-html=dataA |
| Unit | tst.js:100:18:100:19 | 42 | tst.js:102:20:102:29 | this.dataA |
xssSink
| compont-with-route.vue:2:8:2:21 | v-html=dataA |
| single-component-file-1.vue:2:8:2:21 | v-html=dataA |

View File

@@ -11,18 +11,12 @@ query predicate component_getOption(Vue::Component c, string name, DataFlow::Nod
query predicate component(Vue::Component c) { any() }
query predicate instance_heapStep(
Vue::InstanceHeapStep step, DataFlow::Node pred, DataFlow::Node succ
) {
step.step(pred, succ)
query predicate viewComponentStep(DataFlow::Node pred, DataFlow::Node succ) {
TaintTracking::viewComponentStep(pred, succ)
}
query predicate templateElement(Vue::Template::Element template) { any() }
query predicate vhtmlSourceWrite(Vue::VHtmlSourceWrite w, DataFlow::Node pred, DataFlow::Node succ) {
w.step(pred, succ)
}
query predicate xssSink(DomBasedXss::Sink s) { any() }
query RemoteFlowSource remoteFlowSource() { any() }

View File

@@ -103,3 +103,15 @@ new Vue({
}
});
});
let base = Vue.extend({
data: { fromBase: 123 }
});
let subclass = new Vue({
extends: base
});
let subclass2 = base.extend({
data: {
fromSubclass2: 100
}
});