merging 2.16.4, resolving zipslip .expected file conflict

This commit is contained in:
Dilan Bhalla
2024-03-11 14:12:21 -07:00
904 changed files with 53390 additions and 50539 deletions

View File

@@ -1,6 +1,6 @@
load("@//:dist.bzl", "dist")
load("@semmle_code//:dist.bzl", "dist")
load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
load("@//buildutils-internal:zipmerge.bzl", "zipmerge")
load("@semmle_code//buildutils-internal:zipmerge.bzl", "zipmerge")
package(default_visibility = ["//visibility:public"])
@@ -30,7 +30,7 @@ dist(
"//javascript/downgrades",
"//javascript/externs",
"//javascript/extractor:tools-extractor",
"@//language-packs/javascript:resources",
"@semmle_code//language-packs/javascript:resources",
],
prefix = "javascript",
)

View File

@@ -1,4 +1,4 @@
load("@//:dist.bzl", "pack_zip")
load("@semmle_code//:dist.bzl", "pack_zip")
pack_zip(
name = "downgrades",

View File

@@ -1,4 +1,4 @@
load("@//:dist.bzl", "pack_zip")
load("@semmle_code//:dist.bzl", "pack_zip")
pack_zip(
name = "externs",

View File

@@ -1,21 +1,21 @@
load("@//:common.bzl", "codeql_fat_jar", "codeql_java_project")
load("@semmle_code//:common.bzl", "codeql_fat_jar", "codeql_java_project")
load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
java_library(
name = "deps",
visibility = [":__subpackages__"],
exports = [
"@//extractor:html",
"@//extractor:yaml",
"@//resources/lib/java:commons-compress",
"@//resources/lib/java:gson",
"@//resources/lib/java:jericho-html",
"@//resources/lib/java:slf4j-api",
"@//resources/lib/java:snakeyaml",
"@//third_party:jackson",
"@//third_party:logback",
"@//util-java7",
"@//util-java8",
"@semmle_code//extractor:html",
"@semmle_code//extractor:yaml",
"@semmle_code//resources/lib/java:commons-compress",
"@semmle_code//resources/lib/java:gson",
"@semmle_code//resources/lib/java:jericho-html",
"@semmle_code//resources/lib/java:slf4j-api",
"@semmle_code//resources/lib/java:snakeyaml",
"@semmle_code//third_party:jackson",
"@semmle_code//third_party:logback",
"@semmle_code//util-java7",
"@semmle_code//util-java8",
],
)
@@ -26,30 +26,23 @@ codeql_java_project(
],
)
pkg_files(
name = "javascript-extractor-resources",
srcs = glob(["resources/**"]),
strip_prefix = "resources",
)
codeql_fat_jar(
name = "extractor-javascript",
srcs = [
":extractor",
"@//extractor:html",
"@//extractor:xml-trap-writer",
"@//extractor:yaml",
"@//resources/lib/java:commons-compress",
"@//resources/lib/java:gson",
"@//resources/lib/java:jericho-html",
"@//resources/lib/java:slf4j-api",
"@//resources/lib/java:snakeyaml",
"@//third_party:jackson",
"@//third_party:logback",
"@//util-java7",
"@//util-java8",
"@semmle_code//extractor:html",
"@semmle_code//extractor:xml-trap-writer",
"@semmle_code//extractor:yaml",
"@semmle_code//resources/lib/java:commons-compress",
"@semmle_code//resources/lib/java:gson",
"@semmle_code//resources/lib/java:jericho-html",
"@semmle_code//resources/lib/java:slf4j-api",
"@semmle_code//resources/lib/java:snakeyaml",
"@semmle_code//third_party:jackson",
"@semmle_code//third_party:logback",
"@semmle_code//util-java7",
"@semmle_code//util-java8",
],
files = [":javascript-extractor-resources"],
main_class = "com.semmle.js.extractor.Main",
)

View File

@@ -1,4 +1,4 @@
load("@//:common.bzl", "on_windows")
load("@semmle_code//:common.bzl", "on_windows")
# Builds a zip file of the compiled typscript-parser-wrapper and its dependencies.
genrule(

View File

@@ -7,15 +7,15 @@ java_test(
"//javascript/extractor/tests",
"@nodejs//:node_bin",
],
test_class = "com.semmle.js.extractor.test.AllTests",
deps = [
"//javascript/extractor",
"//javascript/extractor:deps",
"@//resources/lib/java/DO_NOT_DISTRIBUTE:junit",
"@bazel_tools//tools/java/runfiles",
],
env = {
"NODE_BIN": "$(rlocationpath @nodejs//:node_bin)",
"TS_WRAPPER_ZIP": "$(rlocationpath //javascript/extractor/lib/typescript)",
},
test_class = "com.semmle.js.extractor.test.AllTests",
deps = [
"//javascript/extractor",
"//javascript/extractor:deps",
"@bazel_tools//tools/java/runfiles",
"@semmle_code//resources/lib/java/DO_NOT_DISTRIBUTE:junit",
],
)

View File

@@ -1 +1 @@
**/ql/javascript/extractor/tests/*/input//
**/*ql*/javascript/extractor/tests/*/input//

View File

@@ -1,3 +1,7 @@
## 0.8.10
No user-facing changes.
## 0.8.9
### Minor Analysis Improvements

View File

@@ -3,6 +3,7 @@
*/
import semmle.files.FileSystem
private import codeql.util.FileSystem
/**
* Returns the `File` matching the given source file name as encoded by the VS
@@ -10,13 +11,5 @@ import semmle.files.FileSystem
*/
cached
File getFileBySourceArchiveName(string name) {
// The name provided for a file in the source archive by the VS Code extension
// has some differences from the absolute path in the database:
// 1. colons are replaced by underscores
// 2. there's a leading slash, even for Windows paths: "C:/foo/bar" ->
// "/C_/foo/bar"
// 3. double slashes in UNC prefixes are replaced with a single slash
// We can handle 2 and 3 together by unconditionally adding a leading slash
// before replacing double slashes.
name = ("/" + result.getAbsolutePath().replaceAll(":", "_")).replaceAll("//", "/")
result = IdeContextual<File>::getFileBySourceArchiveName(name)
}

View File

@@ -0,0 +1,3 @@
## 0.8.10
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.8.9
lastReleaseVersion: 0.8.10

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-all
version: 0.8.9
version: 0.8.10
groups: javascript
dbscheme: semmlecode.javascript.dbscheme
extractor: javascript

View File

@@ -38,22 +38,92 @@ private string join(string x, string y) {
private predicate isPackageExport(API::Node node) { node = API::moduleExport(_) }
private predicate memberEdge(API::Node pred, API::Node succ) { succ = pred.getAMember() }
/** Gets the shortest distance from a packaeg export to `nd` in the API graph. */
private int distanceFromPackageExport(API::Node nd) =
shortestDistances(isPackageExport/1, memberEdge/2)(_, nd, result)
private predicate isExported(API::Node node) {
isPackageExport(node)
or
exists(API::Node pred |
isExported(pred) and
memberEdge(pred, node) and
not isPrivateLike(node)
/**
* A version of `getInstance()` only from sink nodes to the special `ClassInstance` node.
*
* This ensures we see instance methods, but not side effects on `this` or on instantiations of the class.
*/
private predicate instanceEdge(API::Node pred, API::Node succ) {
exists(DataFlow::ClassNode cls |
pred.getAValueReachingSink() = cls and
succ = API::Internal::getClassInstance(cls)
)
}
/** Holds if `pred -> succ` is an edge we can use for naming. */
private predicate relevantEdge(API::Node pred, API::Node succ) {
succ = pred.getMember(_) and
not isPrivateLike(succ)
or
instanceEdge(pred, succ)
}
private signature predicate isRootNodeSig(API::Node node);
private signature predicate edgeSig(API::Node pred, API::Node succ);
/** Builds `shortestDistances` using the API graph root node as the only origin node, to ensure unique results. */
private module ApiGraphDistance<isRootNodeSig/1 isRootNode, edgeSig/2 edges> {
private predicate edgesWithEntry(API::Node pred, API::Node succ) {
edges(pred, succ)
or
pred = API::root() and
isRootNode(succ)
}
int distanceTo(API::Node node) = shortestDistances(API::root/0, edgesWithEntry/2)(_, node, result)
}
/** Gets the shortest distance from a package export to `nd` in the API graph. */
private predicate distanceFromPackageExport =
ApiGraphDistance<isPackageExport/1, relevantEdge/2>::distanceTo/1;
/**
* Holds if `(package, name)` is the fallback name for `cls`, to be used as a last resort
* in order to name its instance methods.
*
* This happens when the class is not accessible via an access path, but instances of the
* class can still escape via more complex access patterns, for example:
*
* class InternalClass {}
* function foo() {
* return new InternalClass();
* }
*/
private predicate classHasFallbackName(
DataFlow::ClassNode cls, string package, string name, int badness
) {
hasEscapingInstance(cls) and
not exists(distanceFromPackageExport(any(API::Node node | node.getAValueReachingSink() = cls))) and
exists(string baseName |
InternalModuleNaming::fallbackModuleName(cls.getTopLevel(), package, baseName, badness - 100) and
name = join(baseName, cls.getName())
)
}
/** Holds if `node` describes instances of a class that has a fallback name. */
private predicate isClassInstanceWithFallbackName(API::Node node) {
exists(DataFlow::ClassNode cls |
classHasFallbackName(cls, _, _, _) and
node = API::Internal::getClassInstance(cls)
)
}
/** Gets the shortest distance from a node with a fallback name, to `nd` in the API graph. */
private predicate distanceFromFallbackName =
ApiGraphDistance<isClassInstanceWithFallbackName/1, relevantEdge/2>::distanceTo/1;
/** Gets the shortest distance from a name-root (package export or fallback name) to `nd` */
private int distanceFromRoot(API::Node nd) {
result = distanceFromPackageExport(nd)
or
not exists(distanceFromPackageExport(nd)) and
result = 100 + distanceFromFallbackName(nd)
}
/** Holds if `node` can be given a name. */
private predicate isRelevant(API::Node node) { exists(distanceFromRoot(node)) }
/**
* Holds if `node` is a default export that can be reinterpreted as a namespace export,
* because the enclosing module has no named exports.
@@ -79,21 +149,29 @@ private predicate isPrivateAssignment(DataFlow::Node node) {
private predicate isPrivateLike(API::Node node) { isPrivateAssignment(node.asSink()) }
bindingset[name]
private int getNameBadness(string name) {
if name = ["constructor", "default"] then result = 10 else result = 0
}
private API::Node getASuccessor(API::Node node, string name, int badness) {
isExported(node) and
exists(string member |
result = node.getMember(member) and
if member = "default"
then
if defaultExportCanBeInterpretedAsNamespaceExport(node)
isRelevant(node) and
isRelevant(result) and
(
exists(string member |
result = node.getMember(member) and
if member = "default" and defaultExportCanBeInterpretedAsNamespaceExport(node)
then (
badness = 5 and name = ""
) else (
badness = 10 and name = "default"
name = member and
badness = getNameBadness(name)
)
else (
name = member and badness = 0
)
or
instanceEdge(node, result) and
name = "prototype" and
badness = 0
)
}
@@ -114,15 +192,17 @@ private API::Node getPreferredPredecessor(API::Node node, string name, int badne
min(API::Node pred, int b |
pred = getAPredecessor(node, _, b) and
// ensure the preferred predecessor is strictly closer to a root export, even if it means accepting more badness
distanceFromPackageExport(pred) < distanceFromPackageExport(node)
distanceFromRoot(pred) < distanceFromRoot(node)
|
b
) and
result =
min(API::Node pred, string name1 |
pred = getAPredecessor(node, name1, badness)
pred = getAPredecessor(node, name1, badness) and
// ensure the preferred predecessor is strictly closer to a root export, even if it means accepting more badness
distanceFromRoot(pred) < distanceFromRoot(node)
|
pred order by distanceFromPackageExport(pred), name1
pred order by distanceFromRoot(pred), name1
) and
name = min(string n | result = getAPredecessor(node, n, badness) | n)
}
@@ -137,6 +217,12 @@ private predicate sinkHasNameCandidate(API::Node sink, string package, string na
name = "" and
badness = 0
or
exists(DataFlow::ClassNode cls, string className |
sink = API::Internal::getClassInstance(cls) and
classHasFallbackName(cls, package, className, badness) and
name = join(className, "prototype")
)
or
exists(API::Node baseNode, string baseName, int baseBadness, string step, int stepBadness |
sinkHasNameCandidate(baseNode, package, baseName, baseBadness) and
baseNode = getPreferredPredecessor(sink, step, stepBadness) and
@@ -163,80 +249,32 @@ predicate sinkHasPrimaryName(API::Node sink, string package, string name) {
sinkHasPrimaryName(sink, package, name, _)
}
/**
* Holds if `(package, name)` is an alias for `node`.
*
* This means it is a valid name for it, but was not chosen as the primary name.
*/
private predicate sinkHasAlias(API::Node sink, string package, string name) {
not sinkHasPrimaryName(sink, package, name) and
(
exists(string baseName, string step |
sinkHasPrimaryName(getAPredecessor(sink, step, _), package, baseName) and
name = join(baseName, step)
)
or
sink = API::moduleExport(package) and
name = ""
/** Gets a source node that can flow to `sink` without using a return step. */
private DataFlow::SourceNode nodeReachingSink(API::Node sink, DataFlow::TypeBackTracker t) {
t.start() and
result = sink.asSink().getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 |
result = nodeReachingSink(sink, t2).backtrack(t2, t) and
t.hasReturn() = false
)
}
/** Gets a source node that can flow to `sink` without using a return step. */
DataFlow::SourceNode nodeReachingSink(API::Node sink) {
result = nodeReachingSink(sink, DataFlow::TypeBackTracker::end())
}
/** Gets a sink node reachable from `node`. */
bindingset[node]
private API::Node getASinkNode(DataFlow::SourceNode node) { result.getAValueReachingSink() = node }
private API::Node getASinkNode(DataFlow::SourceNode node) { node = nodeReachingSink(result) }
/**
* Holds if `node` is a declaration in an externs file.
*
* This is to ensure that functions/classes in externs are not named after a re-export in a package.
* Holds if `node` is assigned to a global access path. Note that such nodes generally do not have API nodes.
*/
private predicate nameFromExterns(DataFlow::Node node, string package, string name, int badness) {
node.getTopLevel().isExterns() and
private predicate nameFromGlobal(DataFlow::Node node, string package, string name, int badness) {
package = "global" and
node = AccessPath::getAnAssignmentTo(name) and
badness = -10
}
bindingset[qualifiedName]
private int getBadnessOfClassName(string qualifiedName) {
if qualifiedName.matches("%.constructor")
then result = 10
else
if qualifiedName = ""
then result = 5
else result = 0
}
/** Holds if `(package, name)` is a potential name for `cls`, with the given `badness`. */
private predicate classObjectHasNameCandidate(
DataFlow::ClassNode cls, string package, string name, int badness
) {
// There can be multiple API nodes associated with `cls`.
// For example:
///
// class C {}
// module.exports.A = C; // first sink
// module.exports.B = C; // second sink
//
exists(int baseBadness |
sinkHasPrimaryName(getASinkNode(cls), package, name, baseBadness) and
badness = baseBadness + getBadnessOfClassName(name)
)
or
nameFromExterns(cls, package, name, badness)
}
private predicate classObjectHasPrimaryName(
DataFlow::ClassNode cls, string package, string name, int badness
) {
badness = min(int b | classObjectHasNameCandidate(cls, _, _, b) | b) and
package = min(string p | classObjectHasNameCandidate(cls, p, _, badness) | p) and
name = min(string n | classObjectHasNameCandidate(cls, package, n, badness) | n)
}
/** Holds if `(package, name)` is the primary name for the class object of `cls`. */
predicate classObjectHasPrimaryName(DataFlow::ClassNode cls, string package, string name) {
classObjectHasPrimaryName(cls, package, name, _)
(if node.getTopLevel().isExterns() then badness = -10 else badness = 10)
}
/** Holds if an instance of `cls` can be exposed to client code. */
@@ -244,116 +282,116 @@ private predicate hasEscapingInstance(DataFlow::ClassNode cls) {
cls.getAnInstanceReference().flowsTo(any(API::Node n).asSink())
}
/**
* Holds if `(package, name)` is a potential name to use for instances of `cls`, with the given `badness`.
*/
private predicate classInstanceHasNameCandidate(
DataFlow::ClassNode cls, string package, string name, int badness
private predicate sourceNodeHasNameCandidate(
DataFlow::SourceNode node, string package, string name, int badness
) {
exists(string baseName |
classObjectHasPrimaryName(cls, package, baseName, badness) and
name = join(baseName, "prototype")
)
sinkHasPrimaryName(getASinkNode(node), package, name, badness)
or
// In case the class itself is unaccessible, but an instance is exposed via an access path,
// consider using that access path. For example:
//
// class InternalClass {}
// module.exports.foo = new InternalClass();
//
exists(int baseBadness |
sinkHasPrimaryName(getASinkNode(cls.getAnInstanceReference()), package, name, baseBadness) and
badness = baseBadness + 30 // add penalty, as we prefer to base this on the class name
)
or
// If neither the class nor its instances are accessible via an access path, but instances of the
// class can still escape via more complex access patterns, resort to a synthesized name.
// For example:
//
// class InternalClass {}
// function foo() {
// return new InternalClass();
// }
//
hasEscapingInstance(cls) and
exists(string baseName |
InternalModuleNaming::fallbackModuleName(cls.getTopLevel(), package, baseName, badness - 100) and
name = join(baseName, cls.getName()) + ".prototype"
)
nameFromGlobal(node, package, name, badness)
}
private predicate classInstanceHasPrimaryName(
DataFlow::ClassNode cls, string package, string name, int badness
private predicate sourceNodeHasPrimaryName(
DataFlow::SourceNode node, string package, string name, int badness
) {
badness = min(int b | classInstanceHasNameCandidate(cls, _, _, b) | b) and
package = min(string p | classInstanceHasNameCandidate(cls, p, _, badness) | p) and
badness = min(int b | sourceNodeHasNameCandidate(node, _, _, b) | b) and
package =
min(string p | sourceNodeHasNameCandidate(node, p, _, badness) | p order by p.length(), p) and
name =
min(string n |
classInstanceHasNameCandidate(cls, package, n, badness)
|
n order by n.length(), n
)
min(string n | sourceNodeHasNameCandidate(node, package, n, badness) | n order by n.length(), n)
}
/** Holds if `(package, name)` is the primary name to use for instances of `cls`. */
predicate classInstanceHasPrimaryName(DataFlow::ClassNode cls, string package, string name) {
classInstanceHasPrimaryName(cls, package, name, _)
}
/** Holds if `(package, name)` is an alias referring to some instance of `cls`. */
predicate classInstanceHasAlias(DataFlow::ClassNode cls, string package, string name) {
not classInstanceHasPrimaryName(cls, package, name) and
exists(int badness |
classInstanceHasNameCandidate(cls, package, name, badness) and
badness < 100 // Badness 100 is when we start to synthesize names. Do not suggest these as aliases.
)
}
private predicate functionHasNameCandidate(
DataFlow::FunctionNode function, string package, string name, int badness
) {
sinkHasPrimaryName(getASinkNode(function), package, name, badness)
or
exists(DataFlow::ClassNode cls |
function = cls.getConstructor() and
classObjectHasPrimaryName(cls, package, name, badness)
/** Gets a data flow node referring to a function value. */
private DataFlow::SourceNode functionValue(DataFlow::TypeTracker t) {
t.start() and
(
result instanceof DataFlow::FunctionNode
or
exists(string baseName, string memberName |
function = cls.getInstanceMethod(memberName) and
classInstanceHasPrimaryName(cls, package, baseName, badness) and
name = join(baseName, memberName)
or
function = cls.getStaticMethod(memberName) and
classObjectHasPrimaryName(cls, package, baseName, badness) and
name = join(baseName, memberName)
)
result instanceof DataFlow::ClassNode
or
result instanceof DataFlow::PartialInvokeNode
or
result = DataFlow::globalVarRef(["Function", "eval"]).getAnInvocation()
or
// Assume double-invocation of Function also returns a function
result = DataFlow::globalVarRef("Function").getAnInvocation().getAnInvocation()
)
or
nameFromExterns(function, package, name, badness)
exists(DataFlow::TypeTracker t2 | result = functionValue(t2).track(t2, t))
}
private predicate functionHasPrimaryName(
DataFlow::FunctionNode function, string package, string name, int badness
) {
badness = min(int b | functionHasNameCandidate(function, _, _, b) | b) and
package = min(string p | functionHasNameCandidate(function, p, _, badness) | p) and
name =
min(string n |
functionHasNameCandidate(function, package, n, badness)
|
n order by n.length(), n
)
/** Gets a data flow node referring to a function value. */
private DataFlow::SourceNode functionValue() {
result = functionValue(DataFlow::TypeTracker::end())
}
/**
* Holds if `node` is a function or a call that returns a function.
*/
private predicate isFunctionSource(DataFlow::SourceNode node) {
(
exists(getASinkNode(node))
or
nameFromGlobal(node, _, _, _)
) and
(
node instanceof DataFlow::FunctionNode
or
node instanceof DataFlow::ClassNode
or
node = functionValue() and
node instanceof DataFlow::InvokeNode and
// `getASinkNode` steps through imports (but not other calls) so exclude calls that are imports (i.e. require calls)
// as we want to get as close to the source as possible.
not node instanceof DataFlow::ModuleImportNode
)
}
/**
* Holds if `(package, name)` is the primary name for the given `function`.
*
* The `function` node may be an actual function expression, or a call site from which a function is returned.
*/
predicate functionHasPrimaryName(DataFlow::FunctionNode function, string package, string name) {
functionHasPrimaryName(function, package, name, _)
predicate functionHasPrimaryName(DataFlow::SourceNode function, string package, string name) {
sourceNodeHasPrimaryName(function, package, name, _) and
isFunctionSource(function)
}
private predicate sinkHasSourceName(API::Node sink, string package, string name, int badness) {
exists(DataFlow::SourceNode source |
sink = getASinkNode(source) and
sourceNodeHasPrimaryName(source, package, name, badness)
)
}
private predicate sinkHasPrimarySourceName(API::Node sink, string package, string name) {
strictcount(string p, string n | sinkHasSourceName(sink, p, n, _)) = 1 and
sinkHasSourceName(sink, package, name, _)
}
private predicate aliasCandidate(
string package, string name, string targetPackage, string targetName, API::Node aliasDef
) {
sinkHasPrimaryName(aliasDef, package, name) and
sinkHasPrimarySourceName(aliasDef, targetPackage, targetName) and
not sinkHasSourceName(_, package, name, _) // (package, name) cannot be an alias if a source has it as its primary name
}
private predicate nonAlias(string package, string name) {
// `(package, name)` appears to be an alias for multiple things. Treat it as a primary name instead.
strictcount(string targetPackage, string targetName |
aliasCandidate(package, name, targetPackage, targetName, _)
) > 1
or
// Not all sinks with this name agree on the alias target
exists(API::Node sink, string targetPackage, string targetName |
aliasCandidate(package, name, targetPackage, targetName, _) and
sinkHasPrimaryName(sink, package, name) and
not sinkHasPrimarySourceName(sink, targetPackage, targetName)
)
}
/**
* Holds if `(aliasPackage, aliasName)` is an alias for `(primaryPackage, primaryName)`,
* Holds if `(package, name)` is an alias for `(targetPackage, targetName)`,
* defined at `aliasDef`.
*
* Only the last component of an access path is reported as an alias, the prefix always
@@ -365,24 +403,10 @@ predicate functionHasPrimaryName(DataFlow::FunctionNode function, string package
* reported separately.
*/
predicate aliasDefinition(
string primaryPackage, string primaryName, string aliasPackage, string aliasName,
API::Node aliasDef
string package, string name, string targetPackage, string targetName, API::Node aliasDef
) {
exists(DataFlow::SourceNode source |
classObjectHasPrimaryName(source, primaryPackage, primaryName)
or
functionHasPrimaryName(source, primaryPackage, primaryName)
|
aliasDef.getAValueReachingSink() = source and
sinkHasPrimaryName(aliasDef, aliasPackage, aliasName, _) and
not (
primaryPackage = aliasPackage and
primaryName = aliasName
)
)
or
sinkHasPrimaryName(aliasDef, primaryPackage, primaryName) and
sinkHasAlias(aliasDef, aliasPackage, aliasName)
aliasCandidate(package, name, targetPackage, targetName, aliasDef) and
not nonAlias(package, name)
}
/**
@@ -432,8 +456,6 @@ private module InternalModuleNaming {
/** Holds if `(package, name)` should be used to refer to code inside `mod`. */
predicate fallbackModuleName(Module mod, string package, string name, int badness) {
sinkHasPrimaryName(getASinkNode(mod.getDefaultOrBulkExport()), package, name, badness)
or
badness = 50 and
package = getPackageRelativePath(mod) and
name = ""
@@ -462,28 +484,6 @@ module Debug {
)
}
/** Holds if the given `node` has multiple primary names. */
query string ambiguousClassObjectName(DataFlow::ClassNode node) {
strictcount(string package, string name | classObjectHasPrimaryName(node, package, name)) > 1 and
result =
concat(string package, string name |
classObjectHasPrimaryName(node, package, name)
|
renderName(package, name), ", "
)
}
/** Holds if the given `node` has multiple primary names. */
query string ambiguousClassInstanceName(DataFlow::ClassNode node) {
strictcount(string package, string name | classInstanceHasPrimaryName(node, package, name)) > 1 and
result =
concat(string package, string name |
classInstanceHasPrimaryName(node, package, name)
|
renderName(package, name), ", "
)
}
/** Holds if the given `node` has multiple primary names. */
query string ambiguousFunctionName(DataFlow::FunctionNode node) {
strictcount(string package, string name | functionHasPrimaryName(node, package, name)) > 1 and

View File

@@ -1,3 +1,7 @@
## 0.8.10
No user-facing changes.
## 0.8.9
### Bug Fixes

View File

@@ -0,0 +1,3 @@
## 0.8.10
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.8.9
lastReleaseVersion: 0.8.10

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-queries
version: 0.8.9
version: 0.8.10
groups:
- javascript
- queries

View File

@@ -1,7 +1,8 @@
testFailures
ambiguousPreferredPredecessor
| pack2/lib.js:1:1:3:1 | def moduleImport("pack2").getMember("exports").getMember("lib").getMember("LibClass").getInstance() |
| pack2/lib.js:8:22:8:34 | def moduleImport("pack2").getMember("exports").getMember("lib").getMember("LibClass").getMember("foo") |
| pack2/main.js:1:1:3:1 | def moduleImport("pack2").getMember("exports").getMember("MainClass").getInstance() |
ambiguousSinkName
ambiguousClassObjectName
ambiguousClassInstanceName
ambiguousFunctionName
failures

View File

@@ -5,28 +5,21 @@ import semmle.javascript.endpoints.EndpointNaming as EndpointNaming
import testUtilities.InlineExpectationsTest
import EndpointNaming::Debug
private predicate isIgnored(DataFlow::FunctionNode function) {
function.getFunction() = any(ConstructorDeclaration decl | decl.isSynthetic()).getBody()
}
module TestConfig implements TestSig {
string getARelevantTag() { result = ["instance", "class", "method", "alias"] }
string getARelevantTag() { result = ["name", "alias"] }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(string package, string name |
element = "" and
element = "" and
tag = "name" and
exists(DataFlow::SourceNode function, string package, string name |
EndpointNaming::functionHasPrimaryName(function, package, name) and
not isIgnored(function) and
location = function.getAstNode().getLocation() and
value = EndpointNaming::renderName(package, name)
|
exists(DataFlow::ClassNode cls | location = cls.getAstNode().getLocation() |
tag = "class" and
EndpointNaming::classObjectHasPrimaryName(cls, package, name)
or
tag = "instance" and
EndpointNaming::classInstanceHasPrimaryName(cls, package, name)
)
or
exists(DataFlow::FunctionNode function |
not function.getFunction() = any(ConstructorDeclaration decl | decl.isSynthetic()).getBody() and
location = function.getFunction().getLocation() and
tag = "method" and
EndpointNaming::functionHasPrimaryName(function, package, name)
)
)
or
element = "" and
@@ -35,7 +28,7 @@ module TestConfig implements TestSig {
API::Node aliasDef, string primaryPackage, string primaryName, string aliasPackage,
string aliasName
|
EndpointNaming::aliasDefinition(primaryPackage, primaryName, aliasPackage, aliasName, aliasDef) and
EndpointNaming::aliasDefinition(aliasPackage, aliasName, primaryPackage, primaryName, aliasDef) and
value =
EndpointNaming::renderName(aliasPackage, aliasName) + "==" +
EndpointNaming::renderName(primaryPackage, primaryName) and

View File

@@ -1,13 +1,15 @@
export class PublicClass {} // $ class=(pack1).PublicClass instance=(pack1).PublicClass.prototype
export class PublicClass {} // $ name=(pack1).PublicClass
class PrivateClass {}
export const ExportedConst = class ExportedConstClass {} // $ class=(pack1).ExportedConst instance=(pack1).ExportedConst.prototype
export const ExportedConst = class ExportedConstClass {} // $ name=(pack1).ExportedConst
class ClassWithEscapingInstance {} // $ instance=(pack1).ClassWithEscapingInstance.prototype
class ClassWithEscapingInstance {
m() {} // $ name=(pack1).ClassWithEscapingInstance.prototype.m
}
export function getEscapingInstance() {
return new ClassWithEscapingInstance();
} // $ method=(pack1).getEscapingInstance
} // $ name=(pack1).getEscapingInstance
export function publicFunction() {} // $ method=(pack1).publicFunction
export function publicFunction() {} // $ name=(pack1).publicFunction

View File

@@ -1 +1 @@
export default class FooClass {} // $ class=(pack10).Foo instance=(pack10).Foo.prototype
export default class FooClass {} // $ name=(pack10).Foo

View File

@@ -0,0 +1,59 @@
const f1 = {
m() {} // $ name=(pack11).C1.publicField.really.long.name.m
};
export class C1 {
private static privateField = f1;
public static publicField = {
really: {
long: {
name: f1
}
}
}
} // $ name=(pack11).C1
const f2 = {
m() {} // $ name=(pack11).C2.publicField.really.long.name.m
}
export class C2 {
static #privateField = f2;
static publicField = {
really: {
long: {
name: f2
}
}
}
} // $ name=(pack11).C2
function f3() {} // $ name=(pack11).C3.publicField.really.long.name
export class C3 {
private static privateField = f3;
public static publicField = {
really: {
long: {
name: f3
}
}
}
} // $ name=(pack11).C3
const f4 = {
m() {} // $ name=(pack11).C4.really.long.name.m
};
export const C4 = {
[Math.random()]: f4,
really: {
long: {
name: f4
}
}
}

View File

@@ -0,0 +1,4 @@
{
"name": "pack11",
"main": "./index.js"
}

View File

@@ -0,0 +1,10 @@
function wrap(fn) {
return x => fn(x);
}
function f() {}
export const f1 = wrap(f); // $ name=(pack12).f1
export const f2 = wrap(f); // $ name=(pack12).f2
function g() {}
export const g1 = wrap(g); // $ name=(pack12).g1

View File

@@ -0,0 +1,4 @@
{
"name": "pack12",
"main": "./index.js"
}

View File

@@ -1,6 +1,8 @@
class AmbiguousClass {
instanceMethod(foo) {} // $ method=(pack2).lib.LibClass.prototype.instanceMethod
} // $ class=(pack2).lib.LibClass instance=(pack2).lib.LibClass.prototype
instanceMethod(foo) {} // $ name=(pack2).lib.LibClass.prototype.instanceMethod
} // $ name=(pack2).lib.LibClass
export default AmbiguousClass; // $ alias=(pack2).lib.default==(pack2).lib.LibClass
export { AmbiguousClass as LibClass }
AmbiguousClass.foo = function() {} // $ name=(pack2).lib.LibClass.foo

View File

@@ -1,6 +1,6 @@
class AmbiguousClass {
instanceMethod() {} // $ method=(pack2).MainClass.prototype.instanceMethod
} // $ class=(pack2).MainClass instance=(pack2).MainClass.prototype
instanceMethod() {} // $ name=(pack2).MainClass.prototype.instanceMethod
} // $ name=(pack2).MainClass
export default AmbiguousClass; // $ alias=(pack2).default==(pack2).MainClass
export { AmbiguousClass as MainClass }

View File

@@ -1 +1 @@
export default function(x,y,z) {} // $ method=(pack3).libFunction alias=(pack3).libFunction.default==(pack3).libFunction
export default function(x,y,z) {} // $ name=(pack3).libFunction alias=(pack3).libFunction.default==(pack3).libFunction

View File

@@ -1,4 +1,4 @@
function ambiguousFunction(x, y, z) {} // $ method=(pack3).namedFunction
function ambiguousFunction(x, y, z) {} // $ name=(pack3).namedFunction
export default ambiguousFunction; // $ alias=(pack3).default==(pack3).namedFunction
export { ambiguousFunction as namedFunction };

View File

@@ -1 +1 @@
export default class C {} // $ class=(pack4) instance=(pack4).prototype
export default class C {} // $ name=(pack4)

View File

@@ -1 +1 @@
export default class C {} // $ class=(pack5) instance=(pack5).prototype
export default class C {} // $ name=(pack5)

View File

@@ -1,6 +1,6 @@
class C {
instanceMethod() {} // $ method=(pack6).instanceMethod
instanceMethod() {} // $ name=(pack6).instanceMethod
static staticMethod() {} // not accessible
} // $ instance=(pack6)
}
export default new C();

View File

@@ -1,6 +1,6 @@
export class D {} // $ class=(pack7).D instance=(pack7).D.prototype
export class D {} // $ name=(pack7).D
// In this case we are forced to include ".default" to avoid ambiguity with class D above.
export default {
D: class {} // $ class=(pack7).default.D instance=(pack7).default.D.prototype
D: class {} // $ name=(pack7).default.D
};

View File

@@ -1,4 +1,4 @@
class Foo {} // $ class=(pack8).Foo instance=(pack8).Foo.prototype
class Foo {} // $ name=(pack8).Foo
module.exports = Foo;
module.exports.default = Foo; // $ alias=(pack8).Foo.default==(pack8).Foo

View File

@@ -1,4 +1,4 @@
class Main {} // $ class=(pack8) instance=(pack8).prototype
class Main {} // $ name=(pack8)
Main.Foo = require('./foo');

View File

@@ -1 +1 @@
export class Foo {} // $ instance=(pack9/foo).Foo.prototype
export class Foo {}

View File

@@ -6,4 +6,4 @@ import * as foo from "./foo";
export function expose() {
return new foo.Foo(); // expose an instance of Foo but not the class
} // $ method=(pack9).expose
} // $ name=(pack9).expose

View File

@@ -10,7 +10,7 @@
| highlight.js:19:56:19:61 | [^\\]]+ | Strings starting with '[' and with many repetitions of '.[' can start matching anywhere after the start of the preceeding (\\.\|\\.\\/\|\\/)?(""\|"[^"]+"\|''\|'[^']+'\|\\[\\]\|\\[[^\\]]+\\]\|[^\\s!"#%&'()*+,.\\/;<=>@\\[\\\\\\]^`{\|}~]+)((\\.\|\\/)(""\|"[^"]+"\|''\|'[^']+'\|\\[\\]\|\\[[^\\]]+\\]\|[^\\s!"#%&'()*+,.\\/;<=>@\\[\\\\\\]^`{\|}~]+))* |
| highlight.js:22:12:22:82 | ((decltype\\(auto\\)\|(?:[a-zA-Z_]\\w*::)?[a-zA-Z_]\\w*(?:<.*?>)?)[\\*&\\s]+)+ | Strings with many repetitions of 'A\\t' can start matching anywhere after the start of the preceeding .*? |
| highlight.js:22:43:22:45 | \\w* | Strings starting with 'A' and with many repetitions of 'A' can start matching anywhere after the start of the preceeding .*? |
| highlight.js:22:66:22:68 | .*? | Strings starting with 'A<' and with many repetitions of 'A<' can start matching anywhere after the start of the preceeding \\w* |
| highlight.js:22:66:22:68 | .*? | Strings starting with 'A<' and with many repetitions of 'a<' can start matching anywhere after the start of the preceeding \\w* |
| highlight.js:22:73:22:80 | [\\*&\\s]+ | Strings starting with 'A' and with many repetitions of '\\tA\\t' can start matching anywhere after the start of the preceeding .*? |
| highlight.js:23:13:23:82 | ((decltype\\(auto\\)\|([a-zA-Z_]\\w*::)?[a-zA-Z_]\\w*(<[^<>]+>)?)[\\*&\\s]+)+ | Strings with many repetitions of 'A\\t' can start matching anywhere after the start of the preceeding ((decltype\\(auto\\)\|([a-zA-Z_]\\w*::)?[a-zA-Z_]\\w*(<[^<>]+>)?)[\\*&\\s]+)+([a-zA-Z_]\\w*::)?[a-zA-Z]\\w*\\s*\\( |
| highlight.js:23:42:23:44 | \\w* | Strings starting with 'A' and with many repetitions of 'A' can start matching anywhere after the start of the preceeding ((decltype\\(auto\\)\|([a-zA-Z_]\\w*::)?[a-zA-Z_]\\w*(<[^<>]+>)?)[\\*&\\s]+)+([a-zA-Z_]\\w*::)?[a-zA-Z]\\w*\\s*\\( |
@@ -279,7 +279,7 @@
| regexplib/misc.js:117:25:117:26 | .+ | Strings starting with '(a}' and with many repetitions of 'a)' can start matching anywhere after the start of the preceeding .+ |
| regexplib/misc.js:119:20:119:22 | \\w+ | Strings with many repetitions of '0' can start matching anywhere after the start of the preceeding (NOT)?(\\s*\\(*)\\s*(\\w+)\\s*(=\|<>\|<\|>\|LIKE\|IN)\\s*(\\(([^\\)]*)\\)\|'([^']*)'\|(-?\\d*\\.?\\d+))(\\s*\\)*\\s*)(AND\|OR)? |
| regexplib/misc.js:119:52:119:57 | [^\\)]* | Strings starting with '0=(' and with many repetitions of '0<((' can start matching anywhere after the start of the preceeding (NOT)?(\\s*\\(*)\\s*(\\w+)\\s*(=\|<>\|<\|>\|LIKE\|IN)\\s*(\\(([^\\)]*)\\)\|'([^']*)'\|(-?\\d*\\.?\\d+))(\\s*\\)*\\s*)(AND\|OR)? |
| regexplib/misc.js:123:36:123:38 | .*? | Strings starting with '?se[A' and with many repetitions of '?se[Aa' can start matching anywhere after the start of the preceeding (?s)(?:\\e\\[(?:(\\d+);?)*([A-Za-z])(.*?))(?=\\e\\[\|\\z) |
| regexplib/misc.js:123:36:123:38 | .*? | Strings starting with '?se[A' and with many repetitions of '?se[aa' can start matching anywhere after the start of the preceeding (?s)(?:\\e\\[(?:(\\d+);?)*([A-Za-z])(.*?))(?=\\e\\[\|\\z) |
| regexplib/misc.js:126:15:126:20 | [a-z]+ | Strings starting with 'a' and with many repetitions of 'aa' can start matching anywhere after the start of the preceeding [a-z]+ |
| regexplib/misc.js:141:15:141:19 | [^;]+ | Strings starting with '{\\\\f\\\\' and with many repetitions of '{\\\\f\\\\:' can start matching anywhere after the start of the preceeding (\\{\\\\f\\d*)\\\\([^;]+;) |
| regexplib/misc.js:144:52:144:70 | [a-z0-9\\/\\.\\?\\=\\&]* | Strings starting with '".htm' and with many repetitions of '.asp' can start matching anywhere after the start of the preceeding [a-z0-9\\/\\.\\?\\=\\&]* |
@@ -334,7 +334,7 @@
| regexplib/strings.js:54:20:54:22 | \\w+ | Strings with many repetitions of '0' can start matching anywhere after the start of the preceeding (NOT)?(\\s*\\(*)\\s*(\\w+)\\s*(=\|<>\|<\|>\|LIKE\|IN)\\s*(\\(([^\\)]*)\\)\|'([^']*)'\|(-?\\d*\\.?\\d+))(\\s*\\)*\\s*)(AND\|OR)? |
| regexplib/strings.js:54:52:54:57 | [^\\)]* | Strings starting with '0=(' and with many repetitions of '0<((' can start matching anywhere after the start of the preceeding (NOT)?(\\s*\\(*)\\s*(\\w+)\\s*(=\|<>\|<\|>\|LIKE\|IN)\\s*(\\(([^\\)]*)\\)\|'([^']*)'\|(-?\\d*\\.?\\d+))(\\s*\\)*\\s*)(AND\|OR)? |
| regexplib/strings.js:56:52:56:53 | .+ | Strings starting with 'AUX.' and with many repetitions of '.' can start matching anywhere after the start of the preceeding .* |
| regexplib/strings.js:57:36:57:38 | .*? | Strings starting with '?se[A' and with many repetitions of '?se[Aa' can start matching anywhere after the start of the preceeding (?s)(?:\\e\\[(?:(\\d+);?)*([A-Za-z])(.*?))(?=\\e\\[\|\\z) |
| regexplib/strings.js:57:36:57:38 | .*? | Strings starting with '?se[A' and with many repetitions of '?se[aa' can start matching anywhere after the start of the preceeding (?s)(?:\\e\\[(?:(\\d+);?)*([A-Za-z])(.*?))(?=\\e\\[\|\\z) |
| regexplib/strings.js:64:3:64:5 | \\w+ | Strings with many repetitions of '0' can start matching anywhere after the start of the preceeding (\\w+)\\s+\\1 |
| regexplib/strings.js:70:6:70:17 | [a-zA-Z,\\s]+ | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding \\s* |
| regexplib/strings.js:70:18:70:20 | \\s* | Strings starting with '\\t' and with many repetitions of '\\t' can start matching anywhere after the start of the preceeding \\s* |
@@ -345,7 +345,7 @@
| regexplib/strings.js:74:2:74:3 | .* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding .*[Pp]re[Ss\\$]cr[iI1]pt.* |
| regexplib/strings.js:75:2:75:3 | .* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding .*[Vv][Ii1]agr.* |
| regexplib/strings.js:76:2:76:3 | .* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding .*[Oo0][Ee][Mm].* |
| regexplib/strings.js:81:36:81:38 | .*? | Strings starting with '?se[A' and with many repetitions of '?se[Aa' can start matching anywhere after the start of the preceeding (?s)(?:\\e\\[(?:(\\d+);?)*([A-Za-z])(.*?))(?=\\e\\[\|\\z) |
| regexplib/strings.js:81:36:81:38 | .*? | Strings starting with '?se[A' and with many repetitions of '?se[aa' can start matching anywhere after the start of the preceeding (?s)(?:\\e\\[(?:(\\d+);?)*([A-Za-z])(.*?))(?=\\e\\[\|\\z) |
| regexplib/strings.js:82:20:82:22 | \\w+ | Strings with many repetitions of '0' can start matching anywhere after the start of the preceeding (NOT)?(\\s*\\(*)\\s*(\\w+)\\s*(=\|<>\|<\|>\|LIKE\|IN)\\s*(\\(([^\\)]*)\\)\|'([^']*)'\|(-?\\d*\\.?\\d+))(\\s*\\)*\\s*)(AND\|OR)? |
| regexplib/strings.js:82:52:82:57 | [^\\)]* | Strings starting with '0=(' and with many repetitions of '0<((' can start matching anywhere after the start of the preceeding (NOT)?(\\s*\\(*)\\s*(\\w+)\\s*(=\|<>\|<\|>\|LIKE\|IN)\\s*(\\(([^\\)]*)\\)\|'([^']*)'\|(-?\\d*\\.?\\d+))(\\s*\\)*\\s*)(AND\|OR)? |
| regexplib/strings.js:88:3:88:12 | [^\\.\\?\\!]* | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding ([^\\.\\?\\!]*)[\\.\\?\\!] |