Skip to content

Commit 0f4f987

Browse files
authored
Merge pull request #14200 from github/koesie10/add-csharp-model-editor-queries
C#: Add VS Code model editor queries
2 parents 3949914 + 922ff7b commit 0f4f987

22 files changed

+429
-18
lines changed

csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,20 @@ Element interpretElement(
418418
)
419419
}
420420

421+
/**
422+
* A callable where there exists a MaD sink model that applies to it.
423+
*/
424+
class SinkCallable extends Callable {
425+
SinkCallable() { sinkElement(this, _, _, _) }
426+
}
427+
428+
/**
429+
* A callable where there exists a MaD source model that applies to it.
430+
*/
431+
class SourceCallable extends Callable {
432+
SourceCallable() { sourceElement(this, _, _, _) }
433+
}
434+
421435
cached
422436
private module Cached {
423437
/**

csharp/ql/src/Telemetry/ExternalApi.qll

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/** Provides classes and predicates related to handling APIs from external libraries. */
22

33
private import csharp
4-
private import dotnet
54
private import semmle.code.csharp.dispatch.Dispatch
65
private import semmle.code.csharp.dataflow.ExternalFlow
76
private import semmle.code.csharp.dataflow.FlowSummary
@@ -10,32 +9,18 @@ private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlow
109
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
1110
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
1211
private import semmle.code.csharp.security.dataflow.flowsources.Remote
13-
14-
pragma[nomagic]
15-
private predicate isTestNamespace(Namespace ns) {
16-
ns.getFullName()
17-
.matches([
18-
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
19-
])
20-
}
21-
22-
/**
23-
* A test library.
24-
*/
25-
class TestLibrary extends RefType {
26-
TestLibrary() { isTestNamespace(this.getNamespace()) }
27-
}
12+
private import TestLibrary
2813

2914
/** Holds if the given callable is not worth supporting. */
30-
private predicate isUninteresting(DotNet::Callable c) {
15+
private predicate isUninteresting(Callable c) {
3116
c.getDeclaringType() instanceof TestLibrary or
3217
c.(Constructor).isParameterless()
3318
}
3419

3520
/**
3621
* An external API from either the C# Standard Library or a 3rd party library.
3722
*/
38-
class ExternalApi extends DotNet::Callable {
23+
class ExternalApi extends Callable {
3924
ExternalApi() {
4025
this.isUnboundDeclaration() and
4126
this.fromLibrary() and
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
private import csharp
2+
private import dotnet
3+
4+
pragma[nomagic]
5+
private predicate isTestNamespace(Namespace ns) {
6+
ns.getFullName()
7+
.matches([
8+
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
9+
])
10+
}
11+
12+
/**
13+
* A test library.
14+
*/
15+
class TestLibrary extends RefType {
16+
TestLibrary() { isTestNamespace(this.getNamespace()) }
17+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @name Fetch endpoints for use in the model editor (application mode)
3+
* @description A list of 3rd party endpoints (methods and attributes) used in the codebase. Excludes test and generated code.
4+
* @kind table
5+
* @id csharp/utils/modeleditor/application-mode-endpoints
6+
* @tags modeleditor endpoints application-mode
7+
*/
8+
9+
import csharp
10+
import ApplicationModeEndpointsQuery
11+
import ModelEditor
12+
13+
private Call aUsage(ExternalEndpoint api) { result.getTarget().getUnboundDeclaration() = api }
14+
15+
from ExternalEndpoint endpoint, boolean supported, Call usage, string type, string classification
16+
where
17+
supported = isSupported(endpoint) and
18+
usage = aUsage(endpoint) and
19+
type = supportedType(endpoint) and
20+
classification = methodClassification(usage)
21+
select usage, endpoint.getNamespace(), endpoint.getTypeName(), endpoint.getName(),
22+
endpoint.getParameterTypes(), supported, endpoint.dllName(), endpoint.dllVersion(), type,
23+
classification
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
private import csharp
2+
private import semmle.code.csharp.dataflow.ExternalFlow
3+
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
4+
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
5+
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
6+
private import semmle.code.csharp.security.dataflow.flowsources.Remote
7+
private import ModelEditor
8+
9+
/**
10+
* A class of effectively public callables in library code.
11+
*/
12+
class ExternalEndpoint extends Endpoint {
13+
ExternalEndpoint() { this.fromLibrary() }
14+
15+
/** Gets a node that is an input to a call to this API. */
16+
private ArgumentNode getAnInput() {
17+
result
18+
.getCall()
19+
.(DataFlowDispatch::NonDelegateDataFlowCall)
20+
.getATarget(_)
21+
.getUnboundDeclaration() = this
22+
}
23+
24+
/** Gets a node that is an output from a call to this API. */
25+
private DataFlow::Node getAnOutput() {
26+
exists(Call c, DataFlowDispatch::NonDelegateDataFlowCall dc |
27+
dc.getDispatchCall().getCall() = c and
28+
c.getTarget().getUnboundDeclaration() = this
29+
|
30+
result = DataFlowDispatch::getAnOutNode(dc, _)
31+
)
32+
}
33+
34+
override predicate hasSummary() {
35+
Endpoint.super.hasSummary()
36+
or
37+
defaultAdditionalTaintStep(this.getAnInput(), _)
38+
}
39+
40+
override predicate isSource() {
41+
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
42+
}
43+
44+
override predicate isSink() { sinkNode(this.getAnInput(), _) }
45+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Fetch endpoints for use in the model editor (framework mode)
3+
* @description A list of endpoints accessible (methods and attributes) for consumers of the library. Excludes test and generated code.
4+
* @kind table
5+
* @id csharp/utils/modeleditor/framework-mode-endpoints
6+
* @tags modeleditor endpoints framework-mode
7+
*/
8+
9+
import csharp
10+
import FrameworkModeEndpointsQuery
11+
import ModelEditor
12+
13+
from PublicEndpointFromSource endpoint, boolean supported, string type
14+
where
15+
supported = isSupported(endpoint) and
16+
type = supportedType(endpoint)
17+
select endpoint, endpoint.getNamespace(), endpoint.getTypeName(), endpoint.getName(),
18+
endpoint.getParameterTypes(), supported, endpoint.getFile().getBaseName(), type
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
private import csharp
2+
private import semmle.code.csharp.dataflow.ExternalFlow
3+
private import semmle.code.csharp.frameworks.Test
4+
private import ModelEditor
5+
6+
/**
7+
* A class of effectively public callables from source code.
8+
*/
9+
class PublicEndpointFromSource extends Endpoint {
10+
PublicEndpointFromSource() { this.fromSource() and not this.getFile() instanceof TestFile }
11+
12+
override predicate isSource() { this instanceof SourceCallable }
13+
14+
override predicate isSink() { this instanceof SinkCallable }
15+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/** Provides classes and predicates related to handling APIs for the VS Code extension. */
2+
3+
private import csharp
4+
private import semmle.code.csharp.dataflow.FlowSummary
5+
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
6+
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
7+
private import semmle.code.csharp.frameworks.Test
8+
private import Telemetry.TestLibrary
9+
10+
/** Holds if the given callable is not worth supporting. */
11+
private predicate isUninteresting(Callable c) {
12+
c.getDeclaringType() instanceof TestLibrary or
13+
c.(Constructor).isParameterless() or
14+
c.getDeclaringType() instanceof AnonymousClass
15+
}
16+
17+
/**
18+
* A callable method or accessor from either the C# Standard Library, a 3rd party library, or from the source.
19+
*/
20+
class Endpoint extends Callable {
21+
Endpoint() {
22+
[this.(Modifiable), this.(Accessor).getDeclaration()].isEffectivelyPublic() and
23+
not isUninteresting(this) and
24+
this.isUnboundDeclaration()
25+
}
26+
27+
/**
28+
* Gets the namespace of this endpoint.
29+
*/
30+
bindingset[this]
31+
string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) }
32+
33+
/**
34+
* Gets the unbound type name of this endpoint.
35+
*/
36+
bindingset[this]
37+
string getTypeName() { result = nestedName(this.getDeclaringType().getUnboundDeclaration()) }
38+
39+
/**
40+
* Gets the parameter types of this endpoint.
41+
*/
42+
bindingset[this]
43+
string getParameterTypes() { result = parameterQualifiedTypeNamesToString(this) }
44+
45+
private string getDllName() { result = this.getLocation().(Assembly).getName() }
46+
47+
private string getDllVersion() { result = this.getLocation().(Assembly).getVersion().toString() }
48+
49+
string dllName() {
50+
result = this.getDllName()
51+
or
52+
not exists(this.getDllName()) and result = this.getFile().getBaseName()
53+
}
54+
55+
string dllVersion() {
56+
result = this.getDllVersion()
57+
or
58+
not exists(this.getDllVersion()) and result = ""
59+
}
60+
61+
/** Holds if this API has a supported summary. */
62+
pragma[nomagic]
63+
predicate hasSummary() { this instanceof SummarizedCallable }
64+
65+
/** Holds if this API is a known source. */
66+
pragma[nomagic]
67+
abstract predicate isSource();
68+
69+
/** Holds if this API is a known sink. */
70+
pragma[nomagic]
71+
abstract predicate isSink();
72+
73+
/** Holds if this API is a known neutral. */
74+
pragma[nomagic]
75+
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
76+
77+
/**
78+
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
79+
* recognized source, sink or neutral or it has a flow summary.
80+
*/
81+
predicate isSupported() {
82+
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
83+
}
84+
}
85+
86+
boolean isSupported(Endpoint endpoint) {
87+
if endpoint.isSupported() then result = true else result = false
88+
}
89+
90+
string supportedType(Endpoint endpoint) {
91+
endpoint.isSink() and result = "sink"
92+
or
93+
endpoint.isSource() and result = "source"
94+
or
95+
endpoint.hasSummary() and result = "summary"
96+
or
97+
endpoint.isNeutral() and result = "neutral"
98+
or
99+
not endpoint.isSupported() and result = ""
100+
}
101+
102+
string methodClassification(Call method) {
103+
method.getFile() instanceof TestFile and result = "test"
104+
or
105+
not method.getFile() instanceof TestFile and
106+
result = "source"
107+
}
108+
109+
/**
110+
* Gets the nested name of the type `t`.
111+
*
112+
* If the type is not a nested type, the result is the same as `getName()`.
113+
* Otherwise the name of the nested type is prefixed with a `+` and appended to
114+
* the name of the enclosing type, which might be a nested type as well.
115+
*/
116+
private string nestedName(Type t) {
117+
not exists(t.getDeclaringType().getUnboundDeclaration()) and
118+
result = t.getName()
119+
or
120+
nestedName(t.getDeclaringType().getUnboundDeclaration()) + "+" + t.getName() = result
121+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
| NonPublicClass.cs:9:9:9:31 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
2+
| PublicClass.cs:9:9:9:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
3+
| PublicClass.cs:14:9:14:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
4+
| PublicClass.cs:19:9:19:51 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
5+
| PublicClass.cs:19:33:19:50 | call to method ReadLine | System | Console | ReadLine | | true | System.Console | 7.0.0.0 | neutral | source |
6+
| PublicClass.cs:19:33:19:50 | call to method ReadLine | System | Console | ReadLine | | true | System.Console | 7.0.0.0 | source | source |
7+
| PublicClass.cs:24:9:24:46 | call to method Write | System | Console | Write | System.Object | true | System.Console | 7.0.0.0 | neutral | source |
8+
| PublicClass.cs:24:23:24:45 | access to property BackgroundColor | System | Console | get_BackgroundColor | | true | System.Console | 7.0.0.0 | neutral | source |
9+
| PublicClass.cs:25:9:25:31 | access to property ForegroundColor | System | Console | set_ForegroundColor | System.ConsoleColor | true | System.Console | 7.0.0.0 | neutral | source |
10+
| PublicClass.cs:30:9:30:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
11+
| PublicGenericClass.cs:9:9:9:30 | call to method WriteLine | System | Console | WriteLine | System.Object | true | System.Console | 7.0.0.0 | neutral | source |
12+
| PublicGenericClass.cs:14:9:14:30 | call to method WriteLine | System | Console | WriteLine | System.Object | true | System.Console | 7.0.0.0 | neutral | source |
13+
| PublicGenericInterface.cs:13:9:13:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
14+
| PublicInterface.cs:13:9:13:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
utils/modeleditor/ApplicationModeEndpoints.ql

0 commit comments

Comments
 (0)