mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
C#: Move external api declarations to the library pack.
This commit is contained in:
163
csharp/ql/lib/semmle/code/csharp/telemetry/ExternalApi.qll
Normal file
163
csharp/ql/lib/semmle/code/csharp/telemetry/ExternalApi.qll
Normal file
@@ -0,0 +1,163 @@
|
||||
/** Provides classes and predicates related to handling APIs from external libraries. */
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import semmle.code.csharp.dataflow.FlowSummary
|
||||
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.ExternalFlow
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.ApiSources as ApiSources
|
||||
private import semmle.code.csharp.security.dataflow.flowsinks.ApiSinks as ApiSinks
|
||||
private import TestLibrary
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(Callable c) {
|
||||
c.getDeclaringType() instanceof TestLibrary
|
||||
or
|
||||
c.(Constructor).isParameterless()
|
||||
or
|
||||
// The data flow library uses read/store steps for properties, so we don't need to model them,
|
||||
// if both a getter and a setter exist.
|
||||
c.(Accessor).getDeclaration().(Property).isReadWrite()
|
||||
}
|
||||
|
||||
/**
|
||||
* An external API from either the C# Standard Library or a 3rd party library.
|
||||
*/
|
||||
class ExternalApi extends Callable {
|
||||
ExternalApi() {
|
||||
this.isUnboundDeclaration() and
|
||||
this.fromLibrary() and
|
||||
this.(Modifiable).isEffectivelyPublic() and
|
||||
not isUninteresting(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getNamespace() { this.getDeclaringType().hasFullyQualifiedName(result, _) }
|
||||
|
||||
/**
|
||||
* Gets the namespace and signature of this API.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getApiName() { result = this.getNamespace() + "#" + this.getSignature() }
|
||||
|
||||
/** 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, _)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this API has a supported summary. */
|
||||
pragma[nomagic]
|
||||
predicate hasSummary() {
|
||||
this instanceof SummarizedCallable
|
||||
or
|
||||
defaultAdditionalTaintStep(this.getAnInput(), _, _)
|
||||
}
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
pragma[nomagic]
|
||||
predicate isSource() { this.getAnOutput() instanceof ApiSources::SourceNode }
|
||||
|
||||
/** Holds if this API is a known sink. */
|
||||
pragma[nomagic]
|
||||
predicate isSink() { this.getAnInput() instanceof ApiSinks::SinkNode }
|
||||
|
||||
/** Holds if this API is a known neutral. */
|
||||
pragma[nomagic]
|
||||
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
|
||||
|
||||
/**
|
||||
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
|
||||
* recognized source, sink or neutral or it has a flow summary.
|
||||
*/
|
||||
predicate isSupported() {
|
||||
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nested name of the type `t`.
|
||||
*
|
||||
* 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(Type t) {
|
||||
not exists(t.getDeclaringType().getUnboundDeclaration()) and
|
||||
result = t.getName()
|
||||
or
|
||||
nestedName(t.getDeclaringType().getUnboundDeclaration()) + "+" + t.getName() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the limit for the number of results produced by a telemetry query.
|
||||
*/
|
||||
int resultLimit() { result = 100 }
|
||||
|
||||
/**
|
||||
* Holds if it is relevant to count usages of `api`.
|
||||
*/
|
||||
signature predicate relevantApi(ExternalApi api);
|
||||
|
||||
/**
|
||||
* Given a predicate to count relevant API usages, this module provides a predicate
|
||||
* for restricting the number or returned results based on a certain limit.
|
||||
*/
|
||||
module Results<relevantApi/1 getRelevantUsages> {
|
||||
private int getUsages(string apiName) {
|
||||
result =
|
||||
strictcount(Call c, ExternalApi api |
|
||||
c.getTarget().getUnboundDeclaration() = api and
|
||||
apiName = api.getApiName() and
|
||||
getRelevantUsages(api) and
|
||||
c.fromSource()
|
||||
)
|
||||
}
|
||||
|
||||
private int getOrder(string apiName) {
|
||||
apiName =
|
||||
rank[result](string name, int usages |
|
||||
usages = getUsages(name)
|
||||
|
|
||||
name order by usages desc, name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists an API with `apiName` that is being used `usages` times
|
||||
* and if it is in the top results (guarded by resultLimit).
|
||||
*/
|
||||
predicate restrict(string apiName, int usages) {
|
||||
usages = getUsages(apiName) and
|
||||
getOrder(apiName) <= resultLimit()
|
||||
}
|
||||
}
|
||||
16
csharp/ql/lib/semmle/code/csharp/telemetry/TestLibrary.qll
Normal file
16
csharp/ql/lib/semmle/code/csharp/telemetry/TestLibrary.qll
Normal file
@@ -0,0 +1,16 @@
|
||||
private import csharp
|
||||
|
||||
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()) }
|
||||
}
|
||||
@@ -1,163 +1,4 @@
|
||||
/** Provides classes and predicates related to handling APIs from external libraries. */
|
||||
// Use `semmle.code.csharp.telemetry.ExternalApi` instead.
|
||||
deprecated module;
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import semmle.code.csharp.dataflow.FlowSummary
|
||||
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.ExternalFlow
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.ApiSources as ApiSources
|
||||
private import semmle.code.csharp.security.dataflow.flowsinks.ApiSinks as ApiSinks
|
||||
private import TestLibrary
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(Callable c) {
|
||||
c.getDeclaringType() instanceof TestLibrary
|
||||
or
|
||||
c.(Constructor).isParameterless()
|
||||
or
|
||||
// The data flow library uses read/store steps for properties, so we don't need to model them,
|
||||
// if both a getter and a setter exist.
|
||||
c.(Accessor).getDeclaration().(Property).isReadWrite()
|
||||
}
|
||||
|
||||
/**
|
||||
* An external API from either the C# Standard Library or a 3rd party library.
|
||||
*/
|
||||
class ExternalApi extends Callable {
|
||||
ExternalApi() {
|
||||
this.isUnboundDeclaration() and
|
||||
this.fromLibrary() and
|
||||
this.(Modifiable).isEffectivelyPublic() and
|
||||
not isUninteresting(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getNamespace() { this.getDeclaringType().hasFullyQualifiedName(result, _) }
|
||||
|
||||
/**
|
||||
* Gets the namespace and signature of this API.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getApiName() { result = this.getNamespace() + "#" + this.getSignature() }
|
||||
|
||||
/** 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, _)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this API has a supported summary. */
|
||||
pragma[nomagic]
|
||||
predicate hasSummary() {
|
||||
this instanceof SummarizedCallable
|
||||
or
|
||||
defaultAdditionalTaintStep(this.getAnInput(), _, _)
|
||||
}
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
pragma[nomagic]
|
||||
predicate isSource() { this.getAnOutput() instanceof ApiSources::SourceNode }
|
||||
|
||||
/** Holds if this API is a known sink. */
|
||||
pragma[nomagic]
|
||||
predicate isSink() { this.getAnInput() instanceof ApiSinks::SinkNode }
|
||||
|
||||
/** Holds if this API is a known neutral. */
|
||||
pragma[nomagic]
|
||||
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
|
||||
|
||||
/**
|
||||
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
|
||||
* recognized source, sink or neutral or it has a flow summary.
|
||||
*/
|
||||
predicate isSupported() {
|
||||
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nested name of the type `t`.
|
||||
*
|
||||
* 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(Type t) {
|
||||
not exists(t.getDeclaringType().getUnboundDeclaration()) and
|
||||
result = t.getName()
|
||||
or
|
||||
nestedName(t.getDeclaringType().getUnboundDeclaration()) + "+" + t.getName() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the limit for the number of results produced by a telemetry query.
|
||||
*/
|
||||
int resultLimit() { result = 100 }
|
||||
|
||||
/**
|
||||
* Holds if it is relevant to count usages of `api`.
|
||||
*/
|
||||
signature predicate relevantApi(ExternalApi api);
|
||||
|
||||
/**
|
||||
* Given a predicate to count relevant API usages, this module provides a predicate
|
||||
* for restricting the number or returned results based on a certain limit.
|
||||
*/
|
||||
module Results<relevantApi/1 getRelevantUsages> {
|
||||
private int getUsages(string apiName) {
|
||||
result =
|
||||
strictcount(Call c, ExternalApi api |
|
||||
c.getTarget().getUnboundDeclaration() = api and
|
||||
apiName = api.getApiName() and
|
||||
getRelevantUsages(api) and
|
||||
c.fromSource()
|
||||
)
|
||||
}
|
||||
|
||||
private int getOrder(string apiName) {
|
||||
apiName =
|
||||
rank[result](string name, int usages |
|
||||
usages = getUsages(name)
|
||||
|
|
||||
name order by usages desc, name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists an API with `apiName` that is being used `usages` times
|
||||
* and if it is in the top results (guarded by resultLimit).
|
||||
*/
|
||||
predicate restrict(string apiName, int usages) {
|
||||
usages = getUsages(apiName) and
|
||||
getOrder(apiName) <= resultLimit()
|
||||
}
|
||||
}
|
||||
import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate getRelevantUsages(string namespace, int usages) {
|
||||
usages =
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { api.isSupported() }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { api.isSink() }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { api.isSource() }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { api.hasSummary() }
|
||||
|
||||
|
||||
@@ -1,16 +1,4 @@
|
||||
private import csharp
|
||||
// Use `semmle.code.csharp.telemetry.TestLibrary` instead.
|
||||
deprecated module;
|
||||
|
||||
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()) }
|
||||
}
|
||||
import semmle.code.csharp.telemetry.TestLibrary
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { not api.isSupported() }
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import Telemetry.ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
from Call c, ExternalApi api
|
||||
where
|
||||
|
||||
@@ -5,7 +5,7 @@ private import semmle.code.csharp.dataflow.FlowSummary
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||
private import semmle.code.csharp.frameworks.Test
|
||||
private import Telemetry.TestLibrary
|
||||
private import semmle.code.csharp.telemetry.TestLibrary
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(Callable c) {
|
||||
|
||||
Reference in New Issue
Block a user