Merge pull request #6511 from asgerf/js/vue-component-renaming

Approved by erik-krogh
This commit is contained in:
CodeQL CI
2021-08-19 12:57:15 +01:00
committed by GitHub
7 changed files with 72 additions and 61 deletions

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* The class `Vue::Instance` has been renamed to `Vue::Component`.

View File

@@ -11,7 +11,7 @@
import javascript
from Vue::Instance instance, DataFlow::Node def, DataFlow::FunctionNode arrow, ThisExpr dis
from Vue::Component instance, DataFlow::Node def, DataFlow::FunctionNode arrow, ThisExpr dis
where
instance.getABoundFunction() = def and
arrow.flowsTo(def) and

View File

@@ -21,13 +21,13 @@ module Vue {
VueExtend() { this = vue().getAMemberCall("extend") }
}
private newtype TInstance =
private newtype TComponent =
MkVueInstance(DataFlow::NewNode def) { def = vue().getAnInstantiation() } or
MkExtendedVue(VueExtend extend) or
MkExtendedInstance(VueExtend extend, DataFlow::NewNode sub) {
sub = extend.getAnInstantiation()
} or
MkComponent(DataFlow::CallNode def) { def = vue().getAMemberCall("component") } or
MkComponentRegistration(DataFlow::CallNode def) { def = vue().getAMemberCall("component") } or
MkSingleFileComponent(VueFile file)
/** Gets the name of a lifecycle hook method. */
@@ -82,22 +82,31 @@ module Vue {
}
/**
* A Vue instance definition.
* DEPRECATED. This class has been renamed to `Vue::Component`.
*/
deprecated class Instance = Component;
/**
* A Vue component, such as a `new Vue({ ... })` call or a `.vue` file.
*
* This includes both explicit instantiations of Vue objects, and
* implicit instantiations in the form of components or Vue
* extensions that have not yet been instantiated to a Vue instance.
* Generally speaking, a component is always created by calling `Vue.extend()` or
* calling `extend` on another component.
* Often the `Vue.extend()` call is performed by the Vue
* framework, however, so the call is not always visible in the user code.
* For instance, `new Vue(obj)` is shorthand for `new (Vue.extend(obj))`.
*
* The following instances are recognized:
* This class covers both the explicit `Vue.extend()` calls an those implicit in the framework.
*
* The following types of components are recognized:
* - `new Vue({...})`
* - `Vue.extend({...})`
* - `new ExtendedVue({...})`
* - `Vue.component("my-component", {...})`
* - single file components in .vue files
*/
abstract class Instance extends TInstance {
class Component extends TComponent {
/** Gets a textual representation of this element. */
abstract string toString();
string toString() { none() } // overridden in subclasses
/**
* Holds if this element is at the specified location.
@@ -120,18 +129,18 @@ module Vue {
* 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.
*/
abstract DataFlow::Node getOwnOptionsObject();
DataFlow::Node getOwnOptionsObject() { none() } // overridden in subclasses
/**
* Gets the class component implementing this Vue instance, if any.
* Gets the class implementing this Vue component, if any.
*
* Specifically, this is a class annotated with `@Component` which flows to the options
* object of this Vue instance.
* object of this Vue component.
*/
ClassComponent getAsClassComponent() { result.flowsTo(getOwnOptionsObject()) }
/**
* Gets the node for option `name` for this instance, this does not include
* Gets the node for option `name` for this component, not including
* those from extended objects and mixins.
*/
DataFlow::Node getOwnOption(string name) {
@@ -139,7 +148,7 @@ module Vue {
}
/**
* Gets the node for option `name` for this instance, including those from
* Gets the node for option `name` for this component, including those from
* extended objects and mixins.
*/
DataFlow::Node getOption(string name) {
@@ -164,25 +173,25 @@ module Vue {
}
/**
* Gets a source node flowing into the option `name` of this instance, including those from
* 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() }
/**
* Gets the template element used by this instance, if any.
* Gets the template element used by this component, if any.
*/
abstract Template::Element getTemplateElement();
Template::Element getTemplateElement() { none() } // overridden in subclasses
/**
* Gets the node for the `data` option object of this instance.
* 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 instance definitions
// a constructor variant is available for all component definitions
exists(DataFlow::FunctionNode f |
f.flowsTo(data) and
result = f.getAReturn()
@@ -195,13 +204,13 @@ module Vue {
}
/**
* Gets the node for the `template` option of this instance.
* Gets the node for the `template` option of this component.
*/
pragma[nomagic]
DataFlow::SourceNode getTemplate() { result = getOptionSource("template") }
/**
* Gets the node for the `render` option of this instance.
* Gets the node for the `render` option of this component.
*/
pragma[nomagic]
DataFlow::SourceNode getRender() {
@@ -211,19 +220,19 @@ module Vue {
}
/**
* Gets the node for the `methods` option of this instance.
* Gets the node for the `methods` option of this component.
*/
pragma[nomagic]
DataFlow::SourceNode getMethods() { result = getOptionSource("methods") }
/**
* Gets the node for the `computed` option of this instance.
* Gets the node for the `computed` option of this component.
*/
pragma[nomagic]
DataFlow::SourceNode getComputed() { result = getOptionSource("computed") }
/**
* Gets the node for the `watch` option of this instance.
* Gets the node for the `watch` option of this component.
*/
pragma[nomagic]
DataFlow::SourceNode getWatch() { result = getOptionSource("watch") }
@@ -240,7 +249,7 @@ module Vue {
}
/**
* Gets a node for a member of the `methods` option of this instance.
* Gets a node for a member of the `methods` option of this component.
*/
pragma[nomagic]
private DataFlow::SourceNode getAMethod() {
@@ -251,7 +260,7 @@ module Vue {
}
/**
* Gets a node for a member of the `computed` option of this instance that matches `kind`.
* 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) {
@@ -264,7 +273,7 @@ module Vue {
}
/**
* Gets a node for a member `name` of the `computed` option of this instance that matches `kind`.
* Gets a node for a member `name` of the `computed` option of this component that matches `kind`.
*/
private DataFlow::SourceNode getAccessor(string name, DataFlow::MemberKind kind) {
result = getComputed().getAPropertySource(name) and kind = DataFlow::MemberKind::getter()
@@ -276,7 +285,7 @@ module Vue {
}
/**
* Gets the node for the life cycle hook of the `hookName` option of this instance.
* Gets the node for the life cycle hook of the `hookName` option of this component.
*/
pragma[nomagic]
DataFlow::SourceNode getALifecycleHook(string hookName) {
@@ -289,7 +298,7 @@ module Vue {
}
/**
* Gets a node for a function that will be invoked with `this` bound to this instance.
* Gets a node for a function that will be invoked with `this` bound to this component.
*/
DataFlow::FunctionNode getABoundFunction() {
result = getAMethod()
@@ -316,7 +325,7 @@ module Vue {
}
/**
* Gets the data flow node that flows into the property `name` of this instance, or is
* Gets the data flow node that flows into the property `name` of this component, or is
* returned form a getter defining that property.
*/
DataFlow::Node getAPropertyValue(string name) {
@@ -330,9 +339,9 @@ module Vue {
}
/**
* A Vue instance from `new Vue({...})`.
* A Vue component from `new Vue({...})`.
*/
class VueInstance extends Instance, MkVueInstance {
class VueInstance extends Component, MkVueInstance {
DataFlow::NewNode def;
VueInstance() { this = MkVueInstance(def) }
@@ -353,7 +362,7 @@ module Vue {
/**
* An extended Vue from `Vue.extend({...})`.
*/
class ExtendedVue extends Instance, MkExtendedVue {
class ExtendedVue extends Component, MkExtendedVue {
VueExtend extend;
ExtendedVue() { this = MkExtendedVue(extend) }
@@ -374,7 +383,7 @@ module Vue {
/**
* An instance of an extended Vue, for example `instance` of `var Ext = Vue.extend({...}); var instance = new Ext({...})`.
*/
class ExtendedInstance extends Instance, MkExtendedInstance {
class ExtendedInstance extends Component, MkExtendedInstance {
VueExtend extend;
DataFlow::NewNode sub;
@@ -391,7 +400,7 @@ module Vue {
override DataFlow::Node getOwnOptionsObject() { result = sub.getArgument(0) }
override DataFlow::Node getOption(string name) {
result = Instance.super.getOption(name)
result = Component.super.getOption(name)
or
result = MkExtendedVue(extend).(ExtendedVue).getOption(name)
}
@@ -402,10 +411,10 @@ module Vue {
/**
* A Vue component from `Vue.component("my-component", { ... })`.
*/
class Component extends Instance, MkComponent {
class ComponentRegistration extends Component, MkComponentRegistration {
DataFlow::CallNode def;
Component() { this = MkComponent(def) }
ComponentRegistration() { this = MkComponentRegistration(def) }
override string toString() { result = def.toString() }
@@ -423,7 +432,7 @@ module Vue {
/**
* A single file Vue component in a `.vue` file.
*/
class SingleFileComponent extends Instance, MkSingleFileComponent {
class SingleFileComponent extends Component, MkSingleFileComponent {
VueFile file;
SingleFileComponent() { this = MkSingleFileComponent(file) }
@@ -496,7 +505,7 @@ module Vue {
*/
class InstanceHeapStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(Instance i, string name, DataFlow::FunctionNode bound |
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
@@ -531,13 +540,13 @@ module Vue {
*/
class VHtmlSourceWrite extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(Vue::Instance instance, string expr, VHtmlAttribute attr |
exists(Vue::Component component, string expr, VHtmlAttribute attr |
attr.getAttr().getRoot() =
instance.getTemplateElement().(Vue::Template::HtmlElement).getElement() and
component.getTemplateElement().(Vue::Template::HtmlElement).getElement() and
expr = attr.getAttr().getValue() and
// only support for simple identifier expressions
expr.regexpMatch("(?i)[a-z0-9_]+") and
pred = instance.getAPropertyValue(expr) and
pred = component.getAPropertyValue(expr) and
succ = attr
)
}
@@ -638,15 +647,15 @@ module Vue {
or
result = routeConfig().getMember("beforeEnter").getParameter([0, 1]).getAnImmediateUse()
or
exists(Instance i |
result = i.getABoundFunction().getAFunctionValue().getReceiver().getAPropertyRead("$route")
exists(Component c |
result = c.getABoundFunction().getAFunctionValue().getReceiver().getAPropertyRead("$route")
or
result =
i.getALifecycleHook(["beforeRouteEnter", "beforeRouteUpdate", "beforeRouteLeave"])
c.getALifecycleHook(["beforeRouteEnter", "beforeRouteUpdate", "beforeRouteLeave"])
.getAFunctionValue()
.getParameter([0, 1])
or
result = i.getWatchHandler("$route").getParameter([0, 1])
result = c.getWatchHandler("$route").getParameter([0, 1])
)
)
or
@@ -664,7 +673,7 @@ module Vue {
this = routeObject().getAPropertyRead(name)
or
exists(string prop |
this = any(Instance i).getWatchHandler(prop).getParameter([0, 1]) and
this = any(Component c).getWatchHandler(prop).getParameter([0, 1]) and
name = prop.regexpCapture("\\$route\\.(params|query|hash|path|fullPath)\\b.*", 1)
)
|

View File

@@ -26,7 +26,7 @@ private module VueAPI {
* or equivalent.
*/
class VueConfigObject extends API::Node {
VueConfigObject() { this.getARhs() = any(Vue::Instance i).getOwnOptionsObject() }
VueConfigObject() { this.getARhs() = any(Vue::Component c).getOwnOptionsObject() }
/** Gets an API node representing `this` in the Vue component. */
API::Node getAnInstanceRef() {

View File

@@ -346,8 +346,8 @@ module DomBasedXss {
*/
class VueTemplateSink extends DomBasedXss::Sink {
VueTemplateSink() {
// Note: don't use Vue::Instance#getTemplate as it includes an unwanted getALocalSource() step
this = any(Vue::Instance i).getOption("template")
// Note: don't use Vue::Component#getTemplate as it includes an unwanted getALocalSource() step
this = any(Vue::Component c).getOption("template")
}
}
@@ -357,8 +357,8 @@ module DomBasedXss {
*/
class VueCreateElementSink extends DomBasedXss::Sink {
VueCreateElementSink() {
exists(Vue::Instance i, DataFlow::FunctionNode f |
f.flowsTo(i.getRender()) and
exists(Vue::Component c, DataFlow::FunctionNode f |
f.flowsTo(c.getRender()) and
this = f.getParameter(0).getACall().getArgument(0)
)
}

View File

@@ -1,4 +1,4 @@
instance_getAPropertyValue
component_getAPropertyValue
| compont-with-route.vue:0:0:0:0 | compont-with-route.vue | dataA | compont-with-route.vue:31:14:31:34 | this.$r ... ery.foo |
| compont-with-route.vue:0:0:0:0 | compont-with-route.vue | message | compont-with-route.vue:19:15:19:22 | 'Hello!' |
| single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue | dataA | single-component-file-1.vue:6:40:6:41 | 42 |
@@ -28,7 +28,7 @@ instance_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 |
instance_getOption
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 } } |
| single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue | data | single-file-component-3-script.js:4:8:4:42 | functio ... 42 } } |
@@ -58,7 +58,7 @@ instance_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} |
instance
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 |
| single-file-component-2.vue:0:0:0:0 | single-file-component-2.vue |

View File

@@ -1,15 +1,15 @@
import javascript
import semmle.javascript.security.dataflow.Xss
query predicate instance_getAPropertyValue(Vue::Instance i, string name, DataFlow::Node prop) {
i.getAPropertyValue(name) = prop
query predicate component_getAPropertyValue(Vue::Component c, string name, DataFlow::Node prop) {
c.getAPropertyValue(name) = prop
}
query predicate instance_getOption(Vue::Instance i, string name, DataFlow::Node prop) {
i.getOption(name) = prop
query predicate component_getOption(Vue::Component c, string name, DataFlow::Node prop) {
c.getOption(name) = prop
}
query predicate instance(Vue::Instance i) { any() }
query predicate component(Vue::Component c) { any() }
query predicate instance_heapStep(
Vue::InstanceHeapStep step, DataFlow::Node pred, DataFlow::Node succ