mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Merge pull request #13737 from asgerf/dynamic/fuzzy-models
Dynamic: add Fuzzy token
This commit is contained in:
@@ -20,9 +20,7 @@ module Vue {
|
||||
private class VueExportEntryPoint extends API::EntryPoint {
|
||||
VueExportEntryPoint() { this = "VueExportEntryPoint" }
|
||||
|
||||
override DataFlow::Node getASink() {
|
||||
result = any(SingleFileComponent c).getModule().getDefaultOrBulkExport()
|
||||
}
|
||||
override DataFlow::Node getASink() { result = getModuleFromVueFile(_).getDefaultOrBulkExport() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -455,6 +453,13 @@ module Vue {
|
||||
}
|
||||
}
|
||||
|
||||
private Module getModuleFromVueFile(VueFile file) {
|
||||
exists(HTML::ScriptElement elem |
|
||||
xmlElements(elem, _, _, _, file) and // Avoid materializing all of Locatable.getFile()
|
||||
result.getTopLevel() = elem.getScript()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A single file Vue component in a `.vue` file.
|
||||
*/
|
||||
@@ -482,12 +487,7 @@ module Vue {
|
||||
}
|
||||
|
||||
/** Gets the module defined by the `script` tag in this .vue file, if any. */
|
||||
Module getModule() {
|
||||
exists(HTML::ScriptElement elem |
|
||||
xmlElements(elem, _, _, _, file) and // Avoid materializing all of Locatable.getFile()
|
||||
result.getTopLevel() = elem.getScript()
|
||||
)
|
||||
}
|
||||
Module getModule() { result = getModuleFromVueFile(file) }
|
||||
|
||||
override API::Node getComponentRef() {
|
||||
// There is no explicit `new Vue()` call in .vue files, so instead get all the imports
|
||||
|
||||
@@ -454,6 +454,14 @@ private API::Node getNodeFromPath(string type, AccessPath path, int n) {
|
||||
or
|
||||
// Apply a type step
|
||||
typeStep(getNodeFromPath(type, path, n), result)
|
||||
or
|
||||
// Apply a fuzzy step (without advancing 'n')
|
||||
path.getToken(n).getName() = "Fuzzy" and
|
||||
result = Specific::getAFuzzySuccessor(getNodeFromPath(type, path, n))
|
||||
or
|
||||
// Skip a fuzzy step (advance 'n' without changing the current node)
|
||||
path.getToken(n - 1).getName() = "Fuzzy" and
|
||||
result = getNodeFromPath(type, path, n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,6 +508,14 @@ private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath, int n)
|
||||
// will themselves find by following type-steps.
|
||||
n > 0 and
|
||||
n < subPath.getNumToken()
|
||||
or
|
||||
// Apply a fuzzy step (without advancing 'n')
|
||||
subPath.getToken(n).getName() = "Fuzzy" and
|
||||
result = Specific::getAFuzzySuccessor(getNodeFromSubPath(base, subPath, n))
|
||||
or
|
||||
// Skip a fuzzy step (advance 'n' without changing the current node)
|
||||
subPath.getToken(n - 1).getName() = "Fuzzy" and
|
||||
result = getNodeFromSubPath(base, subPath, n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,7 +577,7 @@ private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path)
|
||||
*/
|
||||
bindingset[name]
|
||||
private predicate isValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar"]
|
||||
name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar", "Fuzzy"]
|
||||
or
|
||||
Specific::isExtraValidTokenNameInIdentifyingAccessPath(name)
|
||||
}
|
||||
@@ -572,7 +588,7 @@ private predicate isValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
*/
|
||||
bindingset[name]
|
||||
private predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) {
|
||||
name = "ReturnValue"
|
||||
name = ["ReturnValue", "Fuzzy"]
|
||||
or
|
||||
Specific::isExtraValidNoArgumentTokenInIdentifyingAccessPath(name)
|
||||
}
|
||||
|
||||
@@ -192,6 +192,43 @@ API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathToken toke
|
||||
result.asSink() = node.(DataFlow::CallNode).getReceiver()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is the name of a built-in method on Object, Array, or String.
|
||||
*/
|
||||
private predicate isCommonBuiltinMethodName(string name) {
|
||||
exists(JS::ExternalInstanceMemberDecl member |
|
||||
member.getBaseName() in ["Object", "Array", "String"] and
|
||||
name = member.getName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if fuzzy evaluation should not traverse through `call`.
|
||||
*/
|
||||
private predicate blockFuzzyCall(DataFlow::CallNode call) {
|
||||
isCommonBuiltinMethodName(call.getCalleeName())
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
API::Node getAFuzzySuccessor(API::Node node) {
|
||||
result = node.getAMember() and
|
||||
// Block traversal into calls to built-ins like .toString() and .substring()
|
||||
// Since there is no API node representing the call itself, block flow into the callee node.
|
||||
not exists(DataFlow::CallNode call |
|
||||
node.asSource() = call.getCalleeNode() and
|
||||
blockFuzzyCall(call)
|
||||
)
|
||||
or
|
||||
result = node.getAParameter()
|
||||
or
|
||||
result = node.getReturn()
|
||||
or
|
||||
result = node.getPromised()
|
||||
or
|
||||
// include 'this' parameters but not 'this' arguments
|
||||
result = node.getReceiver() and result.asSource() instanceof DataFlow::ThisNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `invoke` matches the JS-specific call site filter in `token`.
|
||||
*/
|
||||
|
||||
@@ -66,6 +66,14 @@ taintFlow
|
||||
| test.js:231:59:231:66 | source() | test.js:231:59:231:66 | source() |
|
||||
| test.js:232:59:232:66 | source() | test.js:232:59:232:66 | source() |
|
||||
| test.js:233:59:233:66 | source() | test.js:233:59:233:66 | source() |
|
||||
| test.js:237:21:237:28 | source() | test.js:237:21:237:28 | source() |
|
||||
| test.js:238:25:238:32 | source() | test.js:238:25:238:32 | source() |
|
||||
| test.js:239:27:239:34 | source() | test.js:239:27:239:34 | source() |
|
||||
| test.js:241:17:241:24 | source() | test.js:241:17:241:24 | source() |
|
||||
| test.js:244:33:244:40 | source() | test.js:244:33:244:40 | source() |
|
||||
| test.js:249:28:249:35 | source() | test.js:249:28:249:35 | source() |
|
||||
| test.js:252:15:252:22 | source() | test.js:252:15:252:22 | source() |
|
||||
| test.js:254:32:254:39 | source() | test.js:254:32:254:39 | source() |
|
||||
isSink
|
||||
| test.js:54:18:54:25 | source() | test-sink |
|
||||
| test.js:55:22:55:29 | source() | test-sink |
|
||||
@@ -136,6 +144,14 @@ isSink
|
||||
| test.js:231:59:231:66 | source() | test-sink |
|
||||
| test.js:232:59:232:66 | source() | test-sink |
|
||||
| test.js:233:59:233:66 | source() | test-sink |
|
||||
| test.js:237:21:237:28 | source() | test-sink |
|
||||
| test.js:238:25:238:32 | source() | test-sink |
|
||||
| test.js:239:27:239:34 | source() | test-sink |
|
||||
| test.js:241:17:241:24 | source() | test-sink |
|
||||
| test.js:244:33:244:40 | source() | test-sink |
|
||||
| test.js:249:28:249:35 | source() | test-sink |
|
||||
| test.js:252:15:252:22 | source() | test-sink |
|
||||
| test.js:254:32:254:39 | source() | test-sink |
|
||||
syntaxErrors
|
||||
| Member[foo |
|
||||
| Member[foo] .Member[bar] |
|
||||
|
||||
@@ -232,3 +232,27 @@ function typeVars() {
|
||||
testlib.typevar.left.x.getThis().getThis().right.mySink(source()); // NOT OK
|
||||
testlib.typevar.left.x.right.getThis().getThis().mySink(source()); // NOT OK
|
||||
}
|
||||
|
||||
function fuzzy() {
|
||||
testlib.fuzzyCall(source()); // NOT OK
|
||||
testlib.foo.fuzzyCall(source()); // NOT OK
|
||||
testlib.foo().fuzzyCall(source()); // NOT OK
|
||||
new testlib.Blah().foo.bar(async p => {
|
||||
p.fuzzyCall(source()); // NOT OK
|
||||
p.otherCall(source()); // OK
|
||||
p.fuzzyCall().laterMethod(source()); // OK
|
||||
(await p.promise).fuzzyCall(source()); // NOT OK
|
||||
});
|
||||
|
||||
const wrapped = _.partial(testlib.foo, [123]);
|
||||
wrapped().fuzzyCall(source()); // NOT OK [INCONSISTENCY] - API graphs do not currently propagate return values through partial invocation
|
||||
wrapped(p => p.fuzzyCall(source())); // NOT OK
|
||||
|
||||
const wrappedSink = _.partial(testlib.fuzzyCall);
|
||||
wrappedSink(source()); // NOT OK
|
||||
|
||||
_.partial(testlib.fuzzyCall, source()); // NOT OK
|
||||
|
||||
fuzzyCall(source()); // OK - does not come from 'testlib'
|
||||
require('blah').fuzzyCall(source()); // OK - does not come from 'testlib'
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ class Sinks extends ModelInput::SinkModelCsv {
|
||||
"testlib;Member[typevar].TypeVar[ABC].Member[mySink].Argument[0];test-sink",
|
||||
"testlib;Member[typevar].TypeVar[ABC].TypeVar[ABC].Member[mySink].Argument[1];test-sink",
|
||||
"testlib;Member[typevar].TypeVar[LeftRight].Member[mySink].Argument[0];test-sink",
|
||||
"testlib;Fuzzy.Member[fuzzyCall].Argument[0];test-sink"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user