Skip to content

Commit 585fb9d

Browse files
committed
C#: Add VS Code model editor queries
1 parent 3bf0d66 commit 585fb9d

14 files changed

+302
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/** Provides classes and predicates related to handling APIs for the VS Code extension. */
2+
3+
private import csharp
4+
private import dotnet
5+
private import semmle.code.csharp.dispatch.Dispatch
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.internal.FlowSummaryImpl as FlowSummaryImpl
12+
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
13+
private import semmle.code.csharp.frameworks.Test
14+
private import semmle.code.csharp.security.dataflow.flowsources.Remote
15+
16+
pragma[nomagic]
17+
private predicate isTestNamespace(Namespace ns) {
18+
ns.getFullName()
19+
.matches([
20+
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
21+
])
22+
}
23+
24+
/**
25+
* A test library.
26+
*/
27+
class TestLibrary extends RefType {
28+
TestLibrary() { isTestNamespace(this.getNamespace()) }
29+
}
30+
31+
/** Holds if the given callable is not worth supporting. */
32+
private predicate isUninteresting(DotNet::Declaration c) {
33+
c.getDeclaringType() instanceof TestLibrary or
34+
c.(Constructor).isParameterless() or
35+
c.getDeclaringType() instanceof AnonymousClass
36+
}
37+
38+
/**
39+
* An callable method from either the C# Standard Library, a 3rd party library, or from the source.
40+
*/
41+
class CallableMethod extends DotNet::Declaration {
42+
CallableMethod() {
43+
this.(Modifiable).isEffectivelyPublic() and
44+
not isUninteresting(this)
45+
}
46+
47+
/**
48+
* Gets the unbound type, name and parameter types of this API.
49+
*/
50+
bindingset[this]
51+
private string getSignature() {
52+
result =
53+
nestedName(this.getDeclaringType().getUnboundDeclaration()) + "#" + this.getName() + "(" +
54+
parameterQualifiedTypeNamesToString(this) + ")"
55+
}
56+
57+
/**
58+
* Gets the namespace of this API.
59+
*/
60+
bindingset[this]
61+
string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) }
62+
63+
/**
64+
* Gets the namespace and signature of this API.
65+
*/
66+
bindingset[this]
67+
string getApiName() { result = this.getNamespace() + "." + this.getSignature() }
68+
69+
private string getDllName() { result = this.getLocation().(Assembly).getName() }
70+
71+
private string getDllVersion() { result = this.getLocation().(Assembly).getVersion().toString() }
72+
73+
string dllName() {
74+
result = this.getDllName()
75+
or
76+
not exists(this.getDllName()) and result = this.getFile().getBaseName()
77+
}
78+
79+
string dllVersion() {
80+
result = this.getDllVersion()
81+
or
82+
not exists(this.getDllVersion()) and result = ""
83+
}
84+
85+
/** Gets a node that is an input to a call to this API. */
86+
private ArgumentNode getAnInput() {
87+
result
88+
.getCall()
89+
.(DataFlowDispatch::NonDelegateDataFlowCall)
90+
.getATarget(_)
91+
.getUnboundDeclaration() = this
92+
}
93+
94+
/** Gets a node that is an output from a call to this API. */
95+
private DataFlow::Node getAnOutput() {
96+
exists(
97+
Call c, DataFlowDispatch::NonDelegateDataFlowCall dc, DataFlowImplCommon::ReturnKindExt ret
98+
|
99+
dc.getDispatchCall().getCall() = c and
100+
c.getTarget().getUnboundDeclaration() = this
101+
|
102+
result = ret.getAnOutNode(dc)
103+
)
104+
}
105+
106+
/** Holds if this API has a supported summary. */
107+
pragma[nomagic]
108+
predicate hasSummary() {
109+
this instanceof SummarizedCallable
110+
or
111+
defaultAdditionalTaintStep(this.getAnInput(), _)
112+
}
113+
114+
/** Holds if this API is a known source. */
115+
pragma[nomagic]
116+
predicate isSource() {
117+
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
118+
}
119+
120+
/** Holds if this API is a known sink. */
121+
pragma[nomagic]
122+
predicate isSink() { sinkNode(this.getAnInput(), _) }
123+
124+
/** Holds if this API is a known neutral. */
125+
pragma[nomagic]
126+
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
127+
128+
/**
129+
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
130+
* recognized source, sink or neutral or it has a flow summary.
131+
*/
132+
predicate isSupported() {
133+
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
134+
}
135+
}
136+
137+
boolean isSupported(CallableMethod callableMethod) {
138+
callableMethod.isSupported() and result = true
139+
or
140+
not callableMethod.isSupported() and
141+
result = false
142+
}
143+
144+
string supportedType(CallableMethod method) {
145+
method.isSink() and result = "sink"
146+
or
147+
method.isSource() and result = "source"
148+
or
149+
method.hasSummary() and result = "summary"
150+
or
151+
method.isNeutral() and result = "neutral"
152+
or
153+
not method.isSupported() and result = ""
154+
}
155+
156+
string methodClassification(Call method) {
157+
method.getFile() instanceof TestFile and result = "test"
158+
or
159+
not method.getFile() instanceof TestFile and
160+
result = "source"
161+
}
162+
163+
/**
164+
* Gets the nested name of the declaration.
165+
*
166+
* If the declaration is not a nested type, the result is the same as `getName()`.
167+
* Otherwise the name of the nested type is prefixed with a `+` and appended to
168+
* the name of the enclosing type, which might be a nested type as well.
169+
*/
170+
private string nestedName(Declaration declaration) {
171+
not exists(declaration.getDeclaringType().getUnboundDeclaration()) and
172+
result = declaration.getName()
173+
or
174+
nestedName(declaration.getDeclaringType().getUnboundDeclaration()) + "+" + declaration.getName() =
175+
result
176+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @name Fetch model editor methods (application mode)
3+
* @description A list of 3rd party APIs used in the codebase. Excludes test and generated code.
4+
* @kind problem
5+
* @problem.severity recommendation
6+
* @id csharp/utils/modeleditor/fetch-application-mode-methods
7+
* @tags modeleditor fetch methods application-mode
8+
*/
9+
10+
private import csharp
11+
private import AutomodelVsCode
12+
13+
class ExternalApi extends CallableMethod {
14+
ExternalApi() {
15+
this.isUnboundDeclaration() and
16+
this.fromLibrary() and
17+
this.(Modifiable).isEffectivelyPublic()
18+
}
19+
}
20+
21+
private Call aUsage(ExternalApi api) { result.getTarget().getUnboundDeclaration() = api }
22+
23+
from
24+
ExternalApi api, string apiName, boolean supported, Call usage, string type, string classification
25+
where
26+
apiName = api.getApiName() and
27+
supported = isSupported(api) and
28+
usage = aUsage(api) and
29+
type = supportedType(api) and
30+
classification = methodClassification(usage)
31+
select usage, apiName, supported.toString(), "supported", api.dllName(), api.dllVersion(), type,
32+
"type", classification, "classification"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @name Fetch model editor methods (framework mode)
3+
* @description A list of APIs callable by consumers. Excludes test and generated code.
4+
* @kind problem
5+
* @problem.severity recommendation
6+
* @id csharp/utils/modeleditor/fetch-framework-mode-methods
7+
* @tags modeleditor fetch methods framework-mode
8+
*/
9+
10+
private import csharp
11+
private import dotnet
12+
private import semmle.code.csharp.frameworks.Test
13+
private import AutomodelVsCode
14+
15+
class PublicMethod extends CallableMethod {
16+
PublicMethod() { this.fromSource() and not this.getFile() instanceof TestFile }
17+
}
18+
19+
from PublicMethod publicMethod, string apiName, boolean supported, string type
20+
where
21+
apiName = publicMethod.getApiName() and
22+
supported = isSupported(publicMethod) and
23+
type = supportedType(publicMethod)
24+
select publicMethod, apiName, supported.toString(), "supported",
25+
publicMethod.getFile().getBaseName(), "library", type, "type", "unknown", "classification"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| NonPublicClass.cs:9:9:9:31 | call to method WriteLine | System.Console#WriteLine(System.String) | true | supported | System.Console | 7.0.0.0 | neutral | type | source | classification |
2+
| PublicClass.cs:9:9:9:30 | call to method WriteLine | System.Console#WriteLine(System.String) | true | supported | System.Console | 7.0.0.0 | neutral | type | source | classification |
3+
| PublicClass.cs:14:9:14:30 | call to method WriteLine | System.Console#WriteLine(System.String) | true | supported | System.Console | 7.0.0.0 | neutral | type | source | classification |
4+
| PublicClass.cs:19:9:19:51 | call to method WriteLine | System.Console#WriteLine(System.String) | true | supported | System.Console | 7.0.0.0 | neutral | type | source | classification |
5+
| PublicClass.cs:19:33:19:50 | call to method ReadLine | System.Console#ReadLine() | true | supported | System.Console | 7.0.0.0 | neutral | type | source | classification |
6+
| PublicClass.cs:19:33:19:50 | call to method ReadLine | System.Console#ReadLine() | true | supported | System.Console | 7.0.0.0 | source | type | source | classification |
7+
| PublicClass.cs:24:9:24:30 | call to method WriteLine | System.Console#WriteLine(System.String) | true | supported | System.Console | 7.0.0.0 | neutral | type | source | classification |
8+
| PublicInterface.cs:11:9:11:30 | call to method WriteLine | System.Console#WriteLine(System.String) | true | supported | System.Console | 7.0.0.0 | neutral | type | source | classification |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
utils/modeleditor/FetchApplicationModeMethods.ql
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| PublicClass.cs:7:17:7:21 | stuff | GitHub.CodeQL.PublicClass#stuff(System.String) | false | supported | PublicClass.cs | library | | type | unknown | classification |
2+
| PublicClass.cs:12:24:12:34 | staticStuff | GitHub.CodeQL.PublicClass#staticStuff(System.String) | false | supported | PublicClass.cs | library | | type | unknown | classification |
3+
| PublicClass.cs:17:20:17:33 | nonPublicStuff | GitHub.CodeQL.PublicClass#nonPublicStuff(System.String) | false | supported | PublicClass.cs | library | | type | unknown | classification |
4+
| PublicInterface.cs:7:10:7:14 | stuff | GitHub.CodeQL.PublicInterface#stuff(System.String) | false | supported | PublicInterface.cs | library | | type | unknown | classification |
5+
| PublicInterface.cs:9:17:9:27 | staticStuff | GitHub.CodeQL.PublicInterface#staticStuff(System.String) | false | supported | PublicInterface.cs | library | | type | unknown | classification |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
utils/modeleditor/FetchFrameworkModeMethods.ql
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace GitHub.CodeQL;
4+
5+
class NonPublicClass
6+
{
7+
public void noCandidates(String here)
8+
{
9+
Console.WriteLine(here);
10+
}
11+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
3+
namespace GitHub.CodeQL;
4+
5+
public class PublicClass
6+
{
7+
public void stuff(String arg)
8+
{
9+
Console.WriteLine(arg);
10+
}
11+
12+
public static void staticStuff(String arg)
13+
{
14+
Console.WriteLine(arg);
15+
}
16+
17+
protected void nonPublicStuff(String arg)
18+
{
19+
Console.WriteLine(arg + Console.ReadLine());
20+
}
21+
22+
internal void internalStuff(String arg)
23+
{
24+
Console.WriteLine(arg);
25+
}
26+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
3+
namespace GitHub.CodeQL;
4+
5+
public interface PublicInterface
6+
{
7+
void stuff(String arg);
8+
9+
static void staticStuff(String arg)
10+
{
11+
Console.WriteLine(arg);
12+
}
13+
}

0 commit comments

Comments
 (0)