Skip to content

Commit 27b1d1e

Browse files
authored
Merge pull request #8348 from michaelnebel/csharp/externalapi-telemetry
C#: ExternalAPI implementation for Telemetry.
2 parents 146318d + db7abb4 commit 27b1d1e

27 files changed

+330
-50
lines changed

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,9 @@ class ArgumentNode extends Node instanceof ArgumentNodeImpl {
964964
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
965965
super.argumentOf(call, pos)
966966
}
967+
968+
/** Gets the call in which this node is an argument. */
969+
DataFlowCall getCall() { this.argumentOf(result, _) }
967970
}
968971

969972
abstract private class ArgumentNodeImpl extends Node {
@@ -2035,19 +2038,20 @@ abstract class SyntheticField extends string {
20352038
*/
20362039
predicate containerContent(DataFlow::Content c) { c instanceof DataFlow::ElementContent }
20372040

2041+
/** Gets the string representation of the parameters of `c`. */
2042+
string parameterQualifiedTypeNamesToString(DataFlowCallable c) {
2043+
result =
2044+
concat(Parameter p, int i |
2045+
p = c.getParameter(i)
2046+
|
2047+
p.getType().getQualifiedName(), "," order by i
2048+
)
2049+
}
2050+
20382051
/**
20392052
* A module containing predicates related to generating models as data.
20402053
*/
20412054
module Csv {
2042-
private string parameterQualifiedTypeNamesToString(DataFlowCallable c) {
2043-
result =
2044-
concat(Parameter p, int i |
2045-
p = c.getParameter(i)
2046-
|
2047-
p.getType().getQualifiedName(), "," order by i
2048-
)
2049-
}
2050-
20512055
/** Holds if the summary should apply for all overrides of `c`. */
20522056
predicate isBaseCallableOrPrototype(DataFlowCallable c) {
20532057
c.getDeclaringType() instanceof Interface
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/** Provides classes and predicates related to handling APIs from external libraries. */
2+
3+
private import csharp
4+
private import semmle.code.csharp.dispatch.Dispatch
5+
private import semmle.code.csharp.dataflow.DataFlow
6+
private import semmle.code.csharp.dataflow.ExternalFlow
7+
private import semmle.code.csharp.dataflow.FlowSummary
8+
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
9+
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
10+
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
11+
private import semmle.code.csharp.dataflow.TaintTracking
12+
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
13+
private import semmle.code.csharp.security.dataflow.flowsources.Remote
14+
15+
/**
16+
* A test library.
17+
*/
18+
class TestLibrary extends RefType {
19+
TestLibrary() {
20+
this.getNamespace()
21+
.getName()
22+
.matches(["NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%"])
23+
}
24+
}
25+
26+
/**
27+
* An external API from either the C# Standard Library or a 3rd party library.
28+
*/
29+
class ExternalApi extends DataFlowDispatch::DataFlowCallable {
30+
ExternalApi() { this.fromLibrary() }
31+
32+
/**
33+
* Gets the unbound type, name and parameter types of this API.
34+
*/
35+
private string getSignature() {
36+
result =
37+
this.getDeclaringType().getUnboundDeclaration() + "." + this.getName() + "(" +
38+
parameterQualifiedTypeNamesToString(this) + ")"
39+
}
40+
41+
/**
42+
* Gets the namespace of this API.
43+
*/
44+
private string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) }
45+
46+
/**
47+
* Gets the assembly file name containing this API.
48+
*/
49+
private string getAssembly() { result = this.getFile().getBaseName() }
50+
51+
/**
52+
* Gets the assembly file name and namespace of this API.
53+
*/
54+
string getInfoPrefix() { result = this.getAssembly() + "#" + this.getNamespace() }
55+
56+
/**
57+
* Gets the assembly file name, namespace and signature of this API.
58+
*/
59+
string getInfo() { result = this.getInfoPrefix() + "#" + this.getSignature() }
60+
61+
/** Gets a call to this API callable. */
62+
DispatchCall getACall() {
63+
this = result.getADynamicTarget().getUnboundDeclaration()
64+
or
65+
this = result.getAStaticTarget().getUnboundDeclaration()
66+
}
67+
68+
/** Gets a node that is an input to a call to this API. */
69+
private ArgumentNode getAnInput() {
70+
result.getCall().(DataFlowDispatch::NonDelegateDataFlowCall).getDispatchCall() = this.getACall()
71+
}
72+
73+
/** Gets a node that is an output from a call to this API. */
74+
private DataFlow::Node getAnOutput() {
75+
exists(DataFlowDispatch::NonDelegateDataFlowCall call, DataFlowImplCommon::ReturnKindExt ret |
76+
result = ret.getAnOutNode(call)
77+
|
78+
this.getACall() = call.getDispatchCall()
79+
)
80+
}
81+
82+
/** Holds if this API has a supported summary. */
83+
predicate hasSummary() {
84+
this instanceof SummarizedCallable or
85+
defaultAdditionalTaintStep(this.getAnInput(), _)
86+
}
87+
88+
/** Holds if this API is is a constructor without parameters. */
89+
private predicate isParameterlessConstructor() {
90+
this instanceof Constructor and this.getNumberOfParameters() = 0
91+
}
92+
93+
/** Holds if this API is part of a common testing library or framework. */
94+
private predicate isTestLibrary() { this.getDeclaringType() instanceof TestLibrary }
95+
96+
/** Holds if this API is not worth supporting. */
97+
predicate isUninteresting() { this.isTestLibrary() or this.isParameterlessConstructor() }
98+
99+
/** Holds if this API is a known source. */
100+
predicate isSource() {
101+
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
102+
}
103+
104+
/** Holds if this API is a known sink. */
105+
predicate isSink() { sinkNode(this.getAnInput(), _) }
106+
107+
/** 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. */
108+
predicate isSupported() { this.hasSummary() or this.isSource() or this.isSink() }
109+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name External libraries
3+
* @description A list of external libraries used in the code
4+
* @kind metric
5+
* @tags summary
6+
* @id csharp/telemetry/external-libs
7+
*/
8+
9+
private import csharp
10+
private import semmle.code.csharp.dispatch.Dispatch
11+
private import ExternalApi
12+
13+
from int usages, string info
14+
where
15+
usages =
16+
strictcount(DispatchCall c, ExternalApi api |
17+
c = api.getACall() and
18+
api.getInfoPrefix() = info and
19+
not api.isUninteresting()
20+
)
21+
select info, usages order by usages desc
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Supported sinks in external libraries
3+
* @description A list of 3rd party APIs detected as sinks. Excludes APIs exposed by test libraries.
4+
* @kind metric
5+
* @tags summary
6+
* @id csharp/telemetry/supported-external-api-sinks
7+
*/
8+
9+
private import csharp
10+
private import semmle.code.csharp.dispatch.Dispatch
11+
private import ExternalApi
12+
13+
from ExternalApi api, int usages
14+
where
15+
not api.isUninteresting() and
16+
api.isSink() and
17+
usages = strictcount(DispatchCall c | c = api.getACall())
18+
select api.getInfo() as info, usages order by usages desc
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Supported sources in external libraries
3+
* @description A list of 3rd party APIs detected as sources. Excludes APIs exposed by test libraries.
4+
* @kind metric
5+
* @tags summary
6+
* @id csharp/telemetry/supported-external-api-sources
7+
*/
8+
9+
private import csharp
10+
private import semmle.code.csharp.dispatch.Dispatch
11+
private import ExternalApi
12+
13+
from ExternalApi api, int usages
14+
where
15+
not api.isUninteresting() and
16+
api.isSource() and
17+
usages = strictcount(DispatchCall c | c = api.getACall())
18+
select api.getInfo() as info, usages order by usages desc
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Supported flow steps in external libraries
3+
* @description A list of 3rd party APIs detected as flow steps. Excludes APIs exposed by test libraries.
4+
* @kind metric
5+
* @tags summary
6+
* @id csharp/telemetry/supported-external-api-taint
7+
*/
8+
9+
private import csharp
10+
private import semmle.code.csharp.dispatch.Dispatch
11+
private import ExternalApi
12+
13+
from ExternalApi api, int usages
14+
where
15+
not api.isUninteresting() and
16+
api.hasSummary() and
17+
usages = strictcount(DispatchCall c | c = api.getACall())
18+
select api.getInfo() as info, usages order by usages desc
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Usage of unsupported APIs coming from external libraries
3+
* @description A list of 3rd party APIs used in the codebase. Excludes APIs exposed by test libraries.
4+
* @kind metric
5+
* @tags summary
6+
* @id csharp/telemetry/unsupported-external-api
7+
*/
8+
9+
private import csharp
10+
private import semmle.code.csharp.dispatch.Dispatch
11+
private import ExternalApi
12+
13+
from ExternalApi api, int usages
14+
where
15+
not api.isUninteresting() and
16+
not api.isSupported() and
17+
usages = strictcount(DispatchCall c | c = api.getACall())
18+
select api.getInfo() as info, usages order by usages desc
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
public class LibraryUsage
5+
{
6+
public void M1()
7+
{
8+
var l = new List<object>(); // Uninteresting parameterless constructor
9+
var o = new object(); // Uninteresting parameterless constructor
10+
l.Add(o); // Has flow summary
11+
l.Add(o); // Has flow summary
12+
}
13+
14+
public void M2()
15+
{
16+
var d0 = new DateTime(); // Uninteresting parameterless constructor
17+
var next0 = d0.AddYears(30); // Has no flow summary
18+
19+
var d1 = new DateTime(2000, 1, 1); // Interesting constructor
20+
var next1 = next0.AddDays(3); // Has no flow summary
21+
var next2 = next1.AddYears(5); // Has no flow summary
22+
}
23+
24+
public void M3()
25+
{
26+
var guid1 = Guid.Parse("{12345678-1234-1234-1234-123456789012}"); // Has no flow summary
27+
}
28+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| System.Private.CoreLib.dll#System | 5 |
2+
| System.Private.CoreLib.dll#System.Collections.Generic | 2 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Telemetry/ExternalLibraryUsage.ql

0 commit comments

Comments
 (0)