3
3
*/
4
4
5
5
import javascript
6
+ import semmle.javascript.ViewComponentInput
6
7
7
8
module Vue {
8
9
/** The global variable `Vue`, as an API graph entry point. */
@@ -85,17 +86,16 @@ module Vue {
85
86
* A class with a `@Component` decorator, making it usable as an "options" object in Vue.
86
87
*/
87
88
class ClassComponent extends DataFlow:: ClassNode {
89
+ private ClassDefinition cls ;
88
90
DataFlow:: Node decorator ;
89
91
90
92
ClassComponent ( ) {
91
- exists ( ClassDefinition cls |
92
- this = cls .flow ( ) and
93
- cls .getADecorator ( ) .getExpression ( ) = decorator .asExpr ( ) and
94
- (
95
- componentDecorator ( ) .flowsTo ( decorator )
96
- or
97
- componentDecorator ( ) .getACall ( ) = decorator
98
- )
93
+ this = cls .flow ( ) and
94
+ cls .getADecorator ( ) .getExpression ( ) = decorator .asExpr ( ) and
95
+ (
96
+ componentDecorator ( ) .flowsTo ( decorator )
97
+ or
98
+ componentDecorator ( ) .getACall ( ) = decorator
99
99
)
100
100
}
101
101
@@ -105,6 +105,9 @@ module Vue {
105
105
* These options correspond to the options one would pass to `new Vue({...})` or similar.
106
106
*/
107
107
API:: Node getDecoratorOptions ( ) { result = decorator .( API:: CallNode ) .getParameter ( 0 ) }
108
+
109
+ /** Gets the AST node for the class definition. */
110
+ ClassDefinition getClassDefinition ( ) { result = cls }
108
111
}
109
112
110
113
private string memberKindVerb ( DataFlow:: MemberKind kind ) {
@@ -460,6 +463,12 @@ module Vue {
460
463
461
464
SingleFileComponent ( ) { this = MkSingleFileComponent ( file ) }
462
465
466
+ /** Gets a call to `defineProps` in this component. */
467
+ DataFlow:: CallNode getDefinePropsCall ( ) {
468
+ result = DataFlow:: globalVarRef ( "defineProps" ) .getACall ( ) and
469
+ result .getFile ( ) = file
470
+ }
471
+
463
472
override Template:: Element getTemplateElement ( ) {
464
473
exists ( HTML:: Element e | result .( Template:: HtmlElement ) .getElement ( ) = e |
465
474
e .getFile ( ) = file and
@@ -697,4 +706,68 @@ module Vue {
697
706
698
707
override ClientSideRemoteFlowKind getKind ( ) { result = kind }
699
708
}
709
+
710
+ /**
711
+ * Holds if the given type annotation indicates a value that is not typically considered taintable.
712
+ */
713
+ private predicate isSafeType ( TypeAnnotation type ) {
714
+ type .isBooleany ( ) or
715
+ type .isNumbery ( ) or
716
+ type .isRawFunction ( ) or
717
+ type instanceof FunctionTypeExpr
718
+ }
719
+
720
+ /**
721
+ * Holds if the given field has a type that indicates that is can not contain a taintable value.
722
+ */
723
+ private predicate isSafeField ( FieldDeclaration field ) { isSafeType ( field .getTypeAnnotation ( ) ) }
724
+
725
+ private DataFlow:: Node getPropSpec ( Component component ) {
726
+ result = component .getOption ( "props" )
727
+ or
728
+ result = component .( SingleFileComponent ) .getDefinePropsCall ( ) .getArgument ( 0 )
729
+ }
730
+
731
+ /**
732
+ * Holds if `component` has an input prop with the given name, that is of a taintable type.
733
+ */
734
+ private predicate hasTaintableProp ( Component component , string name ) {
735
+ exists ( DataFlow:: SourceNode spec | spec = getPropSpec ( component ) .getALocalSource ( ) |
736
+ spec .( DataFlow:: ArrayCreationNode ) .getAnElement ( ) .getStringValue ( ) = name
737
+ or
738
+ exists ( DataFlow:: PropWrite write |
739
+ write = spec .getAPropertyWrite ( name ) and
740
+ not DataFlow:: globalVarRef ( [ "Number" , "Boolean" ] ) .flowsTo ( write .getRhs ( ) )
741
+ )
742
+ )
743
+ or
744
+ exists ( FieldDeclaration field |
745
+ field = component .getAsClassComponent ( ) .getClassDefinition ( ) .getField ( name ) and
746
+ DataFlow:: moduleMember ( "vue-property-decorator" , "Prop" )
747
+ .getACall ( )
748
+ .flowsToExpr ( field .getADecorator ( ) .getExpression ( ) ) and
749
+ not isSafeField ( field )
750
+ )
751
+ or
752
+ // defineProps() can be called with only type arguments and then the Vue compiler will
753
+ // infer the prop types.
754
+ exists ( CallExpr call , FieldDeclaration field |
755
+ call = component .( SingleFileComponent ) .getDefinePropsCall ( ) .asExpr ( ) and
756
+ field = call .getTypeArgument ( 0 ) .( InterfaceTypeExpr ) .getMember ( name ) and
757
+ not isSafeField ( field )
758
+ )
759
+ }
760
+
761
+ private class PropAsViewComponentInput extends ViewComponentInput {
762
+ PropAsViewComponentInput ( ) {
763
+ exists ( Component component , string name | hasTaintableProp ( component , name ) |
764
+ this = component .getAnInstanceRef ( ) .getAPropertyRead ( name )
765
+ or
766
+ // defineProps() returns the props
767
+ this = component .( SingleFileComponent ) .getDefinePropsCall ( ) .getAPropertyRead ( name )
768
+ )
769
+ }
770
+
771
+ override string getSourceType ( ) { result = "Vue prop" }
772
+ }
700
773
}
0 commit comments