mirror of
https://github.com/github/codeql.git
synced 2026-05-05 13:45:19 +02:00
Merge pull request #8348 from michaelnebel/csharp/externalapi-telemetry
C#: ExternalAPI implementation for Telemetry.
This commit is contained in:
@@ -964,6 +964,9 @@ class ArgumentNode extends Node instanceof ArgumentNodeImpl {
|
||||
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
super.argumentOf(call, pos)
|
||||
}
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
abstract private class ArgumentNodeImpl extends Node {
|
||||
@@ -2035,19 +2038,20 @@ abstract class SyntheticField extends string {
|
||||
*/
|
||||
predicate containerContent(DataFlow::Content c) { c instanceof DataFlow::ElementContent }
|
||||
|
||||
/** Gets the string representation of the parameters of `c`. */
|
||||
string parameterQualifiedTypeNamesToString(DataFlowCallable c) {
|
||||
result =
|
||||
concat(Parameter p, int i |
|
||||
p = c.getParameter(i)
|
||||
|
|
||||
p.getType().getQualifiedName(), "," order by i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A module containing predicates related to generating models as data.
|
||||
*/
|
||||
module Csv {
|
||||
private string parameterQualifiedTypeNamesToString(DataFlowCallable c) {
|
||||
result =
|
||||
concat(Parameter p, int i |
|
||||
p = c.getParameter(i)
|
||||
|
|
||||
p.getType().getQualifiedName(), "," order by i
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the summary should apply for all overrides of `c`. */
|
||||
predicate isBaseCallableOrPrototype(DataFlowCallable c) {
|
||||
c.getDeclaringType() instanceof Interface
|
||||
|
||||
109
csharp/ql/src/Telemetry/ExternalApi.qll
Normal file
109
csharp/ql/src/Telemetry/ExternalApi.qll
Normal file
@@ -0,0 +1,109 @@
|
||||
/** 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.DataFlow
|
||||
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.TaintTracking
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
|
||||
/**
|
||||
* A test library.
|
||||
*/
|
||||
class TestLibrary extends RefType {
|
||||
TestLibrary() {
|
||||
this.getNamespace()
|
||||
.getName()
|
||||
.matches(["NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An external API from either the C# Standard Library or a 3rd party library.
|
||||
*/
|
||||
class ExternalApi extends DataFlowDispatch::DataFlowCallable {
|
||||
ExternalApi() { this.fromLibrary() }
|
||||
|
||||
/**
|
||||
* Gets the unbound type, name and parameter types of this API.
|
||||
*/
|
||||
private string getSignature() {
|
||||
result =
|
||||
this.getDeclaringType().getUnboundDeclaration() + "." + this.getName() + "(" +
|
||||
parameterQualifiedTypeNamesToString(this) + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace of this API.
|
||||
*/
|
||||
private string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) }
|
||||
|
||||
/**
|
||||
* Gets the assembly file name containing this API.
|
||||
*/
|
||||
private string getAssembly() { result = this.getFile().getBaseName() }
|
||||
|
||||
/**
|
||||
* Gets the assembly file name and namespace of this API.
|
||||
*/
|
||||
string getInfoPrefix() { result = this.getAssembly() + "#" + this.getNamespace() }
|
||||
|
||||
/**
|
||||
* Gets the assembly file name, namespace and signature of this API.
|
||||
*/
|
||||
string getInfo() { result = this.getInfoPrefix() + "#" + this.getSignature() }
|
||||
|
||||
/** Gets a call to this API callable. */
|
||||
DispatchCall getACall() {
|
||||
this = result.getADynamicTarget().getUnboundDeclaration()
|
||||
or
|
||||
this = result.getAStaticTarget().getUnboundDeclaration()
|
||||
}
|
||||
|
||||
/** Gets a node that is an input to a call to this API. */
|
||||
private ArgumentNode getAnInput() {
|
||||
result.getCall().(DataFlowDispatch::NonDelegateDataFlowCall).getDispatchCall() = this.getACall()
|
||||
}
|
||||
|
||||
/** Gets a node that is an output from a call to this API. */
|
||||
private DataFlow::Node getAnOutput() {
|
||||
exists(DataFlowDispatch::NonDelegateDataFlowCall call, DataFlowImplCommon::ReturnKindExt ret |
|
||||
result = ret.getAnOutNode(call)
|
||||
|
|
||||
this.getACall() = call.getDispatchCall()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this API has a supported summary. */
|
||||
predicate hasSummary() {
|
||||
this instanceof SummarizedCallable or
|
||||
defaultAdditionalTaintStep(this.getAnInput(), _)
|
||||
}
|
||||
|
||||
/** Holds if this API is is a constructor without parameters. */
|
||||
private predicate isParameterlessConstructor() {
|
||||
this instanceof Constructor and this.getNumberOfParameters() = 0
|
||||
}
|
||||
|
||||
/** Holds if this API is part of a common testing library or framework. */
|
||||
private predicate isTestLibrary() { this.getDeclaringType() instanceof TestLibrary }
|
||||
|
||||
/** Holds if this API is not worth supporting. */
|
||||
predicate isUninteresting() { this.isTestLibrary() or this.isParameterlessConstructor() }
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
predicate isSource() {
|
||||
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
|
||||
}
|
||||
|
||||
/** Holds if this API is a known sink. */
|
||||
predicate isSink() { sinkNode(this.getAnInput(), _) }
|
||||
|
||||
/** Holds if this API is supported by existing CodeQL libraries, that is, it is either a recognized source or sink or has a flow summary. */
|
||||
predicate isSupported() { this.hasSummary() or this.isSource() or this.isSink() }
|
||||
}
|
||||
21
csharp/ql/src/Telemetry/ExternalLibraryUsage.ql
Normal file
21
csharp/ql/src/Telemetry/ExternalLibraryUsage.ql
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name External libraries
|
||||
* @description A list of external libraries used in the code
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* @id csharp/telemetry/external-libs
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
|
||||
from int usages, string info
|
||||
where
|
||||
usages =
|
||||
strictcount(DispatchCall c, ExternalApi api |
|
||||
c = api.getACall() and
|
||||
api.getInfoPrefix() = info and
|
||||
not api.isUninteresting()
|
||||
)
|
||||
select info, usages order by usages desc
|
||||
18
csharp/ql/src/Telemetry/SupportedExternalSinks.ql
Normal file
18
csharp/ql/src/Telemetry/SupportedExternalSinks.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Supported sinks in external libraries
|
||||
* @description A list of 3rd party APIs detected as sinks. Excludes APIs exposed by test libraries.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* @id csharp/telemetry/supported-external-api-sinks
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
|
||||
from ExternalApi api, int usages
|
||||
where
|
||||
not api.isUninteresting() and
|
||||
api.isSink() and
|
||||
usages = strictcount(DispatchCall c | c = api.getACall())
|
||||
select api.getInfo() as info, usages order by usages desc
|
||||
18
csharp/ql/src/Telemetry/SupportedExternalSources.ql
Normal file
18
csharp/ql/src/Telemetry/SupportedExternalSources.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Supported sources in external libraries
|
||||
* @description A list of 3rd party APIs detected as sources. Excludes APIs exposed by test libraries.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* @id csharp/telemetry/supported-external-api-sources
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
|
||||
from ExternalApi api, int usages
|
||||
where
|
||||
not api.isUninteresting() and
|
||||
api.isSource() and
|
||||
usages = strictcount(DispatchCall c | c = api.getACall())
|
||||
select api.getInfo() as info, usages order by usages desc
|
||||
18
csharp/ql/src/Telemetry/SupportedExternalTaint.ql
Normal file
18
csharp/ql/src/Telemetry/SupportedExternalTaint.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Supported flow steps in external libraries
|
||||
* @description A list of 3rd party APIs detected as flow steps. Excludes APIs exposed by test libraries.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* @id csharp/telemetry/supported-external-api-taint
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
|
||||
from ExternalApi api, int usages
|
||||
where
|
||||
not api.isUninteresting() and
|
||||
api.hasSummary() and
|
||||
usages = strictcount(DispatchCall c | c = api.getACall())
|
||||
select api.getInfo() as info, usages order by usages desc
|
||||
18
csharp/ql/src/Telemetry/UnsupportedExternalAPIs.ql
Normal file
18
csharp/ql/src/Telemetry/UnsupportedExternalAPIs.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Usage of unsupported APIs coming from external libraries
|
||||
* @description A list of 3rd party APIs used in the codebase. Excludes APIs exposed by test libraries.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* @id csharp/telemetry/unsupported-external-api
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
|
||||
from ExternalApi api, int usages
|
||||
where
|
||||
not api.isUninteresting() and
|
||||
not api.isSupported() and
|
||||
usages = strictcount(DispatchCall c | c = api.getACall())
|
||||
select api.getInfo() as info, usages order by usages desc
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class LibraryUsage
|
||||
{
|
||||
public void M1()
|
||||
{
|
||||
var l = new List<object>(); // Uninteresting parameterless constructor
|
||||
var o = new object(); // Uninteresting parameterless constructor
|
||||
l.Add(o); // Has flow summary
|
||||
l.Add(o); // Has flow summary
|
||||
}
|
||||
|
||||
public void M2()
|
||||
{
|
||||
var d0 = new DateTime(); // Uninteresting parameterless constructor
|
||||
var next0 = d0.AddYears(30); // Has no flow summary
|
||||
|
||||
var d1 = new DateTime(2000, 1, 1); // Interesting constructor
|
||||
var next1 = next0.AddDays(3); // Has no flow summary
|
||||
var next2 = next1.AddYears(5); // Has no flow summary
|
||||
}
|
||||
|
||||
public void M3()
|
||||
{
|
||||
var guid1 = Guid.Parse("{12345678-1234-1234-1234-123456789012}"); // Has no flow summary
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
| System.Private.CoreLib.dll#System | 5 |
|
||||
| System.Private.CoreLib.dll#System.Collections.Generic | 2 |
|
||||
@@ -0,0 +1 @@
|
||||
Telemetry/ExternalLibraryUsage.ql
|
||||
@@ -0,0 +1 @@
|
||||
| System.Private.CoreLib.dll#System.Collections.Generic#List<>.Add(T) | 2 |
|
||||
@@ -0,0 +1 @@
|
||||
Telemetry/SupportedExternalTaint.ql
|
||||
@@ -0,0 +1,4 @@
|
||||
| System.Private.CoreLib.dll#System#DateTime.AddYears(System.Int32) | 2 |
|
||||
| System.Private.CoreLib.dll#System#DateTime.AddDays(System.Double) | 1 |
|
||||
| System.Private.CoreLib.dll#System#DateTime.DateTime(System.Int32,System.Int32,System.Int32) | 1 |
|
||||
| System.Private.CoreLib.dll#System#Guid.Parse(System.String) | 1 |
|
||||
@@ -0,0 +1 @@
|
||||
Telemetry/UnsupportedExternalAPIs.ql
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
public class SupportedExternalSinks
|
||||
{
|
||||
public void M1()
|
||||
{
|
||||
var o = new object();
|
||||
var response = new HttpResponse();
|
||||
response.AddHeader("header", "value");
|
||||
response.AppendHeader("header", "value");
|
||||
response.Write(o); // Known sink.
|
||||
response.WriteFile("filename"); // Known sink.
|
||||
response.Write(o); // Known sink.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
| System.Web.cs#System.Web#HttpResponse.Write(System.Object) | 2 |
|
||||
| System.Web.cs#System.Web#HttpResponse.WriteFile(System.String) | 1 |
|
||||
@@ -0,0 +1 @@
|
||||
Telemetry/SupportedExternalSinks.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /r:System.Collections.Specialized.dll
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/System.Web.cs
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
public class SupportExternalSources
|
||||
{
|
||||
public void M1()
|
||||
{
|
||||
var l1 = Console.ReadLine(); // Known source.
|
||||
var l2 = Console.ReadLine(); // Known source.
|
||||
Console.SetError(Console.Out);
|
||||
var x = Console.Read(); // Know source.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
| System.Console.dll#System#Console.ReadLine() | 2 |
|
||||
| System.Console.dll#System#Console.Read() | 1 |
|
||||
@@ -0,0 +1 @@
|
||||
Telemetry/SupportedExternalSources.ql
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
private import java
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.FlowSummary
|
||||
@@ -10,22 +9,34 @@ private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* An external API from either the Java Standard Library or a 3rd party library.
|
||||
* A test library.
|
||||
*/
|
||||
private class TestLibrary extends RefType {
|
||||
TestLibrary() {
|
||||
this.getPackage()
|
||||
.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%"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
private string containerAsJar(Container container) {
|
||||
if container instanceof JarFile then result = container.getBaseName() else result = "rt.jar"
|
||||
}
|
||||
|
||||
/**
|
||||
* An external API from either the Standard Library or a 3rd party library.
|
||||
*/
|
||||
class ExternalApi extends Callable {
|
||||
ExternalApi() { not this.fromSource() }
|
||||
|
||||
/** Holds if this API is not worth supporting */
|
||||
predicate isUninteresting() { this.isTestLibrary() or this.isParameterlessConstructor() }
|
||||
|
||||
/** Holds if this API is is a constructor without parameters */
|
||||
predicate isParameterlessConstructor() {
|
||||
this instanceof Constructor and this.getNumberOfParameters() = 0
|
||||
}
|
||||
|
||||
/** Holds if this API is part of a common testing library or framework */
|
||||
private predicate isTestLibrary() { this.getDeclaringType() instanceof TestLibrary }
|
||||
|
||||
/**
|
||||
* Gets information about the external API in the form expected by the CSV modeling framework.
|
||||
*/
|
||||
@@ -38,13 +49,7 @@ class ExternalApi extends Callable {
|
||||
/**
|
||||
* Gets the jar file containing this API. Normalizes the Java Runtime to "rt.jar" despite the presence of modules.
|
||||
*/
|
||||
string jarContainer() {
|
||||
result = this.containerAsJar(this.getCompilationUnit().getParentContainer*())
|
||||
}
|
||||
|
||||
private string containerAsJar(Container container) {
|
||||
if container instanceof JarFile then result = container.getBaseName() else result = "rt.jar"
|
||||
}
|
||||
string jarContainer() { result = containerAsJar(this.getCompilationUnit().getParentContainer*()) }
|
||||
|
||||
/** Gets a node that is an input to a call to this API. */
|
||||
private DataFlow::Node getAnInput() {
|
||||
@@ -68,6 +73,17 @@ class ExternalApi extends Callable {
|
||||
TaintTracking::localAdditionalTaintStep(this.getAnInput(), _)
|
||||
}
|
||||
|
||||
/** Holds if this API is is a constructor without parameters. */
|
||||
private predicate isParameterlessConstructor() {
|
||||
this instanceof Constructor and this.getNumberOfParameters() = 0
|
||||
}
|
||||
|
||||
/** Holds if this API is part of a common testing library or framework. */
|
||||
private predicate isTestLibrary() { this.getDeclaringType() instanceof TestLibrary }
|
||||
|
||||
/** Holds if this API is not worth supporting. */
|
||||
predicate isUninteresting() { this.isTestLibrary() or this.isParameterlessConstructor() }
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
predicate isSource() {
|
||||
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
|
||||
@@ -82,19 +98,3 @@ class ExternalApi extends Callable {
|
||||
|
||||
/** DEPRECATED: Alias for ExternalApi */
|
||||
deprecated class ExternalAPI = ExternalApi;
|
||||
|
||||
private class TestLibrary extends RefType {
|
||||
TestLibrary() {
|
||||
this.getPackage()
|
||||
.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%"
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ExternalAPI
|
||||
import ExternalApi
|
||||
|
||||
from int usages, string jarname
|
||||
where
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ExternalAPI
|
||||
import ExternalApi
|
||||
import semmle.code.java.GeneratedFiles
|
||||
|
||||
from ExternalApi api, int usages
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ExternalAPI
|
||||
import ExternalApi
|
||||
import semmle.code.java.GeneratedFiles
|
||||
|
||||
from ExternalApi api, int usages
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ExternalAPI
|
||||
import ExternalApi
|
||||
import semmle.code.java.GeneratedFiles
|
||||
|
||||
from ExternalApi api, int usages
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import ExternalAPI
|
||||
import ExternalApi
|
||||
import semmle.code.java.GeneratedFiles
|
||||
|
||||
from ExternalApi api, int usages
|
||||
|
||||
Reference in New Issue
Block a user