Skip to content

Commit 394d334

Browse files
authored
Merge pull request github#6213 from asgerf/js/vuex
Approved by erik-krogh
2 parents 62adefb + c88d213 commit 394d334

File tree

13 files changed

+678
-7
lines changed

13 files changed

+678
-7
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lgtm,codescanning
2+
* Support for `vuex` has been added. The security queries can now
3+
track taint through the `vuex` state.

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ import semmle.javascript.frameworks.TorrentLibraries
126126
import semmle.javascript.frameworks.Typeahead
127127
import semmle.javascript.frameworks.UriLibraries
128128
import semmle.javascript.frameworks.Vue
129+
import semmle.javascript.frameworks.Vuex
129130
import semmle.javascript.frameworks.WebSocket
130131
import semmle.javascript.frameworks.XmlParsers
131132
import semmle.javascript.frameworks.xUnit

javascript/ql/src/semmle/javascript/ApiGraphs.qll

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111

1212
import javascript
13+
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
1314

1415
/**
1516
* Provides classes and predicates for working with APIs defined or used in a database.
@@ -747,6 +748,18 @@ module API {
747748
result = DataFlow::moduleVarNode(imp.getImportedModule()).getAPropertyRead("exports")
748749
)
749750
or
751+
exists(ObjectExpr obj |
752+
obj = trackDefNode(nd, t.continue()).asExpr() and
753+
result =
754+
obj.getAProperty()
755+
.(SpreadProperty)
756+
.getInit()
757+
.(SpreadElement)
758+
.getOperand()
759+
.flow()
760+
.getALocalSource()
761+
)
762+
or
750763
t = defStep(nd, result)
751764
}
752765

@@ -930,16 +943,38 @@ private module Label {
930943
/** Gets the `member` edge label for the unknown member. */
931944
string unknownMember() { result = "member *" }
932945

946+
/**
947+
* Gets a property name referred to by the given dynamic property access,
948+
* allowing one property flow step in the process (to allow flow through imports).
949+
*
950+
* This is to support code patterns where the property name is actually constant,
951+
* but the property name has been factored into a library.
952+
*/
953+
private string getAnIndirectPropName(DataFlow::PropRef ref) {
954+
exists(DataFlow::Node pred |
955+
FlowSteps::propertyFlowStep(pred, ref.getPropertyNameExpr().flow()) and
956+
result = pred.getStringValue()
957+
)
958+
}
959+
960+
/**
961+
* Gets unique result of `getAnIndirectPropName` if there is one.
962+
*/
963+
private string getIndirectPropName(DataFlow::PropRef ref) {
964+
result = unique(string s | s = getAnIndirectPropName(ref))
965+
}
966+
933967
/** Gets the `member` edge label for the given property reference. */
934968
string memberFromRef(DataFlow::PropRef pr) {
935-
exists(string pn | pn = pr.getPropertyName() |
969+
exists(string pn | pn = pr.getPropertyName() or pn = getIndirectPropName(pr) |
936970
result = member(pn) and
937971
// only consider properties with alphanumeric(-ish) names, excluding special properties
938972
// and properties whose names look like they are meant to be internal
939973
pn.regexpMatch("(?!prototype$|__)[\\w_$][\\w\\-.$]*")
940974
)
941975
or
942976
not exists(pr.getPropertyName()) and
977+
not exists(getIndirectPropName(pr)) and
943978
result = unknownMember()
944979
}
945980

javascript/ql/src/semmle/javascript/frameworks/Vue.qll

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -656,16 +656,28 @@ module Vue {
656656
/** Gets a data flow node that refers to a `Route` object from `vue-router`. */
657657
DataFlow::SourceNode routeObject() { result = routeObject(DataFlow::TypeTracker::end()) }
658658

659-
private class VueRouterFlowSource extends RemoteFlowSource {
659+
private class VueRouterFlowSource extends ClientSideRemoteFlowSource {
660+
ClientSideRemoteFlowKind kind;
661+
660662
VueRouterFlowSource() {
661-
this = routeObject().getAPropertyRead(["params", "query", "hash", "path", "fullPath"])
662-
or
663-
exists(Instance i, string prop |
664-
this = i.getWatchHandler(prop).getParameter([0, 1]) and
665-
prop.regexpMatch("\\$route\\.(params|query|hash|path|fullPath)\\b.*")
663+
exists(string name |
664+
this = routeObject().getAPropertyRead(name)
665+
or
666+
exists(string prop |
667+
this = any(Instance i).getWatchHandler(prop).getParameter([0, 1]) and
668+
name = prop.regexpCapture("\\$route\\.(params|query|hash|path|fullPath)\\b.*", 1)
669+
)
670+
|
671+
name = ["params", "path", "fullPath"] and kind.isPath()
672+
or
673+
name = "query" and kind.isQuery()
674+
or
675+
name = "hash" and kind.isFragment()
666676
)
667677
}
668678

669679
override string getSourceType() { result = "Vue route parameter" }
680+
681+
override ClientSideRemoteFlowKind getKind() { result = kind }
670682
}
671683
}

0 commit comments

Comments
 (0)