Skip to content

Commit 8a5b907

Browse files
committed
JS: Handle wrapper functions more gracefully
1 parent d96f29d commit 8a5b907

File tree

3 files changed

+32
-30
lines changed

3 files changed

+32
-30
lines changed

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

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -347,26 +347,6 @@ private predicate functionHasNameCandidate(
347347
nameFromExterns(function, package, name, badness)
348348
}
349349

350-
private predicate functionHasPrimaryName(
351-
DataFlow::FunctionNode function, string package, string name, int badness
352-
) {
353-
badness = min(int b | functionHasNameCandidate(function, _, _, b) | b) and
354-
package = min(string p | functionHasNameCandidate(function, p, _, badness) | p) and
355-
name =
356-
min(string n |
357-
functionHasNameCandidate(function, package, n, badness)
358-
|
359-
n order by n.length(), n
360-
)
361-
}
362-
363-
/**
364-
* Holds if `(package, name)` is the primary name for the given `function`.
365-
*/
366-
predicate functionHasPrimaryName(DataFlow::FunctionNode function, string package, string name) {
367-
functionHasPrimaryName(function, package, name, _)
368-
}
369-
370350
private predicate sourceNodeHasNameCandidate(
371351
DataFlow::SourceNode node, string package, string name, int badness
372352
) {
@@ -387,6 +367,29 @@ private predicate sourceNodeHasPrimaryName(
387367
min(string n | sourceNodeHasNameCandidate(node, package, n, badness) | n order by n.length(), n)
388368
}
389369

370+
/**
371+
* Holds if `node` is a function or a call that returns a function.
372+
*/
373+
private predicate isFunctionSource(DataFlow::SourceNode node) {
374+
node instanceof DataFlow::FunctionNode
375+
or
376+
node instanceof DataFlow::InvokeNode and
377+
exists(node.getABoundFunctionValue(_)) and
378+
// `getASinkNode` steps through imports (but not other calls) so exclude calls that are imports (i.e. require calls)
379+
// as we want to get as close to the source as possible.
380+
not node instanceof DataFlow::ModuleImportNode
381+
}
382+
383+
/**
384+
* Holds if `(package, name)` is the primary name for the given `function`.
385+
*
386+
* The `function` node may be an actual function expression, or a call site from which a function is returned.
387+
*/
388+
predicate functionHasPrimaryName(DataFlow::SourceNode function, string package, string name) {
389+
sourceNodeHasPrimaryName(function, package, name, _) and
390+
isFunctionSource(function)
391+
}
392+
390393
private predicate sinkHasSourceName(API::Node sink, string package, string name, int badness) {
391394
exists(DataFlow::SourceNode source |
392395
sink = getASinkNode(source) and

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ testFailures
22
| pack11/index.ts:33:1:33:16 | | Unexpected result: method=(pack11).C3.privateField |
33
| pack11/index.ts:33:18:33:69 | // $ me ... ng.name | Missing result:method=(pack11).C3.publicField.really.long.name |
44
| pack11/index.ts:41:23:41:24 | | Unexpected result: alias=(pack11).C3.publicField.really.long.name==(pack11).C3.privateField |
5-
| pack12/index.js:2:12:2:21 | | Unexpected result: method=(pack12).f1 |
6-
| pack12/index.js:6:28:6:50 | // $ me ... k12).f1 | Missing result:method=(pack12).f1 |
7-
| pack12/index.js:7:19:7:25 | | Unexpected result: alias=(pack12).f2==(pack12).f1 |
8-
| pack12/index.js:7:28:7:50 | // $ me ... k12).f2 | Missing result:method=(pack12).f2 |
9-
| pack12/index.js:10:19:10:25 | | Unexpected result: alias=(pack12).g1==(pack12).f1 |
10-
| pack12/index.js:10:28:10:50 | // $ me ... k12).g1 | Missing result:method=(pack12).g1 |
115
failures
126
ambiguousPreferredPredecessor
137
| pack2/lib.js:1:1:3:1 | def moduleImport("pack2").getMember("exports").getMember("lib").getMember("LibClass").getInstance() |

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import semmle.javascript.endpoints.EndpointNaming as EndpointNaming
55
import testUtilities.InlineExpectationsTest
66
import EndpointNaming::Debug
77

8+
private predicate isIgnored(DataFlow::FunctionNode function) {
9+
function.getFunction() = any(ConstructorDeclaration decl | decl.isSynthetic()).getBody()
10+
}
11+
812
module TestConfig implements TestSig {
913
string getARelevantTag() { result = ["instance", "class", "method", "alias"] }
1014

@@ -21,11 +25,12 @@ module TestConfig implements TestSig {
2125
EndpointNaming::classInstanceHasPrimaryName(cls, package, name)
2226
)
2327
or
24-
exists(DataFlow::FunctionNode function |
25-
not function.getFunction() = any(ConstructorDeclaration decl | decl.isSynthetic()).getBody() and
26-
location = function.getFunction().getLocation() and
28+
exists(DataFlow::SourceNode function |
29+
not isIgnored(function) and
30+
location = function.getAstNode().getLocation() and
2731
tag = "method" and
28-
EndpointNaming::functionHasPrimaryName(function, package, name)
32+
EndpointNaming::functionHasPrimaryName(function, package, name) and
33+
not function instanceof DataFlow::ClassNode // reported with class tag
2934
)
3035
)
3136
or

0 commit comments

Comments
 (0)