mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge pull request #18466 from asgerf/js/view-component-inputs
JS: Add view-component-input threat model
This commit is contained in:
@@ -15,5 +15,6 @@ The less commonly used categories are:
|
||||
- ``database-access-result`` which represents a database access. Currently only used by JavaScript.
|
||||
- ``file-write`` which represents opening a file in write mode. Currently only used in C#.
|
||||
- ``reverse-dns`` which represents reverse DNS lookups. Currently only used in Java.
|
||||
- ``view-component-input`` which represents inputs to a React, Vue, or Angular component (also known as "props"). Currently only used by JavaScript/TypeScript.
|
||||
|
||||
When running a CodeQL analysis, the ``remote`` threat model is included by default. You can optionally include other threat models as appropriate when using the CodeQL CLI and in GitHub code scanning. For more information, see `Analyzing your code with CodeQL queries <https://docs.github.com/code-security/codeql-cli/getting-started-with-the-codeql-cli/analyzing-your-code-with-codeql-queries#including-model-packs-to-add-potential-sources-of-tainted-data>`__ and `Customizing your advanced setup for code scanning <https://docs.github.com/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#extending-codeql-coverage-with-threat-models>`__.
|
||||
|
||||
@@ -26,6 +26,11 @@ class ThreatModelSource extends DataFlow::Node instanceof ThreatModelSource::Ran
|
||||
|
||||
/** Gets a string that describes the type of this threat-model source. */
|
||||
string getSourceType() { result = super.getSourceType() }
|
||||
|
||||
/**
|
||||
* Holds if this is a source of data that is specific to the web browser environment.
|
||||
*/
|
||||
predicate isClientSideSource() { super.isClientSideSource() }
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new sources for specific threat-models. */
|
||||
@@ -48,6 +53,11 @@ module ThreatModelSource {
|
||||
|
||||
/** Gets a string that describes the type of this threat-model source. */
|
||||
abstract string getSourceType();
|
||||
|
||||
/**
|
||||
* Holds if this is a source of data that is specific to the web browser environment.
|
||||
*/
|
||||
predicate isClientSideSource() { this.getThreatModel() = "view-component-input" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
47
javascript/ql/lib/semmle/javascript/ViewComponentInput.qll
Normal file
47
javascript/ql/lib/semmle/javascript/ViewComponentInput.qll
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Provides a classes and predicates for contributing to the `view-component-input` threat model.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
|
||||
/**
|
||||
* An input to a view component, such as React props.
|
||||
*/
|
||||
abstract class ViewComponentInput extends DataFlow::Node {
|
||||
/** Gets a string that describes the type of this threat-model source. */
|
||||
abstract string getSourceType();
|
||||
}
|
||||
|
||||
private class ViewComponentInputAsThreatModelSource extends ThreatModelSource::Range instanceof ViewComponentInput
|
||||
{
|
||||
ViewComponentInputAsThreatModelSource() { not isSafeType(this.asExpr().getType()) }
|
||||
|
||||
final override string getThreatModel() { result = "view-component-input" }
|
||||
|
||||
final override string getSourceType() { result = ViewComponentInput.super.getSourceType() }
|
||||
}
|
||||
|
||||
private predicate isSafeType(Type t) {
|
||||
t instanceof NumberLikeType
|
||||
or
|
||||
t instanceof BooleanLikeType
|
||||
or
|
||||
t instanceof UndefinedType
|
||||
or
|
||||
t instanceof NullType
|
||||
or
|
||||
t instanceof VoidType
|
||||
or
|
||||
hasSafeTypes(t, t.(UnionType).getNumElementType())
|
||||
or
|
||||
isSafeType(t.(IntersectionType).getAnElementType())
|
||||
}
|
||||
|
||||
/** Hold if the first `n` components of `t` are safe types. */
|
||||
private predicate hasSafeTypes(UnionType t, int n) {
|
||||
isSafeType(t.getElementType(0)) and
|
||||
n = 1
|
||||
or
|
||||
isSafeType(t.getElementType(n - 1)) and
|
||||
hasSafeTypes(t, n - 1)
|
||||
}
|
||||
@@ -8,6 +8,7 @@ private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations
|
||||
private import semmle.javascript.DynamicPropertyAccess
|
||||
private import semmle.javascript.dataflow.internal.PreCallGraphStep
|
||||
private import semmle.javascript.ViewComponentInput
|
||||
|
||||
/**
|
||||
* Provides classes for working with Angular (also known as Angular 2.x) applications.
|
||||
@@ -575,4 +576,17 @@ module Angular2 {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class InputFieldAsViewComponentInput extends ViewComponentInput {
|
||||
InputFieldAsViewComponentInput() {
|
||||
this =
|
||||
API::moduleImport("@angular/core")
|
||||
.getMember("Input")
|
||||
.getReturn()
|
||||
.getADecoratedMember()
|
||||
.asSource()
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Angular component input field" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
|
||||
private import semmle.javascript.dataflow.internal.PreCallGraphStep
|
||||
private import semmle.javascript.ViewComponentInput
|
||||
|
||||
/**
|
||||
* Gets a reference to the 'React' object.
|
||||
@@ -868,3 +869,9 @@ private class PropsFlowStep extends PreCallGraphStep {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ReactPropAsViewComponentInput extends ViewComponentInput {
|
||||
ReactPropAsViewComponentInput() { this = any(ReactComponent c).getADirectPropsAccess() }
|
||||
|
||||
override string getSourceType() { result = "React props" }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.ViewComponentInput
|
||||
|
||||
module Vue {
|
||||
/** The global variable `Vue`, as an API graph entry point. */
|
||||
@@ -85,17 +86,16 @@ module Vue {
|
||||
* A class with a `@Component` decorator, making it usable as an "options" object in Vue.
|
||||
*/
|
||||
class ClassComponent extends DataFlow::ClassNode {
|
||||
private ClassDefinition cls;
|
||||
DataFlow::Node decorator;
|
||||
|
||||
ClassComponent() {
|
||||
exists(ClassDefinition cls |
|
||||
this = cls.flow() and
|
||||
cls.getADecorator().getExpression() = decorator.asExpr() and
|
||||
(
|
||||
componentDecorator().flowsTo(decorator)
|
||||
or
|
||||
componentDecorator().getACall() = decorator
|
||||
)
|
||||
this = cls.flow() and
|
||||
cls.getADecorator().getExpression() = decorator.asExpr() and
|
||||
(
|
||||
componentDecorator().flowsTo(decorator)
|
||||
or
|
||||
componentDecorator().getACall() = decorator
|
||||
)
|
||||
}
|
||||
|
||||
@@ -105,6 +105,9 @@ module Vue {
|
||||
* These options correspond to the options one would pass to `new Vue({...})` or similar.
|
||||
*/
|
||||
API::Node getDecoratorOptions() { result = decorator.(API::CallNode).getParameter(0) }
|
||||
|
||||
/** Gets the AST node for the class definition. */
|
||||
ClassDefinition getClassDefinition() { result = cls }
|
||||
}
|
||||
|
||||
private string memberKindVerb(DataFlow::MemberKind kind) {
|
||||
@@ -460,6 +463,12 @@ module Vue {
|
||||
|
||||
SingleFileComponent() { this = MkSingleFileComponent(file) }
|
||||
|
||||
/** Gets a call to `defineProps` in this component. */
|
||||
DataFlow::CallNode getDefinePropsCall() {
|
||||
result = DataFlow::globalVarRef("defineProps").getACall() and
|
||||
result.getFile() = file
|
||||
}
|
||||
|
||||
override Template::Element getTemplateElement() {
|
||||
exists(HTML::Element e | result.(Template::HtmlElement).getElement() = e |
|
||||
e.getFile() = file and
|
||||
@@ -697,4 +706,68 @@ module Vue {
|
||||
|
||||
override ClientSideRemoteFlowKind getKind() { result = kind }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given type annotation indicates a value that is not typically considered taintable.
|
||||
*/
|
||||
private predicate isSafeType(TypeAnnotation type) {
|
||||
type.isBooleany() or
|
||||
type.isNumbery() or
|
||||
type.isRawFunction() or
|
||||
type instanceof FunctionTypeExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given field has a type that indicates that is can not contain a taintable value.
|
||||
*/
|
||||
private predicate isSafeField(FieldDeclaration field) { isSafeType(field.getTypeAnnotation()) }
|
||||
|
||||
private DataFlow::Node getPropSpec(Component component) {
|
||||
result = component.getOption("props")
|
||||
or
|
||||
result = component.(SingleFileComponent).getDefinePropsCall().getArgument(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `component` has an input prop with the given name, that is of a taintable type.
|
||||
*/
|
||||
private predicate hasTaintableProp(Component component, string name) {
|
||||
exists(DataFlow::SourceNode spec | spec = getPropSpec(component).getALocalSource() |
|
||||
spec.(DataFlow::ArrayCreationNode).getAnElement().getStringValue() = name
|
||||
or
|
||||
exists(DataFlow::PropWrite write |
|
||||
write = spec.getAPropertyWrite(name) and
|
||||
not DataFlow::globalVarRef(["Number", "Boolean"]).flowsTo(write.getRhs())
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(FieldDeclaration field |
|
||||
field = component.getAsClassComponent().getClassDefinition().getField(name) and
|
||||
DataFlow::moduleMember("vue-property-decorator", "Prop")
|
||||
.getACall()
|
||||
.flowsToExpr(field.getADecorator().getExpression()) and
|
||||
not isSafeField(field)
|
||||
)
|
||||
or
|
||||
// defineProps() can be called with only type arguments and then the Vue compiler will
|
||||
// infer the prop types.
|
||||
exists(CallExpr call, FieldDeclaration field |
|
||||
call = component.(SingleFileComponent).getDefinePropsCall().asExpr() and
|
||||
field = call.getTypeArgument(0).(InterfaceTypeExpr).getMember(name) and
|
||||
not isSafeField(field)
|
||||
)
|
||||
}
|
||||
|
||||
private class PropAsViewComponentInput extends ViewComponentInput {
|
||||
PropAsViewComponentInput() {
|
||||
exists(Component component, string name | hasTaintableProp(component, name) |
|
||||
this = component.getAnInstanceRef().getAPropertyRead(name)
|
||||
or
|
||||
// defineProps() returns the props
|
||||
this = component.(SingleFileComponent).getDefinePropsCall().getAPropertyRead(name)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Vue prop" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ module CommandInjection {
|
||||
* An active threat-model source, considered as a flow source.
|
||||
*/
|
||||
private class ActiveThreatModelSourceAsSource extends Source instanceof ActiveThreatModelSource {
|
||||
ActiveThreatModelSourceAsSource() { not this instanceof ClientSideRemoteFlowSource }
|
||||
ActiveThreatModelSourceAsSource() { not this.isClientSideSource() }
|
||||
|
||||
override string getSourceType() { result = "a user-provided value" }
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ module CorsMisconfigurationForCredentials {
|
||||
* An active threat-model source, considered as a flow source.
|
||||
*/
|
||||
private class ActiveThreatModelSourceAsSource extends Source instanceof ActiveThreatModelSource {
|
||||
ActiveThreatModelSourceAsSource() { not this instanceof ClientSideRemoteFlowSource }
|
||||
ActiveThreatModelSourceAsSource() { not this.isClientSideSource() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,7 +54,7 @@ deprecated class LogInjectionConfiguration extends TaintTracking::Configuration
|
||||
* A source of remote user controlled input.
|
||||
*/
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource {
|
||||
RemoteSource() { not this instanceof ClientSideRemoteFlowSource }
|
||||
RemoteSource() { not this.isClientSideSource() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,7 +34,7 @@ module RegExpInjection {
|
||||
* An active threat-model source, considered as a flow source.
|
||||
*/
|
||||
private class ActiveThreatModelSourceAsSource extends Source instanceof ActiveThreatModelSource {
|
||||
ActiveThreatModelSourceAsSource() { not this instanceof ClientSideRemoteFlowSource }
|
||||
ActiveThreatModelSourceAsSource() { not this.isClientSideSource() }
|
||||
}
|
||||
|
||||
private import IndirectCommandInjectionCustomizations
|
||||
|
||||
@@ -24,12 +24,18 @@ private module Cached {
|
||||
|
||||
/**
|
||||
* A source of remote input in a web browser environment.
|
||||
*
|
||||
* Note that this does not include `view-component-input` sources even if that threat model has been enabled by the user.
|
||||
* Consider using the predicate `ThreatModelSource#isClientSideSource()` to check for a broader class of client-side sources.
|
||||
*/
|
||||
cached
|
||||
abstract class ClientSideRemoteFlowSource extends RemoteFlowSource {
|
||||
/** Gets a string indicating what part of the browser environment this was derived from. */
|
||||
cached
|
||||
abstract ClientSideRemoteFlowKind getKind();
|
||||
|
||||
cached
|
||||
final override predicate isClientSideSource() { any() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ module RequestForgery {
|
||||
not this.(ClientSideRemoteFlowSource).getKind().isPathOrUrl()
|
||||
}
|
||||
|
||||
override predicate isServerSide() { not this instanceof ClientSideRemoteFlowSource }
|
||||
override predicate isServerSide() { not super.isClientSideSource() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,7 +63,7 @@ module ResourceExhaustion {
|
||||
private class ActiveThreatModelSourceAsSource extends Source instanceof ActiveThreatModelSource {
|
||||
ActiveThreatModelSourceAsSource() {
|
||||
// exclude source that only happen client-side
|
||||
not this instanceof ClientSideRemoteFlowSource and
|
||||
not this.isClientSideSource() and
|
||||
not this = DataFlow::parameterNode(any(PostMessageEventHandler pmeh).getEventParameter())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,7 +719,7 @@ module TaintedPath {
|
||||
* An active threat-model source, considered as a flow source.
|
||||
*/
|
||||
private class ActiveThreatModelSourceAsSource extends Source instanceof ActiveThreatModelSource {
|
||||
ActiveThreatModelSourceAsSource() { not this instanceof ClientSideRemoteFlowSource }
|
||||
ActiveThreatModelSourceAsSource() { not this.isClientSideSource() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* Added a new threat model kind called `view-component-input`, which can enabled with [advanced setup](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#extending-codeql-coverage-with-threat-models).
|
||||
When enabled, all React props, Vue props, and input fields in an Angular component are seen as taint sources, even if none of the corresponding instantiation sites appear to pass in a tainted value.
|
||||
Some users may prefer this as a "defense in depth" option but note that it may result in false positives.
|
||||
Regardless of whether the threat model is enabled, CodeQL will propagate taint from the instantiation sites of such components into the components themselves.
|
||||
@@ -11,13 +11,6 @@
|
||||
import javascript
|
||||
import meta.internal.TaintMetrics
|
||||
|
||||
string getName(DataFlow::Node node) {
|
||||
result = node.(RemoteFlowSource).getSourceType()
|
||||
or
|
||||
not node instanceof RemoteFlowSource and
|
||||
result = "Taint source"
|
||||
}
|
||||
|
||||
from DataFlow::Node node
|
||||
where node = relevantTaintSource()
|
||||
select node, getName(node)
|
||||
from ThreatModelSource node
|
||||
where node = relevantTaintSource() and node.getThreatModel() = "remote"
|
||||
select node, getTaintSourceName(node)
|
||||
|
||||
19
javascript/ql/src/meta/alerts/ThreatModelSources.ql
Normal file
19
javascript/ql/src/meta/alerts/ThreatModelSources.ql
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @name Threat model sources
|
||||
* @description Sources of possibly untrusted input that can be configured via threat models.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id js/meta/alerts/threat-model-sources
|
||||
* @tags meta
|
||||
* @precision very-low
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import meta.internal.TaintMetrics
|
||||
|
||||
from ThreatModelSource node, string threatModel
|
||||
where
|
||||
node = relevantTaintSource() and
|
||||
threatModel = node.getThreatModel() and
|
||||
threatModel != "remote" // "remote" is reported by TaintSources.ql
|
||||
select node, getTaintSourceName(node) + " (\"" + threatModel + "\" threat model)"
|
||||
@@ -75,9 +75,9 @@ DataFlow::Node relevantTaintSink(string kind) {
|
||||
DataFlow::Node relevantTaintSink() { result = relevantTaintSink(_) }
|
||||
|
||||
/**
|
||||
* Gets a relevant remote flow source.
|
||||
* Gets a relevant threat model source.
|
||||
*/
|
||||
RemoteFlowSource relevantTaintSource() { not result.getFile() instanceof IgnoredFile }
|
||||
ThreatModelSource relevantTaintSource() { not result.getFile() instanceof IgnoredFile }
|
||||
|
||||
/**
|
||||
* Gets the output of a call that shows intent to sanitize a value
|
||||
@@ -100,3 +100,10 @@ DataFlow::Node relevantSanitizerInput() {
|
||||
result = any(HtmlSanitizerCall call).getInput() and
|
||||
not result.getFile() instanceof IgnoredFile
|
||||
}
|
||||
|
||||
string getTaintSourceName(DataFlow::Node node) {
|
||||
result = node.(ThreatModelSource).getSourceType()
|
||||
or
|
||||
not node instanceof ThreatModelSource and
|
||||
result = "Taint source"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
@Component({
|
||||
@@ -6,17 +6,17 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
template: "not important"
|
||||
})
|
||||
export class SinkComponent {
|
||||
sink1: string;
|
||||
sink2: string;
|
||||
sink3: string;
|
||||
sink4: string;
|
||||
sink5: string;
|
||||
sink6: string;
|
||||
sink7: string;
|
||||
sink8: string;
|
||||
sink9: string;
|
||||
@Input() sink1: string;
|
||||
@Input() sink2: string;
|
||||
@Input() sink3: string;
|
||||
@Input() sink4: string;
|
||||
@Input() sink5: string;
|
||||
@Input() sink6: string;
|
||||
@Input() sink7: string;
|
||||
@Input() sink8: string;
|
||||
@Input() sink9: string;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
constructor(private sanitizer: DomSanitizer) { }
|
||||
|
||||
foo() {
|
||||
this.sanitizer.bypassSecurityTrustHtml(this.sink1);
|
||||
|
||||
@@ -36,3 +36,13 @@ taintFlow
|
||||
| source.component.ts:16:33:16:40 | source() | sink.component.ts:22:48:22:57 | this.sink1 |
|
||||
testAttrSourceLocation
|
||||
| inline.component.ts:8:43:8:60 | [testAttr]=taint | inline.component.ts:8:55:8:59 | <toplevel> |
|
||||
threatModelSource
|
||||
| sink.component.ts:22:48:22:57 | this.sink1 | view-component-input |
|
||||
| sink.component.ts:23:48:23:57 | this.sink2 | view-component-input |
|
||||
| sink.component.ts:24:48:24:57 | this.sink3 | view-component-input |
|
||||
| sink.component.ts:25:48:25:57 | this.sink4 | view-component-input |
|
||||
| sink.component.ts:26:48:26:57 | this.sink5 | view-component-input |
|
||||
| sink.component.ts:27:48:27:57 | this.sink6 | view-component-input |
|
||||
| sink.component.ts:28:48:28:57 | this.sink7 | view-component-input |
|
||||
| sink.component.ts:29:48:29:57 | this.sink8 | view-component-input |
|
||||
| sink.component.ts:30:48:30:57 | this.sink9 | view-component-input |
|
||||
|
||||
@@ -42,3 +42,7 @@ deprecated class LegacyConfig extends TaintTracking::Configuration {
|
||||
}
|
||||
|
||||
deprecated import utils.test.LegacyDataFlowDiff::DataFlowDiff<TestFlow, LegacyConfig>
|
||||
|
||||
query predicate threatModelSource(ThreatModelSource source, string kind) {
|
||||
kind = source.getThreatModel()
|
||||
}
|
||||
|
||||
@@ -318,3 +318,28 @@ test_JsxName_this
|
||||
| thisAccesses.js:61:19:61:41 | <this.t ... s.this> | thisAccesses.js:61:20:61:23 | this |
|
||||
locationSource
|
||||
| importedComponent.jsx:3:32:3:39 | location |
|
||||
threatModelSource
|
||||
| es5.js:4:24:4:33 | this.props | view-component-input |
|
||||
| es5.js:20:24:20:33 | this.props | view-component-input |
|
||||
| es6.js:1:37:1:36 | args | view-component-input |
|
||||
| es6.js:3:24:3:33 | this.props | view-component-input |
|
||||
| exportedComponent.jsx:1:29:1:33 | props | view-component-input |
|
||||
| importedComponent.jsx:3:24:3:40 | {color, location} | view-component-input |
|
||||
| importedComponent.jsx:3:32:3:39 | location | remote |
|
||||
| namedImport.js:3:27:3:26 | args | view-component-input |
|
||||
| namedImport.js:5:19:5:18 | args | view-component-input |
|
||||
| plainfn.js:1:16:1:20 | props | view-component-input |
|
||||
| plainfn.js:5:17:5:21 | props | view-component-input |
|
||||
| plainfn.js:9:17:9:21 | props | view-component-input |
|
||||
| plainfn.js:20:28:20:32 | props | view-component-input |
|
||||
| preact.js:1:38:1:37 | args | view-component-input |
|
||||
| preact.js:2:12:2:16 | props | view-component-input |
|
||||
| preact.js:9:38:9:37 | args | view-component-input |
|
||||
| probably-a-component.js:1:31:1:30 | args | view-component-input |
|
||||
| probably-a-component.js:3:9:3:18 | this.props | view-component-input |
|
||||
| props.js:2:37:2:36 | args | view-component-input |
|
||||
| props.js:26:16:26:20 | props | view-component-input |
|
||||
| rare-lifecycle-methods.js:1:33:1:32 | args | view-component-input |
|
||||
| statePropertyWrites.js:38:24:38:33 | this.props | view-component-input |
|
||||
| thisAccesses.js:31:12:31:16 | props | view-component-input |
|
||||
| thisAccesses.js:48:18:48:18 | y | view-component-input |
|
||||
|
||||
@@ -11,3 +11,7 @@ import ReactComponent_getAPropRead
|
||||
import ReactName
|
||||
|
||||
query DataFlow::SourceNode locationSource() { result = DOM::locationSource() }
|
||||
|
||||
query predicate threatModelSource(ThreatModelSource source, string kind) {
|
||||
kind = source.getThreatModel()
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data: function() { return { dataA: 42 } }
|
||||
props: ['input'],
|
||||
data: function() { return { dataA: 42 + this.input } }
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
<script>
|
||||
var x = require('x');
|
||||
module.exports = { // not properly detected by the module system yet
|
||||
data: function() { return { dataA: 42 } }
|
||||
props: ['input'],
|
||||
data: function() { return { dataA: 42 + this.input } }
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var x = require('x');
|
||||
|
||||
module.exports = {
|
||||
data: function() { return { dataA: 42 } }
|
||||
props: ['input'],
|
||||
data: function() { return { dataA: 42 + this.input } }
|
||||
}
|
||||
|
||||
@@ -4,16 +4,22 @@
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import Component from 'vue-class-component'
|
||||
import { Prop } from 'vue-property-decorator'
|
||||
|
||||
@Component({
|
||||
render: (h) => { }
|
||||
})
|
||||
export default class MyComponent extends Vue {
|
||||
message: string = 'Hello!'
|
||||
@Prop() input: string;
|
||||
|
||||
get dataA() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
get dataB() {
|
||||
return this.input;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
@@ -4,14 +4,20 @@
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import Component from 'vue-class-component'
|
||||
import { Prop } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class MyComponent extends Vue {
|
||||
message: string = 'Hello!'
|
||||
@Prop() input: string;
|
||||
|
||||
get dataA() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
get dataB() {
|
||||
return this.input;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<p v-html="input"/>
|
||||
</template>
|
||||
<script setup>
|
||||
const { input } = defineProps(['input']);
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<p v-html="input"/>
|
||||
</template>
|
||||
<script setup>
|
||||
const { input, numericInput, booleanInput } = defineProps({
|
||||
input: String,
|
||||
numericInput: Number,
|
||||
booleanInput: Boolean,
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<p v-html="input"/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
const { input, numericInput, booleanInput } = defineProps<{
|
||||
input: string,
|
||||
numericInput: number,
|
||||
booleanInput: boolean,
|
||||
}>();
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
@@ -1,12 +1,14 @@
|
||||
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 |
|
||||
| single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue | dataA | single-file-component-3-script.js:4:37:4:38 | 42 |
|
||||
| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | dataA | single-file-component-4.vue:15:14:15:15 | 42 |
|
||||
| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | message | single-file-component-4.vue:12:23:12:30 | 'Hello!' |
|
||||
| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue | dataA | single-file-component-5.vue:13:14:13:15 | 42 |
|
||||
| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue | message | single-file-component-5.vue:10:23:10:30 | 'Hello!' |
|
||||
| single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue | dataA | single-component-file-1.vue:7:40:7:54 | 42 + this.input |
|
||||
| single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue | dataA | single-file-component-3-script.js:5:37:5:51 | 42 + this.input |
|
||||
| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | dataA | single-file-component-4.vue:17:14:17:15 | 42 |
|
||||
| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | dataB | single-file-component-4.vue:21:14:21:23 | this.input |
|
||||
| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | message | single-file-component-4.vue:13:23:13:30 | 'Hello!' |
|
||||
| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue | dataA | single-file-component-5.vue:15:14:15:15 | 42 |
|
||||
| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue | dataB | single-file-component-5.vue:19:14:19:23 | this.input |
|
||||
| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue | message | single-file-component-5.vue:11:23:11:30 | 'Hello!' |
|
||||
| tst.js:3:1:10:2 | new Vue ... 2\\n\\t}\\n}) | dataA | tst.js:8:10:8:11 | 42 |
|
||||
| tst.js:12:1:16:2 | new Vue ... \\t}),\\n}) | dataA | tst.js:14:10:14:11 | 42 |
|
||||
| tst.js:18:1:27:2 | Vue.com ... }\\n\\t}\\n}) | dataA | tst.js:20:10:20:11 | 42 |
|
||||
@@ -35,9 +37,11 @@ component_getAPropertyValue
|
||||
| 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 } } |
|
||||
| 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 } } |
|
||||
| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | render | single-file-component-4.vue:9:13:9:22 | (h) => { } |
|
||||
| single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue | data | single-component-file-1.vue:7:11:7:58 | functio ... put } } |
|
||||
| single-component-file-1.vue:0:0:0:0 | single-component-file-1.vue | props | single-component-file-1.vue:6:12:6:20 | ['input'] |
|
||||
| single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue | data | single-file-component-3-script.js:5:8:5:55 | functio ... put } } |
|
||||
| single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue | props | single-file-component-3-script.js:4:9:4:17 | ['input'] |
|
||||
| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue | render | single-file-component-4.vue:10:13:10:22 | (h) => { } |
|
||||
| tst.js:3:1:10:2 | new Vue ... 2\\n\\t}\\n}) | data | tst.js:7:8:9:2 | {\\n\\t\\tdataA: 42\\n\\t} |
|
||||
| tst.js:3:1:10:2 | new Vue ... 2\\n\\t}\\n}) | render | tst.js:4:10:6:2 | functio ... c);\\n\\t} |
|
||||
| tst.js:12:1:16:2 | new Vue ... \\t}),\\n}) | data | tst.js:13:8:15:3 | () => ( ... 42\\n\\t}) |
|
||||
@@ -76,6 +80,9 @@ component
|
||||
| single-file-component-3.vue:0:0:0:0 | single-file-component-3.vue |
|
||||
| single-file-component-4.vue:0:0:0:0 | single-file-component-4.vue |
|
||||
| single-file-component-5.vue:0:0:0:0 | single-file-component-5.vue |
|
||||
| single-file-component-6.vue:0:0:0:0 | single-file-component-6.vue |
|
||||
| single-file-component-7.vue:0:0:0:0 | single-file-component-7.vue |
|
||||
| single-file-component-8.vue:0:0:0:0 | single-file-component-8.vue |
|
||||
| special-syntax.vue:0:0:0:0 | special-syntax.vue |
|
||||
| tst.js:3:1:10:2 | new Vue ... 2\\n\\t}\\n}) |
|
||||
| tst.js:12:1:16:2 | new Vue ... \\t}),\\n}) |
|
||||
@@ -97,10 +104,10 @@ component
|
||||
| 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 |
|
||||
| single-component-file-1.vue:7:40:7:54 | 42 + this.input | single-component-file-1.vue:2:8:2:21 | v-html=dataA |
|
||||
| single-file-component-3-script.js:5:37:5:51 | 42 + this.input | single-file-component-3.vue:2:8:2:21 | v-html=dataA |
|
||||
| single-file-component-4.vue:17:14:17:15 | 42 | single-file-component-4.vue:2:8:2:21 | v-html=dataA |
|
||||
| single-file-component-5.vue:15:14:15: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>...</> |
|
||||
@@ -108,25 +115,37 @@ templateElement
|
||||
| compont-with-route.vue:4:1:49:9 | <script>...</> |
|
||||
| compont-with-route.vue:50:1:51:8 | <style>...</> |
|
||||
| single-component-file-1.vue:1:1:3:11 | <template>...</> |
|
||||
| single-component-file-1.vue:2:5:10:8 | <p>...</> |
|
||||
| single-component-file-1.vue:4:1:8:9 | <script>...</> |
|
||||
| single-component-file-1.vue:9:1:10:8 | <style>...</> |
|
||||
| single-component-file-1.vue:2:5:11:8 | <p>...</> |
|
||||
| single-component-file-1.vue:4:1:9:9 | <script>...</> |
|
||||
| single-component-file-1.vue:10:1:11:8 | <style>...</> |
|
||||
| single-file-component-2.vue:1:1:3:11 | <template>...</> |
|
||||
| single-file-component-2.vue:2:5:11:8 | <p>...</> |
|
||||
| single-file-component-2.vue:4:1:9:9 | <script>...</> |
|
||||
| single-file-component-2.vue:10:1:11:8 | <style>...</> |
|
||||
| single-file-component-2.vue:2:5:12:8 | <p>...</> |
|
||||
| single-file-component-2.vue:4:1:10:9 | <script>...</> |
|
||||
| single-file-component-2.vue:11:1:12:8 | <style>...</> |
|
||||
| single-file-component-3.vue:1:1:3:11 | <template>...</> |
|
||||
| single-file-component-3.vue:2:5:7:8 | <p>...</> |
|
||||
| single-file-component-3.vue:4:1:5:9 | <script>...</> |
|
||||
| single-file-component-3.vue:6:1:7:8 | <style>...</> |
|
||||
| single-file-component-4.vue:1:1:3:11 | <template>...</> |
|
||||
| single-file-component-4.vue:2:5:20:9 | <p>...</> |
|
||||
| single-file-component-4.vue:4:1:18:9 | <script>...</> |
|
||||
| single-file-component-4.vue:19:1:20:8 | <style>...</> |
|
||||
| single-file-component-4.vue:2:5:26:9 | <p>...</> |
|
||||
| single-file-component-4.vue:4:1:24:9 | <script>...</> |
|
||||
| single-file-component-4.vue:25:1:26:8 | <style>...</> |
|
||||
| single-file-component-5.vue:1:1:3:11 | <template>...</> |
|
||||
| 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>...</> |
|
||||
| single-file-component-5.vue:2:5:24:9 | <p>...</> |
|
||||
| single-file-component-5.vue:4:1:22:9 | <script>...</> |
|
||||
| single-file-component-5.vue:23:1:24:8 | <style>...</> |
|
||||
| single-file-component-6.vue:1:1:3:11 | <template>...</> |
|
||||
| single-file-component-6.vue:2:5:8:9 | <p>...</> |
|
||||
| single-file-component-6.vue:4:1:6:9 | <script>...</> |
|
||||
| single-file-component-6.vue:7:1:8:8 | <style>...</> |
|
||||
| single-file-component-7.vue:1:1:3:11 | <template>...</> |
|
||||
| single-file-component-7.vue:2:5:12:9 | <p>...</> |
|
||||
| single-file-component-7.vue:4:1:10:9 | <script>...</> |
|
||||
| single-file-component-7.vue:11:1:12:8 | <style>...</> |
|
||||
| single-file-component-8.vue:1:1:3:11 | <template>...</> |
|
||||
| single-file-component-8.vue:2:5:12:9 | <p>...</> |
|
||||
| single-file-component-8.vue:4:1:10:9 | <script>...</> |
|
||||
| single-file-component-8.vue:11:1:12:8 | <style>...</> |
|
||||
| special-syntax.vue:1:1:4:11 | <template>...</> |
|
||||
| special-syntax.vue:2:3:2:37 | <blah>...</> |
|
||||
| special-syntax.vue:3:3:3:32 | <blah>...</> |
|
||||
@@ -138,6 +157,9 @@ xssSink
|
||||
| single-file-component-3.vue:2:8:2:21 | v-html=dataA |
|
||||
| single-file-component-4.vue:2:8:2:21 | v-html=dataA |
|
||||
| single-file-component-5.vue:2:8:2:21 | v-html=dataA |
|
||||
| single-file-component-6.vue:2:8:2:21 | v-html=input |
|
||||
| single-file-component-7.vue:2:8:2:21 | v-html=input |
|
||||
| single-file-component-8.vue:2:8:2:21 | v-html=input |
|
||||
| tst.js:5:13:5:13 | a |
|
||||
| tst.js:38:12:38:17 | danger |
|
||||
remoteFlowSource
|
||||
@@ -175,6 +197,46 @@ attribute
|
||||
| single-file-component-3.vue:4:9:4:49 | src=./single-file-component-3-script.js | src |
|
||||
| single-file-component-4.vue:2:8:2:21 | v-html=dataA | v-html |
|
||||
| single-file-component-5.vue:2:8:2:21 | v-html=dataA | v-html |
|
||||
| single-file-component-6.vue:2:8:2:21 | v-html=input | v-html |
|
||||
| single-file-component-6.vue:4:9:4:13 | setup= | setup |
|
||||
| single-file-component-7.vue:2:8:2:21 | v-html=input | v-html |
|
||||
| single-file-component-7.vue:4:9:4:13 | setup= | setup |
|
||||
| single-file-component-8.vue:2:8:2:21 | v-html=input | v-html |
|
||||
| single-file-component-8.vue:4:9:4:13 | setup= | setup |
|
||||
| single-file-component-8.vue:4:15:4:23 | lang=ts | lang |
|
||||
| special-syntax.vue:2:9:2:22 | :colonProp=x | :colonProp |
|
||||
| special-syntax.vue:2:24:2:34 | @atProp=x | @atProp |
|
||||
| special-syntax.vue:3:9:3:29 | :colonField.field=x | :colonField.field |
|
||||
threatModelSource
|
||||
| compont-with-route.vue:13:17:13:21 | newId | remote |
|
||||
| compont-with-route.vue:13:24:13:28 | oldId | remote |
|
||||
| compont-with-route.vue:22:7:22:24 | this.$route.params | remote |
|
||||
| compont-with-route.vue:23:7:23:23 | this.$route.query | remote |
|
||||
| compont-with-route.vue:24:7:24:22 | this.$route.hash | remote |
|
||||
| compont-with-route.vue:25:7:25:22 | this.$route.path | remote |
|
||||
| compont-with-route.vue:26:7:26:26 | this.$route.fullPath | remote |
|
||||
| compont-with-route.vue:27:7:27:31 | router. ... e.query | remote |
|
||||
| compont-with-route.vue:31:14:31:30 | this.$route.query | remote |
|
||||
| compont-with-route.vue:35:7:35:14 | to.query | remote |
|
||||
| compont-with-route.vue:36:7:36:16 | from.query | remote |
|
||||
| compont-with-route.vue:40:7:40:14 | to.query | remote |
|
||||
| compont-with-route.vue:41:7:41:16 | from.query | remote |
|
||||
| compont-with-route.vue:45:7:45:14 | to.query | remote |
|
||||
| compont-with-route.vue:46:7:46:16 | from.query | remote |
|
||||
| router.js:8:17:8:24 | to.query | remote |
|
||||
| router.js:9:17:9:26 | from.query | remote |
|
||||
| router.js:15:25:15:32 | to.query | remote |
|
||||
| router.js:16:25:16:34 | from.query | remote |
|
||||
| router.js:23:9:23:16 | to.query | remote |
|
||||
| router.js:24:9:24:18 | from.query | remote |
|
||||
| router.js:29:5:29:12 | to.query | remote |
|
||||
| router.js:30:5:30:14 | from.query | remote |
|
||||
| router.js:34:5:34:12 | to.query | remote |
|
||||
| router.js:35:5:35:14 | from.query | remote |
|
||||
| single-component-file-1.vue:7:45:7:54 | this.input | view-component-input |
|
||||
| single-file-component-3-script.js:5:42:5:51 | this.input | view-component-input |
|
||||
| single-file-component-4.vue:21:14:21:23 | this.input | view-component-input |
|
||||
| single-file-component-5.vue:19:14:19:23 | this.input | view-component-input |
|
||||
| single-file-component-6.vue:5:11:5:15 | input | view-component-input |
|
||||
| single-file-component-7.vue:5:11:5:15 | input | view-component-input |
|
||||
| single-file-component-8.vue:5:11:5:15 | input | view-component-input |
|
||||
|
||||
@@ -24,3 +24,7 @@ query RemoteFlowSource remoteFlowSource() { any() }
|
||||
query predicate parseErrors(JSParseError err) { exists(err) }
|
||||
|
||||
query predicate attribute(HTML::Attribute attrib, string name) { attrib.getName() = name }
|
||||
|
||||
query predicate threatModelSource(ThreatModelSource source, string kind) {
|
||||
kind = source.getThreatModel()
|
||||
}
|
||||
|
||||
@@ -29,3 +29,4 @@ extensions:
|
||||
- ["database-access-result", "all"]
|
||||
- ["file-write", "all"]
|
||||
- ["reverse-dns", "all"]
|
||||
- ["view-component-input", "all"]
|
||||
|
||||
Reference in New Issue
Block a user