Merge pull request #15380 from asgerf/js/endpoint-naming

JS: Add library for naming endpoints
This commit is contained in:
Asger F
2024-02-14 10:48:13 +01:00
committed by GitHub
35 changed files with 730 additions and 14 deletions

View File

@@ -0,0 +1,7 @@
testFailures
ambiguousPreferredPredecessor
ambiguousSinkName
ambiguousClassObjectName
ambiguousClassInstanceName
ambiguousFunctionName
failures

View File

@@ -0,0 +1,47 @@
import javascript
import semmle.javascript.RestrictedLocations
import semmle.javascript.Lines
import semmle.javascript.endpoints.EndpointNaming as EndpointNaming
import testUtilities.InlineExpectationsTest
import EndpointNaming::Debug
module TestConfig implements TestSig {
string getARelevantTag() { result = ["instance", "class", "method", "alias"] }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(string package, string name |
element = "" 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
tag = "alias" and
exists(
API::Node aliasDef, string primaryPackage, string primaryName, string aliasPackage,
string aliasName
|
EndpointNaming::aliasDefinition(primaryPackage, primaryName, aliasPackage, aliasName, aliasDef) and
value =
EndpointNaming::renderName(aliasPackage, aliasName) + "==" +
EndpointNaming::renderName(primaryPackage, primaryName) and
location = aliasDef.asSink().asExpr().getLocation()
)
}
}
import MakeTest<TestConfig>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
import { default as Foo } from "./foo";
export { Foo }

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
class AmbiguousClass {
instanceMethod() {} // $ method=(pack2).MainClass.prototype.instanceMethod
} // $ class=(pack2).MainClass instance=(pack2).MainClass.prototype
export default AmbiguousClass; // $ alias=(pack2).default==(pack2).MainClass
export { AmbiguousClass as MainClass }
import * as lib from "./lib";
export { lib }

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
function ambiguousFunction(x, y, z) {} // $ method=(pack3).namedFunction
export default ambiguousFunction; // $ alias=(pack3).default==(pack3).namedFunction
export { ambiguousFunction as namedFunction };
import libFunction from "./lib";
export { libFunction };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,6 @@
export class D {} // $ class=(pack7).D instance=(pack7).D.prototype
// 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
};

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
class Main {} // $ class=(pack8) instance=(pack8).prototype
Main.Foo = require('./foo');
module.exports = Main;

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
// Only the type is exposed. For the time being we do not consider type-only declarations or .d.ts files
// when naming classes.
export type { Foo } from "./foo";
import * as foo from "./foo";
export function expose() {
return new foo.Foo(); // expose an instance of Foo but not the class
} // $ method=(pack9).expose

View File

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

View File

@@ -3,6 +3,7 @@ groups: [javascript, test]
dependencies:
codeql/javascript-all: ${workspace}
codeql/javascript-queries: ${workspace}
codeql/util: ${workspace}
extractor: javascript
tests: .
warnOnImplicitThis: true

View File

@@ -0,0 +1,8 @@
/**
* Inline expectation tests for JS.
* See `shared/util/codeql/util/test/InlineExpectationsTest.qll`
*/
private import codeql.util.test.InlineExpectationsTest
private import internal.InlineExpectationsTestImpl
import Make<Impl>

View File

@@ -0,0 +1,12 @@
private import javascript as JS
private import codeql.util.test.InlineExpectationsTest
module Impl implements InlineExpectationsTestSig {
private import javascript
class ExpectationComment extends LineComment {
string getContents() { result = this.getText() }
}
class Location = JS::Location;
}