Skip to content

Commit 29258ad

Browse files
committed
WIP new aliasing rule
1 parent 4ef1ac9 commit 29258ad

File tree

3 files changed

+68
-20
lines changed

3 files changed

+68
-20
lines changed

javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,70 @@ predicate functionHasPrimaryName(DataFlow::FunctionNode function, string package
352352
functionHasPrimaryName(function, package, name, _)
353353
}
354354

355+
private predicate sourceNodeHasNameCandidate(
356+
DataFlow::SourceNode node, string package, string name, int badness
357+
) {
358+
sinkHasPrimaryName(getASinkNode(node), package, name, badness)
359+
or
360+
functionHasNameCandidate(node, package, name, badness)
361+
or
362+
classObjectHasNameCandidate(node, package, name, badness)
363+
}
364+
365+
private predicate sourceNodeHasPrimaryName(
366+
DataFlow::SourceNode node, string package, string name, int badness
367+
) {
368+
badness = min(int b | sourceNodeHasNameCandidate(node, _, _, b) | b) and
369+
package =
370+
min(string p | sourceNodeHasNameCandidate(node, p, _, badness) | p order by p.length(), p) and
371+
name =
372+
min(string n | sourceNodeHasNameCandidate(node, package, n, badness) | n order by n.length(), n)
373+
}
374+
375+
private predicate sinkHasSourceName(API::Node sink, string package, string name, int badness) {
376+
exists(DataFlow::SourceNode source |
377+
sink = getASinkNode(source) and
378+
sourceNodeHasPrimaryName(source, package, name, badness)
379+
)
380+
}
381+
382+
private predicate sinkHasPrimarySourceName(API::Node sink, string package, string name, int badness) {
383+
badness = min(int b | sinkHasSourceName(sink, _, _, b) | b) and
384+
package = min(string p | sinkHasSourceName(sink, p, _, badness) | p order by p.length(), p) and
385+
name = min(string n | sinkHasSourceName(sink, package, n, badness) | n order by n.length(), n)
386+
}
387+
388+
private predicate sinkHasPrimarySourceName(API::Node sink, string package, string name) {
389+
sinkHasPrimarySourceName(sink, package, name, _)
390+
}
391+
392+
private predicate aliasCandidate(
393+
string package, string name, string targetPackage, string targetName, API::Node aliasDef
394+
) {
395+
sinkHasPrimaryName(aliasDef, package, name) and
396+
sinkHasPrimarySourceName(aliasDef, targetPackage, targetName) and
397+
not (
398+
package = targetPackage and
399+
name = targetName
400+
)
401+
}
402+
403+
private predicate nonAlias(string package, string name) {
404+
// `(package, name)` appears to be an alias for multiple things. Treat it as a primary name instead.
405+
strictcount(string targetPackage, string targetName |
406+
aliasCandidate(package, name, targetPackage, targetName, _)
407+
) > 1
408+
or
409+
// Not all sinks with this name agree on the alias target
410+
exists(API::Node sink, string targetPackage, string targetName |
411+
aliasCandidate(package, name, targetPackage, targetName, _) and
412+
sinkHasPrimaryName(sink, package, name) and
413+
not sinkHasPrimarySourceName(sink, targetPackage, targetName, _)
414+
)
415+
}
416+
355417
/**
356-
* Holds if `(aliasPackage, aliasName)` is an alias for `(primaryPackage, primaryName)`,
418+
* Holds if `(package, name)` is an alias for `(targetPackage, targetName)`,
357419
* defined at `aliasDef`.
358420
*
359421
* Only the last component of an access path is reported as an alias, the prefix always
@@ -365,24 +427,10 @@ predicate functionHasPrimaryName(DataFlow::FunctionNode function, string package
365427
* reported separately.
366428
*/
367429
predicate aliasDefinition(
368-
string primaryPackage, string primaryName, string aliasPackage, string aliasName,
369-
API::Node aliasDef
430+
string package, string name, string targetPackage, string targetName, API::Node aliasDef
370431
) {
371-
exists(DataFlow::SourceNode source |
372-
classObjectHasPrimaryName(source, primaryPackage, primaryName)
373-
or
374-
functionHasPrimaryName(source, primaryPackage, primaryName)
375-
|
376-
aliasDef.getAValueReachingSink() = source and
377-
sinkHasPrimaryName(aliasDef, aliasPackage, aliasName, _) and
378-
not (
379-
primaryPackage = aliasPackage and
380-
primaryName = aliasName
381-
)
382-
)
383-
or
384-
sinkHasPrimaryName(aliasDef, primaryPackage, primaryName) and
385-
sinkHasAlias(aliasDef, aliasPackage, aliasName)
432+
aliasCandidate(package, name, targetPackage, targetName, aliasDef) and
433+
not nonAlias(package, name)
386434
}
387435

388436
/**

javascript/ql/test/library-tests/EndpointNaming/EndpointNaming.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ module TestConfig implements TestSig {
3535
API::Node aliasDef, string primaryPackage, string primaryName, string aliasPackage,
3636
string aliasName
3737
|
38-
EndpointNaming::aliasDefinition(primaryPackage, primaryName, aliasPackage, aliasName, aliasDef) and
38+
EndpointNaming::aliasDefinition(aliasPackage, aliasName, primaryPackage, primaryName, aliasDef) and
3939
value =
4040
EndpointNaming::renderName(aliasPackage, aliasName) + "==" +
4141
EndpointNaming::renderName(primaryPackage, primaryName) and

javascript/ql/test/library-tests/EndpointNaming/pack2/lib.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ class AmbiguousClass {
55
export default AmbiguousClass; // $ alias=(pack2).lib.default==(pack2).lib.LibClass
66
export { AmbiguousClass as LibClass }
77

8-
AmbiguousClass.foo = function() {} // $ method=(pack2).lib.LibClass.foo alias=(pack2).lib.default.foo==(pack2).lib.LibClass.foo
8+
AmbiguousClass.foo = function() {} // $ method=(pack2).lib.LibClass.foo

0 commit comments

Comments
 (0)