Update model editor queries
This updates the model editor queries to the version that will be merged into the CodeQL repository. There are some slight changes to the output format, so we slightly need to change the BQRS decoding of those queries. The queries themselves were copied from the two PRs with some minor additions at the end since these were changes in core CodeQL library files.
This commit is contained in:
@@ -2,41 +2,72 @@ import { DecodedBqrsChunk } from "../common/bqrs-cli-types";
|
||||
import { Call, CallClassification, Method } from "./method";
|
||||
import { ModeledMethodType } from "./modeled-method";
|
||||
import { parseLibraryFilename } from "./library";
|
||||
import { Mode } from "./shared/mode";
|
||||
import { ApplicationModeTuple, FrameworkModeTuple } from "./queries/query";
|
||||
|
||||
export function decodeBqrsToMethods(chunk: DecodedBqrsChunk): Method[] {
|
||||
export function decodeBqrsToMethods(
|
||||
chunk: DecodedBqrsChunk,
|
||||
mode: Mode,
|
||||
): Method[] {
|
||||
const methodsByApiName = new Map<string, Method>();
|
||||
|
||||
chunk?.tuples.forEach((tuple) => {
|
||||
const usage = tuple[0] as Call;
|
||||
const signature = tuple[1] as string;
|
||||
const supported = (tuple[2] as string) === "true";
|
||||
let library = tuple[4] as string;
|
||||
let libraryVersion: string | undefined = tuple[5] as string;
|
||||
const type = tuple[6] as ModeledMethodType;
|
||||
const classification = tuple[8] as CallClassification;
|
||||
let usage: Call;
|
||||
let packageName: string;
|
||||
let typeName: string;
|
||||
let methodName: string;
|
||||
let methodParameters: string;
|
||||
let supported: boolean;
|
||||
let library: string;
|
||||
let libraryVersion: string | undefined;
|
||||
let type: ModeledMethodType;
|
||||
let classification: CallClassification;
|
||||
|
||||
const [packageWithType, methodDeclaration] = signature.split("#");
|
||||
if (mode === Mode.Application) {
|
||||
[
|
||||
usage,
|
||||
packageName,
|
||||
typeName,
|
||||
methodName,
|
||||
methodParameters,
|
||||
supported,
|
||||
library,
|
||||
libraryVersion,
|
||||
type,
|
||||
classification,
|
||||
] = tuple as ApplicationModeTuple;
|
||||
} else {
|
||||
[
|
||||
usage,
|
||||
packageName,
|
||||
typeName,
|
||||
methodName,
|
||||
methodParameters,
|
||||
supported,
|
||||
library,
|
||||
type,
|
||||
] = tuple as FrameworkModeTuple;
|
||||
|
||||
const packageName = packageWithType.substring(
|
||||
0,
|
||||
packageWithType.lastIndexOf("."),
|
||||
);
|
||||
const typeName = packageWithType.substring(
|
||||
packageWithType.lastIndexOf(".") + 1,
|
||||
);
|
||||
classification = CallClassification.Unknown;
|
||||
}
|
||||
|
||||
const methodName = methodDeclaration.substring(
|
||||
0,
|
||||
methodDeclaration.indexOf("("),
|
||||
);
|
||||
const methodParameters = methodDeclaration.substring(
|
||||
methodDeclaration.indexOf("("),
|
||||
);
|
||||
if (!methodParameters.startsWith("(")) {
|
||||
// There's a difference in how the Java and C# queries return method parameters. In the C# query, the method
|
||||
// parameters are returned without parentheses. In the Java query, the method parameters are returned with
|
||||
// parentheses. Therefore, we'll just add them if we don't see them.
|
||||
methodParameters = `(${methodParameters})`;
|
||||
}
|
||||
|
||||
const signature = `${packageName}.${typeName}#${methodName}${methodParameters}`;
|
||||
|
||||
// For Java, we'll always get back a .jar file, and the library version may be bad because not all library authors
|
||||
// properly specify the version. Therefore, we'll always try to parse the name and version from the library filename
|
||||
// for Java.
|
||||
if (library.endsWith(".jar") || libraryVersion === "") {
|
||||
if (
|
||||
library.endsWith(".jar") ||
|
||||
libraryVersion === "" ||
|
||||
libraryVersion === undefined
|
||||
) {
|
||||
const { name, version } = parseLibraryFilename(library);
|
||||
library = name;
|
||||
if (version) {
|
||||
|
||||
@@ -132,7 +132,7 @@ export async function runExternalApiQueries(
|
||||
maxStep: externalApiQueriesProgressMaxStep,
|
||||
});
|
||||
|
||||
return decodeBqrsToMethods(bqrsChunk);
|
||||
return decodeBqrsToMethods(bqrsChunk, mode);
|
||||
}
|
||||
|
||||
type GetResultsOptions = {
|
||||
@@ -160,7 +160,5 @@ export async function readQueryResults({
|
||||
}
|
||||
|
||||
function queryNameFromMode(mode: Mode): string {
|
||||
return `FetchExternalApis${
|
||||
mode.charAt(0).toUpperCase() + mode.slice(1)
|
||||
}Mode.ql`;
|
||||
return `${mode.charAt(0).toUpperCase() + mode.slice(1)}ModeEndpoints.ql`;
|
||||
}
|
||||
|
||||
@@ -2,130 +2,152 @@ import { Query } from "./query";
|
||||
|
||||
export const fetchExternalApisQuery: Query = {
|
||||
applicationModeQuery: `/**
|
||||
* @name Usage of APIs coming from external libraries
|
||||
* @description A list of 3rd party APIs used in the codebase.
|
||||
* @tags telemetry
|
||||
* @kind problem
|
||||
* @id cs/telemetry/fetch-external-apis
|
||||
* @name Fetch endpoints for use in the model editor (application mode)
|
||||
* @description A list of 3rd party endpoints (methods and attributes) used in the codebase. Excludes test and generated code.
|
||||
* @kind table
|
||||
* @id csharp/utils/modeleditor/application-mode-endpoints
|
||||
* @tags modeleditor endpoints application-mode
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import AutomodelVsCode
|
||||
import csharp
|
||||
import ApplicationModeEndpointsQuery
|
||||
import ModelEditor
|
||||
|
||||
class ExternalApi extends CallableMethod {
|
||||
ExternalApi() {
|
||||
this.isUnboundDeclaration() and
|
||||
this.fromLibrary() and
|
||||
this.(Modifiable).isEffectivelyPublic()
|
||||
}
|
||||
}
|
||||
private Call aUsage(ExternalEndpoint api) { result.getTarget().getUnboundDeclaration() = api }
|
||||
|
||||
private Call aUsage(ExternalApi api) { result.getTarget().getUnboundDeclaration() = api }
|
||||
|
||||
from
|
||||
ExternalApi api, string apiName, boolean supported, Call usage, string type, string classification
|
||||
from ExternalEndpoint endpoint, boolean supported, Call usage, string type, string classification
|
||||
where
|
||||
apiName = api.getApiName() and
|
||||
supported = isSupported(api) and
|
||||
usage = aUsage(api) and
|
||||
type = supportedType(api) and
|
||||
supported = isSupported(endpoint) and
|
||||
usage = aUsage(endpoint) and
|
||||
type = supportedType(endpoint) and
|
||||
classification = methodClassification(usage)
|
||||
select usage, apiName, supported.toString(), "supported", api.dllName(), api.dllVersion(), type,
|
||||
"type", classification, "classification"
|
||||
select usage, endpoint.getNamespace(), endpoint.getTypeName(), endpoint.getName(),
|
||||
endpoint.getParameterTypes(), supported, endpoint.dllName(), endpoint.dllVersion(), type,
|
||||
classification
|
||||
`,
|
||||
frameworkModeQuery: `/**
|
||||
* @name Public methods
|
||||
* @description A list of APIs callable by consumers. Excludes test and generated code.
|
||||
* @tags telemetry
|
||||
* @kind problem
|
||||
* @id cs/telemetry/fetch-public-methods
|
||||
* @name Fetch endpoints for use in the model editor (framework mode)
|
||||
* @description A list of endpoints accessible (methods and attributes) for consumers of the library. Excludes test and generated code.
|
||||
* @kind table
|
||||
* @id csharp/utils/modeleditor/framework-mode-endpoints
|
||||
* @tags modeleditor endpoints framework-mode
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import dotnet
|
||||
private import semmle.code.csharp.frameworks.Test
|
||||
private import AutomodelVsCode
|
||||
import csharp
|
||||
import FrameworkModeEndpointsQuery
|
||||
import ModelEditor
|
||||
|
||||
class PublicMethod extends CallableMethod {
|
||||
PublicMethod() { this.fromSource() and not this.getFile() instanceof TestFile }
|
||||
}
|
||||
|
||||
from PublicMethod publicMethod, string apiName, boolean supported, string type
|
||||
from PublicEndpointFromSource endpoint, boolean supported, string type
|
||||
where
|
||||
apiName = publicMethod.getApiName() and
|
||||
supported = isSupported(publicMethod) and
|
||||
type = supportedType(publicMethod)
|
||||
select publicMethod, apiName, supported.toString(), "supported",
|
||||
publicMethod.getFile().getBaseName(), "library", type, "type", "unknown", "classification"
|
||||
supported = isSupported(endpoint) and
|
||||
type = supportedType(endpoint)
|
||||
select endpoint, endpoint.getNamespace(), endpoint.getTypeName(), endpoint.getName(),
|
||||
endpoint.getParameterTypes(), supported, endpoint.getFile().getBaseName(), type
|
||||
`,
|
||||
dependencies: {
|
||||
"AutomodelVsCode.qll": `/** Provides classes and predicates related to handling APIs for the VS Code extension. */
|
||||
|
||||
private import csharp
|
||||
private import dotnet
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
"ApplicationModeEndpointsQuery.qll": `private import csharp
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
private import semmle.code.csharp.dataflow.FlowSummary
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
private import semmle.code.csharp.frameworks.Test
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isTestNamespace(Namespace ns) {
|
||||
ns.getFullName()
|
||||
.matches([
|
||||
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
|
||||
])
|
||||
}
|
||||
private import ModelEditor
|
||||
|
||||
/**
|
||||
* A test library.
|
||||
* A class of effectively public callables in library code.
|
||||
*/
|
||||
class TestLibrary extends RefType {
|
||||
TestLibrary() { isTestNamespace(this.getNamespace()) }
|
||||
class ExternalEndpoint extends Endpoint {
|
||||
ExternalEndpoint() { this.fromLibrary() }
|
||||
|
||||
/** Gets a node that is an input to a call to this API. */
|
||||
private ArgumentNode getAnInput() {
|
||||
result
|
||||
.getCall()
|
||||
.(DataFlowDispatch::NonDelegateDataFlowCall)
|
||||
.getATarget(_)
|
||||
.getUnboundDeclaration() = this
|
||||
}
|
||||
|
||||
/** Gets a node that is an output from a call to this API. */
|
||||
private DataFlow::Node getAnOutput() {
|
||||
exists(Call c, DataFlowDispatch::NonDelegateDataFlowCall dc |
|
||||
dc.getDispatchCall().getCall() = c and
|
||||
c.getTarget().getUnboundDeclaration() = this
|
||||
|
|
||||
result = DataFlowDispatch::getAnOutNode(dc, _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasSummary() {
|
||||
Endpoint.super.hasSummary()
|
||||
or
|
||||
defaultAdditionalTaintStep(this.getAnInput(), _)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
|
||||
}
|
||||
|
||||
override predicate isSink() { sinkNode(this.getAnInput(), _) }
|
||||
}
|
||||
`,
|
||||
"FrameworkModeEndpointsQuery.qll": `private import csharp
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
private import semmle.code.csharp.frameworks.Test
|
||||
private import ModelEditor
|
||||
|
||||
/**
|
||||
* A class of effectively public callables from source code.
|
||||
*/
|
||||
class PublicEndpointFromSource extends Endpoint {
|
||||
PublicEndpointFromSource() { this.fromSource() and not this.getFile() instanceof TestFile }
|
||||
|
||||
override predicate isSource() { this instanceof SourceCallable }
|
||||
|
||||
override predicate isSink() { this instanceof SinkCallable }
|
||||
}`,
|
||||
"ModelEditor.qll": `/** Provides classes and predicates related to handling APIs for the VS Code extension. */
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dataflow.FlowSummary
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.frameworks.Test
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(DotNet::Declaration c) {
|
||||
private predicate isUninteresting(Callable c) {
|
||||
c.getDeclaringType() instanceof TestLibrary or
|
||||
c.(Constructor).isParameterless() or
|
||||
c.getDeclaringType() instanceof AnonymousClass
|
||||
}
|
||||
|
||||
/**
|
||||
* An callable method from either the C# Standard Library, a 3rd party library, or from the source.
|
||||
* A callable method or accessor from either the C# Standard Library, a 3rd party library, or from the source.
|
||||
*/
|
||||
class CallableMethod extends DotNet::Declaration {
|
||||
CallableMethod() {
|
||||
this.(Modifiable).isEffectivelyPublic() and
|
||||
not isUninteresting(this)
|
||||
class Endpoint extends Callable {
|
||||
Endpoint() {
|
||||
[this.(Modifiable), this.(Accessor).getDeclaration()].isEffectivelyPublic() and
|
||||
not isUninteresting(this) and
|
||||
this.isUnboundDeclaration()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unbound type, name and parameter types of this API.
|
||||
*/
|
||||
bindingset[this]
|
||||
private string getSignature() {
|
||||
result =
|
||||
nestedName(this.getDeclaringType().getUnboundDeclaration()) + "#" + this.getName() + "(" +
|
||||
parameterQualifiedTypeNamesToString(this) + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace of this API.
|
||||
* Gets the namespace of this endpoint.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) }
|
||||
|
||||
/**
|
||||
* Gets the namespace and signature of this API.
|
||||
* Gets the unbound type name of this endpoint.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getApiName() { result = this.getNamespace() + "." + this.getSignature() }
|
||||
string getTypeName() { result = nestedName(this.getDeclaringType().getUnboundDeclaration()) }
|
||||
|
||||
/**
|
||||
* Gets the parameter types of this endpoint.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getParameterTypes() { result = parameterQualifiedTypeNamesToString(this) }
|
||||
|
||||
private string getDllName() { result = this.getLocation().(Assembly).getName() }
|
||||
|
||||
@@ -143,44 +165,17 @@ class CallableMethod extends DotNet::Declaration {
|
||||
not exists(this.getDllVersion()) and result = ""
|
||||
}
|
||||
|
||||
/** Gets a node that is an input to a call to this API. */
|
||||
private ArgumentNode getAnInput() {
|
||||
result
|
||||
.getCall()
|
||||
.(DataFlowDispatch::NonDelegateDataFlowCall)
|
||||
.getATarget(_)
|
||||
.getUnboundDeclaration() = this
|
||||
}
|
||||
|
||||
/** Gets a node that is an output from a call to this API. */
|
||||
private DataFlow::Node getAnOutput() {
|
||||
exists(
|
||||
Call c, DataFlowDispatch::NonDelegateDataFlowCall dc, DataFlowImplCommon::ReturnKindExt ret
|
||||
|
|
||||
dc.getDispatchCall().getCall() = c and
|
||||
c.getTarget().getUnboundDeclaration() = this
|
||||
|
|
||||
result = ret.getAnOutNode(dc)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this API has a supported summary. */
|
||||
pragma[nomagic]
|
||||
predicate hasSummary() {
|
||||
this instanceof SummarizedCallable
|
||||
or
|
||||
defaultAdditionalTaintStep(this.getAnInput(), _)
|
||||
}
|
||||
predicate hasSummary() { this instanceof SummarizedCallable }
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
pragma[nomagic]
|
||||
predicate isSource() {
|
||||
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
/** Holds if this API is a known sink. */
|
||||
pragma[nomagic]
|
||||
predicate isSink() { sinkNode(this.getAnInput(), _) }
|
||||
abstract predicate isSink();
|
||||
|
||||
/** Holds if this API is a known neutral. */
|
||||
pragma[nomagic]
|
||||
@@ -195,23 +190,20 @@ class CallableMethod extends DotNet::Declaration {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSupported(CallableMethod callableMethod) {
|
||||
callableMethod.isSupported() and result = true
|
||||
or
|
||||
not callableMethod.isSupported() and
|
||||
result = false
|
||||
boolean isSupported(Endpoint endpoint) {
|
||||
if endpoint.isSupported() then result = true else result = false
|
||||
}
|
||||
|
||||
string supportedType(CallableMethod method) {
|
||||
method.isSink() and result = "sink"
|
||||
string supportedType(Endpoint endpoint) {
|
||||
endpoint.isSink() and result = "sink"
|
||||
or
|
||||
method.isSource() and result = "source"
|
||||
endpoint.isSource() and result = "source"
|
||||
or
|
||||
method.hasSummary() and result = "summary"
|
||||
endpoint.hasSummary() and result = "summary"
|
||||
or
|
||||
method.isNeutral() and result = "neutral"
|
||||
endpoint.isNeutral() and result = "neutral"
|
||||
or
|
||||
not method.isSupported() and result = ""
|
||||
not endpoint.isSupported() and result = ""
|
||||
}
|
||||
|
||||
string methodClassification(Call method) {
|
||||
@@ -222,18 +214,51 @@ string methodClassification(Call method) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nested name of the declaration.
|
||||
* Gets the nested name of the type \`t\`.
|
||||
*
|
||||
* If the declaration is not a nested type, the result is the same as \`getName()\`.
|
||||
* If the type is not a nested type, the result is the same as \`getName()\`.
|
||||
* Otherwise the name of the nested type is prefixed with a \`+\` and appended to
|
||||
* the name of the enclosing type, which might be a nested type as well.
|
||||
*/
|
||||
private string nestedName(Declaration declaration) {
|
||||
not exists(declaration.getDeclaringType().getUnboundDeclaration()) and
|
||||
result = declaration.getName()
|
||||
private string nestedName(Type t) {
|
||||
not exists(t.getDeclaringType().getUnboundDeclaration()) and
|
||||
result = t.getName()
|
||||
or
|
||||
nestedName(declaration.getDeclaringType().getUnboundDeclaration()) + "+" + declaration.getName() =
|
||||
result
|
||||
nestedName(t.getDeclaringType().getUnboundDeclaration()) + "+" + t.getName() = result
|
||||
}
|
||||
|
||||
// Temporary copy of csharp/ql/src/Telemetry/TestLibrary.qll
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isTestNamespace(Namespace ns) {
|
||||
ns.getFullName()
|
||||
.matches([
|
||||
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* A test library.
|
||||
*/
|
||||
class TestLibrary extends RefType {
|
||||
TestLibrary() { isTestNamespace(this.getNamespace()) }
|
||||
}
|
||||
|
||||
// Temporary copy of csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImplSpecific
|
||||
|
||||
/**
|
||||
* A callable where there exists a MaD sink model that applies to it.
|
||||
*/
|
||||
class SinkCallable extends Callable {
|
||||
SinkCallable() { sinkElement(this, _, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable where there exists a MaD source model that applies to it.
|
||||
*/
|
||||
class SourceCallable extends Callable {
|
||||
SourceCallable() { sourceElement(this, _, _, _) }
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
@@ -2,66 +2,113 @@ import { Query } from "./query";
|
||||
|
||||
export const fetchExternalApisQuery: Query = {
|
||||
applicationModeQuery: `/**
|
||||
* @name Usage of APIs coming from external libraries
|
||||
* @description A list of 3rd party APIs used in the codebase. Excludes test and generated code.
|
||||
* @tags telemetry
|
||||
* @kind problem
|
||||
* @id java/telemetry/fetch-external-apis
|
||||
* @name Fetch endpoints for use in the model editor (application mode)
|
||||
* @description A list of 3rd party endpoints (methods) used in the codebase. Excludes test and generated code.
|
||||
* @kind table
|
||||
* @id java/utils/modeleditor/application-mode-endpoints
|
||||
* @tags modeleditor endpoints application-mode
|
||||
*/
|
||||
|
||||
import java
|
||||
import AutomodelVsCode
|
||||
|
||||
class ExternalApi extends CallableMethod {
|
||||
ExternalApi() { not this.fromSource() }
|
||||
}
|
||||
|
||||
private Call aUsage(ExternalApi api) { result.getCallee().getSourceDeclaration() = api }
|
||||
|
||||
from
|
||||
ExternalApi externalApi, string apiName, boolean supported, Call usage, string type,
|
||||
string classification
|
||||
where
|
||||
apiName = externalApi.getApiName() and
|
||||
supported = isSupported(externalApi) and
|
||||
usage = aUsage(externalApi) and
|
||||
type = supportedType(externalApi) and
|
||||
classification = methodClassification(usage)
|
||||
select usage, apiName, supported.toString(), "supported", externalApi.jarContainer(),
|
||||
externalApi.jarVersion(), type, "type", classification, "classification"
|
||||
`,
|
||||
frameworkModeQuery: `/**
|
||||
* @name Public methods
|
||||
* @description A list of APIs callable by consumers. Excludes test and generated code.
|
||||
* @tags telemetry
|
||||
* @kind problem
|
||||
* @id java/telemetry/fetch-public-methods
|
||||
*/
|
||||
|
||||
import java
|
||||
import AutomodelVsCode
|
||||
|
||||
class PublicMethodFromSource extends CallableMethod, ModelApi { }
|
||||
|
||||
from PublicMethodFromSource publicMethod, string apiName, boolean supported, string type
|
||||
where
|
||||
apiName = publicMethod.getApiName() and
|
||||
supported = isSupported(publicMethod) and
|
||||
type = supportedType(publicMethod)
|
||||
select publicMethod, apiName, supported.toString(), "supported",
|
||||
publicMethod.getCompilationUnit().getParentContainer().getBaseName(), "library", type, "type",
|
||||
"unknown", "classification"
|
||||
`,
|
||||
dependencies: {
|
||||
"AutomodelVsCode.qll": `/** Provides classes and predicates related to handling APIs for the VS Code extension. */
|
||||
|
||||
private import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import ApplicationModeEndpointsQuery
|
||||
private import ModelEditor
|
||||
|
||||
private Call aUsage(ExternalEndpoint endpoint) {
|
||||
result.getCallee().getSourceDeclaration() = endpoint
|
||||
}
|
||||
|
||||
from ExternalEndpoint endpoint, boolean supported, Call usage, string type, string classification
|
||||
where
|
||||
supported = isSupported(endpoint) and
|
||||
usage = aUsage(endpoint) and
|
||||
type = supportedType(endpoint) and
|
||||
classification = usageClassification(usage)
|
||||
select usage, endpoint.getPackageName(), endpoint.getTypeName(), endpoint.getName(),
|
||||
endpoint.getParameterTypes(), supported, endpoint.jarContainer(), endpoint.jarVersion(), type,
|
||||
classification
|
||||
`,
|
||||
frameworkModeQuery: `/**
|
||||
* @name Fetch endpoints for use in the model editor (framework mode)
|
||||
* @description A list of endpoints accessible (methods) for consumers of the library. Excludes test and generated code.
|
||||
* @kind table
|
||||
* @id java/utils/modeleditor/framework-mode-endpoints
|
||||
* @tags modeleditor endpoints framework-mode
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import FrameworkModeEndpointsQuery
|
||||
private import ModelEditor
|
||||
|
||||
from PublicEndpointFromSource endpoint, boolean supported, string type
|
||||
where
|
||||
supported = isSupported(endpoint) and
|
||||
type = supportedType(endpoint)
|
||||
select endpoint, endpoint.getPackageName(), endpoint.getTypeName(), endpoint.getName(),
|
||||
endpoint.getParameterTypes(), supported,
|
||||
endpoint.getCompilationUnit().getParentContainer().getBaseName(), type
|
||||
`,
|
||||
dependencies: {
|
||||
"ApplicationModeEndpointsQuery.qll": `private import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.FlowSummary
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import ModelEditor
|
||||
|
||||
/**
|
||||
* A class of effectively public callables in library code.
|
||||
*/
|
||||
class ExternalEndpoint extends Endpoint {
|
||||
ExternalEndpoint() { not this.fromSource() }
|
||||
|
||||
/** Gets a node that is an input to a call to this API. */
|
||||
private DataFlow::Node getAnInput() {
|
||||
exists(Call call | call.getCallee().getSourceDeclaration() = this |
|
||||
result.asExpr().(Argument).getCall() = call or
|
||||
result.(ArgumentNode).getCall().asCall() = call
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a node that is an output from a call to this API. */
|
||||
private DataFlow::Node getAnOutput() {
|
||||
exists(Call call | call.getCallee().getSourceDeclaration() = this |
|
||||
result.asExpr() = call or
|
||||
result.(DataFlow::PostUpdateNode).getPreUpdateNode().(ArgumentNode).getCall().asCall() = call
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasSummary() {
|
||||
Endpoint.super.hasSummary()
|
||||
or
|
||||
TaintTracking::localAdditionalTaintStep(this.getAnInput(), _)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
|
||||
}
|
||||
|
||||
override predicate isSink() { sinkNode(this.getAnInput(), _) }
|
||||
}
|
||||
`,
|
||||
"FrameworkModeEndpointsQuery.qll": `private import java
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific
|
||||
private import semmle.code.java.dataflow.internal.ModelExclusions
|
||||
private import ModelEditor
|
||||
|
||||
/**
|
||||
* A class of effectively public callables from source code.
|
||||
*/
|
||||
class PublicEndpointFromSource extends Endpoint, ModelApi {
|
||||
override predicate isSource() { sourceElement(this, _, _, _) }
|
||||
|
||||
override predicate isSink() { sinkElement(this, _, _, _) }
|
||||
}
|
||||
`,
|
||||
"ModelEditor.qll": `/** Provides classes and predicates related to handling APIs for the VS Code extension. */
|
||||
|
||||
private import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSummary
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.internal.ModelExclusions
|
||||
|
||||
@@ -75,17 +122,23 @@ private predicate isUninteresting(Callable c) {
|
||||
/**
|
||||
* A callable method from either the Standard Library, a 3rd party library or from the source.
|
||||
*/
|
||||
class CallableMethod extends Callable {
|
||||
CallableMethod() { not isUninteresting(this) }
|
||||
class Endpoint extends Callable {
|
||||
Endpoint() { not isUninteresting(this) }
|
||||
|
||||
/**
|
||||
* Gets information about the external API in the form expected by the MaD modeling framework.
|
||||
* Gets the package name of this endpoint.
|
||||
*/
|
||||
string getApiName() {
|
||||
result =
|
||||
this.getDeclaringType().getPackage() + "." + this.getDeclaringType().nestedName() + "#" +
|
||||
this.getName() + paramsString(this)
|
||||
}
|
||||
string getPackageName() { result = this.getDeclaringType().getPackage().getName() }
|
||||
|
||||
/**
|
||||
* Gets the type name of this endpoint.
|
||||
*/
|
||||
string getTypeName() { result = this.getDeclaringType().nestedName() }
|
||||
|
||||
/**
|
||||
* Gets the parameter types of this endpoint.
|
||||
*/
|
||||
string getParameterTypes() { result = paramsString(this) }
|
||||
|
||||
private string getJarName() {
|
||||
result = this.getCompilationUnit().getParentContainer*().(JarFile).getBaseName()
|
||||
@@ -113,43 +166,23 @@ class CallableMethod extends Callable {
|
||||
not exists(this.getJarVersion()) and result = ""
|
||||
}
|
||||
|
||||
/** Gets a node that is an input to a call to this API. */
|
||||
private DataFlow::Node getAnInput() {
|
||||
exists(Call call | call.getCallee().getSourceDeclaration() = this |
|
||||
result.asExpr().(Argument).getCall() = call or
|
||||
result.(ArgumentNode).getCall().asCall() = call
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a node that is an output from a call to this API. */
|
||||
private DataFlow::Node getAnOutput() {
|
||||
exists(Call call | call.getCallee().getSourceDeclaration() = this |
|
||||
result.asExpr() = call or
|
||||
result.(DataFlow::PostUpdateNode).getPreUpdateNode().(ArgumentNode).getCall().asCall() = call
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this API has a supported summary. */
|
||||
pragma[nomagic]
|
||||
predicate hasSummary() {
|
||||
this = any(SummarizedCallable sc).asCallable() or
|
||||
TaintTracking::localAdditionalTaintStep(this.getAnInput(), _)
|
||||
}
|
||||
predicate hasSummary() { this = any(SummarizedCallable sc).asCallable() }
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
pragma[nomagic]
|
||||
predicate isSource() {
|
||||
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
/** Holds if this API is a known sink. */
|
||||
pragma[nomagic]
|
||||
predicate isSink() { sinkNode(this.getAnInput(), _) }
|
||||
abstract predicate isSink();
|
||||
|
||||
/** Holds if this API is a known neutral. */
|
||||
pragma[nomagic]
|
||||
predicate isNeutral() {
|
||||
exists(string namespace, string type, string name, string signature, string kind, string provenance |
|
||||
neutralModel(namespace, type, name, signature, kind, provenance) and
|
||||
exists(string namespace, string type, string name, string signature |
|
||||
neutralModel(namespace, type, name, signature, _, _) and
|
||||
this = interpretElement(namespace, type, false, name, signature, "")
|
||||
)
|
||||
}
|
||||
@@ -163,108 +196,38 @@ class CallableMethod extends Callable {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSupported(CallableMethod method) {
|
||||
method.isSupported() and result = true
|
||||
boolean isSupported(Endpoint endpoint) {
|
||||
endpoint.isSupported() and result = true
|
||||
or
|
||||
not method.isSupported() and result = false
|
||||
not endpoint.isSupported() and result = false
|
||||
}
|
||||
|
||||
string supportedType(CallableMethod method) {
|
||||
method.isSink() and result = "sink"
|
||||
string supportedType(Endpoint endpoint) {
|
||||
endpoint.isSink() and result = "sink"
|
||||
or
|
||||
method.isSource() and result = "source"
|
||||
endpoint.isSource() and result = "source"
|
||||
or
|
||||
method.hasSummary() and result = "summary"
|
||||
endpoint.hasSummary() and result = "summary"
|
||||
or
|
||||
method.isNeutral() and result = "neutral"
|
||||
endpoint.isNeutral() and result = "neutral"
|
||||
or
|
||||
not method.isSupported() and result = ""
|
||||
not endpoint.isSupported() and result = ""
|
||||
}
|
||||
|
||||
string methodClassification(Call method) {
|
||||
isInTestFile(method.getLocation().getFile()) and result = "test"
|
||||
string usageClassification(Call usage) {
|
||||
isInTestFile(usage.getLocation().getFile()) and result = "test"
|
||||
or
|
||||
method.getFile() instanceof GeneratedFile and result = "generated"
|
||||
usage.getFile() instanceof GeneratedFile and result = "generated"
|
||||
or
|
||||
not isInTestFile(method.getLocation().getFile()) and
|
||||
not method.getFile() instanceof GeneratedFile and
|
||||
not isInTestFile(usage.getLocation().getFile()) and
|
||||
not usage.getFile() instanceof GeneratedFile and
|
||||
result = "source"
|
||||
}
|
||||
|
||||
// The below is a copy of https://github.com/github/codeql/blob/249f9f863db1e94e3c46ca85b49fb0ec32f8ca92/java/ql/lib/semmle/code/java/dataflow/internal/ModelExclusions.qll
|
||||
// to avoid the use of internal modules.
|
||||
/** Holds if the given package \`p\` is a test package. */
|
||||
pragma[nomagic]
|
||||
private predicate isTestPackage(Package p) {
|
||||
p.getName()
|
||||
.matches([
|
||||
"org.junit%", "junit.%", "org.mockito%", "org.assertj%",
|
||||
"com.github.tomakehurst.wiremock%", "org.hamcrest%", "org.springframework.test.%",
|
||||
"org.springframework.mock.%", "org.springframework.boot.test.%", "reactor.test%",
|
||||
"org.xmlunit%", "org.testcontainers.%", "org.opentest4j%", "org.mockserver%",
|
||||
"org.powermock%", "org.skyscreamer.jsonassert%", "org.rnorth.visibleassertions",
|
||||
"org.openqa.selenium%", "com.gargoylesoftware.htmlunit%", "org.jboss.arquillian.testng%",
|
||||
"org.testng%"
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* A test library.
|
||||
*/
|
||||
class TestLibrary extends RefType {
|
||||
TestLibrary() { isTestPackage(this.getPackage()) }
|
||||
}
|
||||
|
||||
/** Holds if the given file is a test file. */
|
||||
private predicate isInTestFile(File file) {
|
||||
// Temporarily copied from java/ql/lib/semmle/code/java/dataflow/internal/ModelExclusions.qll
|
||||
predicate isInTestFile(File file) {
|
||||
file.getAbsolutePath().matches(["%/test/%", "%/guava-tests/%", "%/guava-testlib/%"]) and
|
||||
not file.getAbsolutePath().matches("%/ql/test/%") // allows our test cases to work
|
||||
}
|
||||
|
||||
/** Holds if the given compilation unit's package is a JDK internal. */
|
||||
private predicate isJdkInternal(CompilationUnit cu) {
|
||||
cu.getPackage().getName().matches("org.graalvm%") or
|
||||
cu.getPackage().getName().matches("com.sun%") or
|
||||
cu.getPackage().getName().matches("sun%") or
|
||||
cu.getPackage().getName().matches("jdk%") or
|
||||
cu.getPackage().getName().matches("java2d%") or
|
||||
cu.getPackage().getName().matches("build.tools%") or
|
||||
cu.getPackage().getName().matches("propertiesparser%") or
|
||||
cu.getPackage().getName().matches("org.jcp%") or
|
||||
cu.getPackage().getName().matches("org.w3c%") or
|
||||
cu.getPackage().getName().matches("org.ietf.jgss%") or
|
||||
cu.getPackage().getName().matches("org.xml.sax%") or
|
||||
cu.getPackage().getName().matches("com.oracle%") or
|
||||
cu.getPackage().getName().matches("org.omg%") or
|
||||
cu.getPackage().getName().matches("org.relaxng%") or
|
||||
cu.getPackage().getName() = "compileproperties" or
|
||||
cu.getPackage().getName() = "transparentruler" or
|
||||
cu.getPackage().getName() = "genstubs" or
|
||||
cu.getPackage().getName() = "netscape.javascript" or
|
||||
cu.getPackage().getName() = ""
|
||||
}
|
||||
|
||||
/** Holds if the given callable is not worth modeling. */
|
||||
predicate isUninterestingForModels(Callable c) {
|
||||
isInTestFile(c.getCompilationUnit().getFile()) or
|
||||
isJdkInternal(c.getCompilationUnit()) or
|
||||
c instanceof MainMethod or
|
||||
c instanceof StaticInitializer or
|
||||
exists(FunctionalExpr funcExpr | c = funcExpr.asMethod()) or
|
||||
c.getDeclaringType() instanceof TestLibrary or
|
||||
c.(Constructor).isParameterless()
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that represents all callables for which we might be
|
||||
* interested in having a MaD model.
|
||||
*/
|
||||
class ModelApi extends SrcCallable {
|
||||
ModelApi() {
|
||||
this.fromSource() and
|
||||
this.isEffectivelyPublic() and
|
||||
not isUninterestingForModels(this)
|
||||
}
|
||||
not file.getAbsolutePath().matches(["%/ql/test/%", "%/ql/automodel/test/%"]) // allows our test cases to work
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { Call, CallClassification } from "../method";
|
||||
import { ModeledMethodType } from "../modeled-method";
|
||||
|
||||
export type Query = {
|
||||
/**
|
||||
* The application query.
|
||||
*
|
||||
* It should select all usages of external APIs, and return the following result pattern:
|
||||
* - usage: the usage of the external API. This is an entity.
|
||||
* - apiName: the name of the external API. This is a string.
|
||||
* - supported: whether the external API is modeled. This should be a string representation of a boolean to satify the result pattern for a problem query.
|
||||
* - "supported": a string literal. This is required to make the query a valid problem query.
|
||||
* - packageName: the package name of the external API. This is a string.
|
||||
* - typeName: the type name of the external API. This is a string.
|
||||
* - methodName: the method name of the external API. This is a string.
|
||||
* - methodParameters: the parameters of the external API. This is a string.
|
||||
* - supported: whether the external API is modeled. This is a boolean.
|
||||
* - libraryName: the name of the library that contains the external API. This is a string and usually the basename of a file.
|
||||
* - libraryVersion: the version of the library that contains the external API. This is a string and can be empty if the version cannot be determined.
|
||||
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
||||
* - "type": a string literal. This is required to make the query a valid problem query.
|
||||
* - classification: the classification of the use of the method, either "source", "test", "generated", or "unknown"
|
||||
* - "classification: a string literal. This is required to make the query a valid problem query.
|
||||
*/
|
||||
applicationModeQuery: string;
|
||||
/**
|
||||
@@ -21,18 +24,40 @@ export type Query = {
|
||||
* It should select all methods that are callable by applications, which is usually all public methods (and constructors).
|
||||
* The result pattern should be as follows:
|
||||
* - method: the method that is callable by applications. This is an entity.
|
||||
* - apiName: the name of the external API. This is a string.
|
||||
* - packageName: the package name of the method. This is a string.
|
||||
* - typeName: the type name of the method. This is a string.
|
||||
* - methodName: the method name of the method. This is a string.
|
||||
* - methodParameters: the parameters of the method. This is a string.
|
||||
* - supported: whether this method is modeled. This should be a string representation of a boolean to satify the result pattern for a problem query.
|
||||
* - "supported": a string literal. This is required to make the query a valid problem query.
|
||||
* - libraryName: an arbitrary string. This is required to make it match the structure of the application query.
|
||||
* - libraryVersion: an arbitrary string. This is required to make it match the structure of the application query.
|
||||
* - libraryName: the name of the file or library that contains the method. This is a string and usually the basename of a file.
|
||||
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
||||
* - "type": a string literal. This is required to make the query a valid problem query.
|
||||
* - "unknown": a string literal. This is required to make it match the structure of the application query.
|
||||
* - "classification: a string literal. This is required to make the query a valid problem query.
|
||||
*/
|
||||
frameworkModeQuery: string;
|
||||
dependencies?: {
|
||||
[filename: string]: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type ApplicationModeTuple = [
|
||||
Call,
|
||||
string,
|
||||
string,
|
||||
string,
|
||||
string,
|
||||
boolean,
|
||||
string,
|
||||
string,
|
||||
ModeledMethodType,
|
||||
CallClassification,
|
||||
];
|
||||
|
||||
export type FrameworkModeTuple = [
|
||||
Call,
|
||||
string,
|
||||
string,
|
||||
string,
|
||||
string,
|
||||
boolean,
|
||||
string,
|
||||
ModeledMethodType,
|
||||
];
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import { decodeBqrsToMethods } from "../../../src/model-editor/bqrs";
|
||||
import { DecodedBqrsChunk } from "../../../src/common/bqrs-cli-types";
|
||||
import { CallClassification } from "../../../src/model-editor/method";
|
||||
import { Mode } from "../../../src/model-editor/shared/mode";
|
||||
|
||||
describe("decodeBqrsToMethods", () => {
|
||||
describe("Java queries", () => {
|
||||
describe("application mode query", () => {
|
||||
const chunk: DecodedBqrsChunk = {
|
||||
columns: [
|
||||
{ name: "usage", kind: "Entity" },
|
||||
{ name: "apiName", kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ name: "supported", kind: "Boolean" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ name: "type", kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ name: "classification", kind: "String" },
|
||||
{ kind: "String" },
|
||||
],
|
||||
tuples: [
|
||||
[
|
||||
@@ -28,15 +31,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 49,
|
||||
},
|
||||
},
|
||||
"java.io.PrintStream#println(String)",
|
||||
"true",
|
||||
"supported",
|
||||
"java.io",
|
||||
"PrintStream",
|
||||
"println",
|
||||
"(String)",
|
||||
true,
|
||||
"rt.jar",
|
||||
"",
|
||||
"sink",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -49,15 +52,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 66,
|
||||
},
|
||||
},
|
||||
"org.springframework.boot.SpringApplication#run(Class,String[])",
|
||||
"false",
|
||||
"supported",
|
||||
"org.springframework.boot",
|
||||
"SpringApplication",
|
||||
"run",
|
||||
"(Class,String[])",
|
||||
false,
|
||||
"spring-boot-3.0.2.jar",
|
||||
"",
|
||||
"none",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -70,15 +73,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 56,
|
||||
},
|
||||
},
|
||||
"org.sql2o.Connection#createQuery(String)",
|
||||
"true",
|
||||
"supported",
|
||||
"org.sql2o",
|
||||
"Connection",
|
||||
"createQuery",
|
||||
"(String)",
|
||||
true,
|
||||
"sql2o-1.6.0.jar",
|
||||
"",
|
||||
"sink",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -91,15 +94,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 39,
|
||||
},
|
||||
},
|
||||
"org.sql2o.Connection#createQuery(String)",
|
||||
"true",
|
||||
"supported",
|
||||
"org.sql2o",
|
||||
"Connection",
|
||||
"createQuery",
|
||||
"(String)",
|
||||
true,
|
||||
"sql2o-1.6.0.jar",
|
||||
"",
|
||||
"sink",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -112,15 +115,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 85,
|
||||
},
|
||||
},
|
||||
"org.sql2o.Query#executeScalar(Class)",
|
||||
"true",
|
||||
"supported",
|
||||
"org.sql2o",
|
||||
"Query",
|
||||
"executeScalar",
|
||||
"(Class)",
|
||||
true,
|
||||
"sql2o-1.6.0.jar",
|
||||
"",
|
||||
"sink",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -133,15 +136,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 68,
|
||||
},
|
||||
},
|
||||
"org.sql2o.Query#executeScalar(Class)",
|
||||
"true",
|
||||
"supported",
|
||||
"org.sql2o",
|
||||
"Query",
|
||||
"executeScalar",
|
||||
"(Class)",
|
||||
true,
|
||||
"sql2o-1.6.0.jar",
|
||||
"",
|
||||
"sink",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -154,15 +157,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 35,
|
||||
},
|
||||
},
|
||||
"org.sql2o.Sql2o#open()",
|
||||
"true",
|
||||
"supported",
|
||||
"org.sql2o",
|
||||
"Sql2o",
|
||||
"open",
|
||||
"()",
|
||||
true,
|
||||
"sql2o-1.6.0.jar",
|
||||
"",
|
||||
"sink",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -175,15 +178,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 35,
|
||||
},
|
||||
},
|
||||
"org.sql2o.Sql2o#open()",
|
||||
"true",
|
||||
"supported",
|
||||
"org.sql2o",
|
||||
"Sql2o",
|
||||
"open",
|
||||
"()",
|
||||
true,
|
||||
"sql2o-1.6.0.jar",
|
||||
"",
|
||||
"sink",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -196,15 +199,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 88,
|
||||
},
|
||||
},
|
||||
"org.sql2o.Sql2o#Sql2o(String,String,String)",
|
||||
"true",
|
||||
"supported",
|
||||
"org.sql2o",
|
||||
"Sql2o",
|
||||
"Sql2o",
|
||||
"(String,String,String)",
|
||||
true,
|
||||
"sql2o-1.6.0.jar",
|
||||
"",
|
||||
"sink",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -217,15 +220,15 @@ describe("decodeBqrsToMethods", () => {
|
||||
endColumn: 36,
|
||||
},
|
||||
},
|
||||
"org.sql2o.Sql2o#Sql2o(String)",
|
||||
"true",
|
||||
"supported",
|
||||
"org.sql2o",
|
||||
"Sql2o",
|
||||
"Sql2o",
|
||||
"(String)",
|
||||
true,
|
||||
"sql2o-1.6.0.jar",
|
||||
"",
|
||||
"sink",
|
||||
"type",
|
||||
"source",
|
||||
"classification",
|
||||
],
|
||||
],
|
||||
};
|
||||
@@ -234,7 +237,7 @@ describe("decodeBqrsToMethods", () => {
|
||||
// Even though there are a number of methods with the same number of usages, the order returned should be stable:
|
||||
// - Iterating over a map (as done by .values()) is guaranteed to be in insertion order
|
||||
// - Sorting the array of methods is guaranteed to be a stable sort
|
||||
expect(decodeBqrsToMethods(chunk)).toEqual([
|
||||
expect(decodeBqrsToMethods(chunk, Mode.Application)).toEqual([
|
||||
{
|
||||
library: "rt",
|
||||
libraryVersion: undefined,
|
||||
@@ -439,4 +442,251 @@ describe("decodeBqrsToMethods", () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("framework mode query", () => {
|
||||
const chunk: DecodedBqrsChunk = {
|
||||
columns: [
|
||||
{ name: "endpoint", kind: "Entity" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ name: "supported", kind: "Boolean" },
|
||||
{ kind: "String" },
|
||||
{ name: "type", kind: "String" },
|
||||
],
|
||||
tuples: [
|
||||
[
|
||||
{
|
||||
label: "connect",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 22,
|
||||
startColumn: 19,
|
||||
endLine: 22,
|
||||
endColumn: 25,
|
||||
},
|
||||
},
|
||||
"org.example",
|
||||
"HelloController",
|
||||
"connect",
|
||||
"(String)",
|
||||
false,
|
||||
"example",
|
||||
"",
|
||||
],
|
||||
[
|
||||
{
|
||||
label: "index",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 13,
|
||||
startColumn: 19,
|
||||
endLine: 13,
|
||||
endColumn: 23,
|
||||
},
|
||||
},
|
||||
"org.example",
|
||||
"HelloController",
|
||||
"index",
|
||||
"(String)",
|
||||
true,
|
||||
"example",
|
||||
"summary",
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
it("extracts methods", () => {
|
||||
expect(decodeBqrsToMethods(chunk, Mode.Framework)).toEqual([
|
||||
{
|
||||
library: "",
|
||||
libraryVersion: undefined,
|
||||
signature: "org.example.HelloController#connect(String)",
|
||||
packageName: "org.example",
|
||||
typeName: "HelloController",
|
||||
methodName: "connect",
|
||||
methodParameters: "(String)",
|
||||
supported: false,
|
||||
supportedType: "",
|
||||
usages: [
|
||||
{
|
||||
label: "connect",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 22,
|
||||
startColumn: 19,
|
||||
endLine: 22,
|
||||
endColumn: 25,
|
||||
},
|
||||
classification: CallClassification.Unknown,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
library: "",
|
||||
libraryVersion: undefined,
|
||||
signature: "org.example.HelloController#index(String)",
|
||||
packageName: "org.example",
|
||||
typeName: "HelloController",
|
||||
methodName: "index",
|
||||
methodParameters: "(String)",
|
||||
supported: true,
|
||||
supportedType: "summary",
|
||||
usages: [
|
||||
{
|
||||
label: "index",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 13,
|
||||
startColumn: 19,
|
||||
endLine: 13,
|
||||
endColumn: 23,
|
||||
},
|
||||
classification: CallClassification.Unknown,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("C# queries", () => {
|
||||
describe("application mode query", () => {
|
||||
const chunk: DecodedBqrsChunk = {
|
||||
columns: [
|
||||
{ name: "usage", kind: "Entity" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ name: "supported", kind: "Boolean" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ name: "type", kind: "String" },
|
||||
{ name: "classification", kind: "String" },
|
||||
],
|
||||
tuples: [
|
||||
[
|
||||
{
|
||||
label: "call to method GetMethodInfo",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/bulk-builder/bulk-builder/src/Moq/ActionObserver.cs",
|
||||
startLine: 74,
|
||||
startColumn: 40,
|
||||
endLine: 74,
|
||||
endColumn: 61,
|
||||
},
|
||||
},
|
||||
"System.Reflection",
|
||||
"RuntimeReflectionExtensions",
|
||||
"GetMethodInfo",
|
||||
"System.Delegate",
|
||||
true,
|
||||
"mscorlib",
|
||||
"4.0.0.0",
|
||||
"summary",
|
||||
"source",
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
it("extracts methods", () => {
|
||||
expect(decodeBqrsToMethods(chunk, Mode.Application)).toEqual([
|
||||
{
|
||||
library: "mscorlib",
|
||||
libraryVersion: "4.0.0.0",
|
||||
signature:
|
||||
"System.Reflection.RuntimeReflectionExtensions#GetMethodInfo(System.Delegate)",
|
||||
packageName: "System.Reflection",
|
||||
typeName: "RuntimeReflectionExtensions",
|
||||
methodName: "GetMethodInfo",
|
||||
methodParameters: "(System.Delegate)",
|
||||
supported: true,
|
||||
supportedType: "summary",
|
||||
usages: [
|
||||
{
|
||||
label: "call to method GetMethodInfo",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/bulk-builder/bulk-builder/src/Moq/ActionObserver.cs",
|
||||
startLine: 74,
|
||||
startColumn: 40,
|
||||
endLine: 74,
|
||||
endColumn: 61,
|
||||
},
|
||||
classification: CallClassification.Source,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("framework mode query", () => {
|
||||
const chunk: DecodedBqrsChunk = {
|
||||
columns: [
|
||||
{ name: "endpoint", kind: "Entity" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ name: "supported", kind: "Boolean" },
|
||||
{ kind: "String" },
|
||||
{ name: "type", kind: "String" },
|
||||
],
|
||||
tuples: [
|
||||
[
|
||||
{
|
||||
label: "Validate",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/bulk-builder/bulk-builder/src/Moq/Times.cs",
|
||||
startLine: 369,
|
||||
startColumn: 21,
|
||||
endLine: 369,
|
||||
endColumn: 28,
|
||||
},
|
||||
},
|
||||
"Moq",
|
||||
"Times",
|
||||
"Validate",
|
||||
"System.Int32",
|
||||
false,
|
||||
"Times.cs",
|
||||
"",
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
it("extracts methods", () => {
|
||||
expect(decodeBqrsToMethods(chunk, Mode.Framework)).toEqual([
|
||||
{
|
||||
library: "Times",
|
||||
libraryVersion: undefined,
|
||||
signature: "Moq.Times#Validate(System.Int32)",
|
||||
packageName: "Moq",
|
||||
typeName: "Times",
|
||||
methodName: "Validate",
|
||||
methodParameters: "(System.Int32)",
|
||||
supported: false,
|
||||
supportedType: "",
|
||||
usages: [
|
||||
{
|
||||
label: "Validate",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/bulk-builder/bulk-builder/src/Moq/Times.cs",
|
||||
startLine: 369,
|
||||
startColumn: 21,
|
||||
endLine: 369,
|
||||
endColumn: 28,
|
||||
},
|
||||
classification: CallClassification.Unknown,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -152,7 +152,7 @@ describe("external api usage query", () => {
|
||||
expect(options.queryRunner.createQueryRun).toHaveBeenCalledWith(
|
||||
"/a/b/c/src.zip",
|
||||
{
|
||||
queryPath: expect.stringMatching(/FetchExternalApis\S*\.ql/),
|
||||
queryPath: expect.stringMatching(/\S*ModeEndpoints\.ql/),
|
||||
quickEvalPosition: undefined,
|
||||
quickEvalCountOnly: false,
|
||||
},
|
||||
|
||||
@@ -34,9 +34,11 @@ describe("setUpPack", () => {
|
||||
expect(queryFiles.sort()).toEqual(
|
||||
[
|
||||
"codeql-pack.yml",
|
||||
"FetchExternalApisApplicationMode.ql",
|
||||
"FetchExternalApisFrameworkMode.ql",
|
||||
"AutomodelVsCode.qll",
|
||||
"ApplicationModeEndpoints.ql",
|
||||
"ApplicationModeEndpointsQuery.qll",
|
||||
"FrameworkModeEndpoints.ql",
|
||||
"FrameworkModeEndpointsQuery.qll",
|
||||
"ModelEditor.qll",
|
||||
].sort(),
|
||||
);
|
||||
|
||||
@@ -58,9 +60,7 @@ describe("setUpPack", () => {
|
||||
readFileSync(
|
||||
join(
|
||||
queryDir,
|
||||
`FetchExternalApis${
|
||||
mode.charAt(0).toUpperCase() + mode.slice(1)
|
||||
}Mode.ql`,
|
||||
`${mode.charAt(0).toUpperCase() + mode.slice(1)}ModeEndpoints.ql`,
|
||||
),
|
||||
"utf8",
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user