Merge branch 'main' into angular-sources-sinks

This commit is contained in:
Paul Hodgkinson
2025-01-24 15:46:48 +00:00
committed by GitHub
119 changed files with 13233 additions and 4129 deletions

View File

@@ -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.
@@ -610,4 +611,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" }
}
}

View File

@@ -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" }
}

View File

@@ -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" }
}
}