Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C#: Add VS Code model editor queries #14200

Merged
merged 22 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
585fb9d
C#: Add VS Code model editor queries
koesie10 Sep 13, 2023
0cc74a2
C#: Extract TestLibrary to separate module
koesie10 Sep 18, 2023
e524e35
C#: Check accessor declaration for publicness
koesie10 Sep 18, 2023
ff2cef3
C#: Switch from Declaration to Callable
koesie10 Sep 18, 2023
8472b84
C#: Remove unnecessary isEffectivelyPublic predicate
koesie10 Sep 18, 2023
f468b2a
C#: Add tests for generic interfaces/classes/methods
koesie10 Sep 18, 2023
4693f72
C#: Rename CallableMethod to Endpoint
koesie10 Sep 18, 2023
93972a4
C#: Rename AutomodelVsCode to ModelEditor
koesie10 Sep 18, 2023
81a8eee
C#: Only include unbound declarations in endpoints
koesie10 Sep 18, 2023
948e36a
C#: Update comment for Endpoint
koesie10 Sep 18, 2023
489561f
C#: Fix formatting of ExternalApi
koesie10 Sep 18, 2023
dd79049
C#: Remove unnecessary isUnboundDeclaration predicates
koesie10 Sep 19, 2023
14a2b7f
C#: Add tests for private methods and accessors
koesie10 Sep 19, 2023
eace7a4
C#: Add tests for supported framework methods
koesie10 Sep 19, 2023
044fb9f
C#: Rename queries from fetch methods to endpoints
koesie10 Sep 19, 2023
3ebb9e1
C#: Update query id/tags and documentation
koesie10 Sep 19, 2023
45432f2
C#: Identify whether callables in the source code are supported in te…
michaelnebel Sep 20, 2023
50a9219
C#: Re-factor most of the logic out of the model editor query files.
michaelnebel Sep 20, 2023
13dd9a6
C#: Address review comments.
michaelnebel Sep 20, 2023
0fea21f
C#: Remove unnecessary columns
koesie10 Sep 22, 2023
dc6def7
C#: Split API name column into separate columns
koesie10 Sep 25, 2023
922ff7b
C#: Remove unnecessary import
koesie10 Sep 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,20 @@ Element interpretElement(
)
}

/**
* A callable where there exists a MaD sink model that applies to it.
*/
class SinkCallable extends Callable {
SinkCallable() { sinkElement(this, _, _, _) }
}

/**
* A callable where there exists a MaD source model that applies to it.
*/
class SourceCallable extends Callable {
SourceCallable() { sourceElement(this, _, _, _) }
}

cached
private module Cached {
/**
Expand Down
21 changes: 3 additions & 18 deletions csharp/ql/src/Telemetry/ExternalApi.qll
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/** Provides classes and predicates related to handling APIs from external libraries. */

private import csharp
private import dotnet
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.FlowSummary
Expand All @@ -11,32 +10,18 @@ private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlow
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.Remote

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()) }
}
private import TestLibrary

/** Holds if the given callable is not worth supporting. */
private predicate isUninteresting(DotNet::Callable c) {
private predicate isUninteresting(Callable c) {
c.getDeclaringType() instanceof TestLibrary or
c.(Constructor).isParameterless()
}

/**
* An external API from either the C# Standard Library or a 3rd party library.
*/
class ExternalApi extends DotNet::Callable {
class ExternalApi extends Callable {
ExternalApi() {
this.isUnboundDeclaration() and
this.fromLibrary() and
Expand Down
17 changes: 17 additions & 0 deletions csharp/ql/src/Telemetry/TestLibrary.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
private import csharp
private import dotnet

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()) }
}
23 changes: 23 additions & 0 deletions csharp/ql/src/utils/modeleditor/ApplicationModeEndpoints.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @name Fetch endpoints for use in the model editor (application mode)
* @description A list of 3rd party endpoints (methods and attributes) used in the codebase. Excludes test and generated code.
* @kind table
* @id csharp/utils/modeleditor/application-mode-endpoints
* @tags modeleditor endpoints application-mode
*/

import csharp
import ApplicationModeEndpointsQuery
import ModelEditor

private Call aUsage(ExternalEndpoint api) { result.getTarget().getUnboundDeclaration() = api }

from ExternalEndpoint endpoint, boolean supported, Call usage, string type, string classification
where
supported = isSupported(endpoint) and
usage = aUsage(endpoint) and
type = supportedType(endpoint) and
classification = methodClassification(usage)
select usage, endpoint.getNamespace(), endpoint.getTypeName(), endpoint.getName(),
endpoint.getParameterTypes(), supported, endpoint.dllName(), endpoint.dllVersion(), type,
classification
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
private import csharp
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
private import semmle.code.csharp.security.dataflow.flowsources.Remote
private import ModelEditor

/**
* A class of effectively public callables in library code.
*/
class ExternalEndpoint extends Endpoint {
ExternalEndpoint() { this.fromLibrary() }

/** 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, _)
)
}

override predicate hasSummary() {
Endpoint.super.hasSummary()
or
defaultAdditionalTaintStep(this.getAnInput(), _)
}

override predicate isSource() {
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
}

override predicate isSink() { sinkNode(this.getAnInput(), _) }
}
18 changes: 18 additions & 0 deletions csharp/ql/src/utils/modeleditor/FrameworkModeEndpoints.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @name Fetch endpoints for use in the model editor (framework mode)
* @description A list of endpoints accessible (methods and attributes) for consumers of the library. Excludes test and generated code.
* @kind table
* @id csharp/utils/modeleditor/framework-mode-endpoints
* @tags modeleditor endpoints framework-mode
*/

import csharp
import FrameworkModeEndpointsQuery
import ModelEditor

from PublicEndpointFromSource endpoint, boolean supported, string type
where
supported = isSupported(endpoint) and
type = supportedType(endpoint)
select endpoint, endpoint.getNamespace(), endpoint.getTypeName(), endpoint.getName(),
endpoint.getParameterTypes(), supported, endpoint.getFile().getBaseName(), type
15 changes: 15 additions & 0 deletions csharp/ql/src/utils/modeleditor/FrameworkModeEndpointsQuery.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
private import csharp
private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.frameworks.Test
private import ModelEditor

/**
* A class of effectively public callables from source code.
*/
class PublicEndpointFromSource extends Endpoint {
PublicEndpointFromSource() { this.fromSource() and not this.getFile() instanceof TestFile }

override predicate isSource() { this instanceof SourceCallable }

override predicate isSink() { this instanceof SinkCallable }
}
121 changes: 121 additions & 0 deletions csharp/ql/src/utils/modeleditor/ModelEditor.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/** Provides classes and predicates related to handling APIs for the VS Code extension. */

private import csharp
private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.frameworks.Test
private import Telemetry.TestLibrary

/** Holds if the given callable is not worth supporting. */
private predicate isUninteresting(Callable c) {
c.getDeclaringType() instanceof TestLibrary or
c.(Constructor).isParameterless() or
c.getDeclaringType() instanceof AnonymousClass
}

/**
* A callable method or accessor from either the C# Standard Library, a 3rd party library, or from the source.
*/
class Endpoint extends Callable {
Endpoint() {
[this.(Modifiable), this.(Accessor).getDeclaration()].isEffectivelyPublic() and
not isUninteresting(this) and
this.isUnboundDeclaration()
}

/**
* Gets the namespace of this endpoint.
*/
bindingset[this]
string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) }

/**
* Gets the unbound type name of this endpoint.
*/
bindingset[this]
string getTypeName() { result = nestedName(this.getDeclaringType().getUnboundDeclaration()) }

/**
* Gets the parameter types of this endpoint.
*/
bindingset[this]
string getParameterTypes() { result = parameterQualifiedTypeNamesToString(this) }

private string getDllName() { result = this.getLocation().(Assembly).getName() }

private string getDllVersion() { result = this.getLocation().(Assembly).getVersion().toString() }

string dllName() {
result = this.getDllName()
or
not exists(this.getDllName()) and result = this.getFile().getBaseName()
}

string dllVersion() {
result = this.getDllVersion()
or
not exists(this.getDllVersion()) and result = ""
}

/** Holds if this API has a supported summary. */
pragma[nomagic]
predicate hasSummary() { this instanceof SummarizedCallable }

/** Holds if this API is a known source. */
pragma[nomagic]
abstract predicate isSource();

/** Holds if this API is a known sink. */
pragma[nomagic]
abstract predicate isSink();

/** 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()
}
}

boolean isSupported(Endpoint endpoint) {
if endpoint.isSupported() then result = true else result = false
}

string supportedType(Endpoint endpoint) {
endpoint.isSink() and result = "sink"
or
endpoint.isSource() and result = "source"
or
endpoint.hasSummary() and result = "summary"
or
endpoint.isNeutral() and result = "neutral"
or
not endpoint.isSupported() and result = ""
}

string methodClassification(Call method) {
method.getFile() instanceof TestFile and result = "test"
or
not method.getFile() instanceof TestFile and
result = "source"
}

/**
* 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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
| NonPublicClass.cs:9:9:9:31 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
| PublicClass.cs:9:9:9:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
| PublicClass.cs:14:9:14:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
| PublicClass.cs:19:9:19:51 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
| PublicClass.cs:19:33:19:50 | call to method ReadLine | System | Console | ReadLine | | true | System.Console | 7.0.0.0 | neutral | source |
| PublicClass.cs:19:33:19:50 | call to method ReadLine | System | Console | ReadLine | | true | System.Console | 7.0.0.0 | source | source |
| PublicClass.cs:24:9:24:46 | call to method Write | System | Console | Write | System.Object | true | System.Console | 7.0.0.0 | neutral | source |
| PublicClass.cs:24:23:24:45 | access to property BackgroundColor | System | Console | get_BackgroundColor | | true | System.Console | 7.0.0.0 | neutral | source |
| 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 |
| PublicClass.cs:30:9:30:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
| PublicGenericClass.cs:9:9:9:30 | call to method WriteLine | System | Console | WriteLine | System.Object | true | System.Console | 7.0.0.0 | neutral | source |
| PublicGenericClass.cs:14:9:14:30 | call to method WriteLine | System | Console | WriteLine | System.Object | true | System.Console | 7.0.0.0 | neutral | source |
| PublicGenericInterface.cs:13:9:13:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
| PublicInterface.cs:13:9:13:30 | call to method WriteLine | System | Console | WriteLine | System.String | true | System.Console | 7.0.0.0 | neutral | source |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
utils/modeleditor/ApplicationModeEndpoints.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
| PublicClass.cs:7:17:7:21 | stuff | GitHub.CodeQL | PublicClass | stuff | System.String | false | PublicClass.cs | |
| PublicClass.cs:12:24:12:34 | staticStuff | GitHub.CodeQL | PublicClass | staticStuff | System.String | false | PublicClass.cs | |
| PublicClass.cs:17:20:17:33 | protectedStuff | GitHub.CodeQL | PublicClass | protectedStuff | System.String | false | PublicClass.cs | |
| PublicClass.cs:33:45:33:47 | get_PublicProperty | GitHub.CodeQL | PublicClass | get_PublicProperty | | false | PublicClass.cs | |
| PublicClass.cs:33:50:33:52 | set_PublicProperty | GitHub.CodeQL | PublicClass | set_PublicProperty | System.String | false | PublicClass.cs | |
| PublicClass.cs:35:19:35:30 | summaryStuff | GitHub.CodeQL | PublicClass | summaryStuff | System.String | true | PublicClass.cs | summary |
| PublicClass.cs:40:19:40:29 | sourceStuff | GitHub.CodeQL | PublicClass | sourceStuff | | true | PublicClass.cs | source |
| PublicClass.cs:45:17:45:25 | sinkStuff | GitHub.CodeQL | PublicClass | sinkStuff | System.String | true | PublicClass.cs | sink |
| PublicClass.cs:50:17:50:28 | neutralStuff | GitHub.CodeQL | PublicClass | neutralStuff | System.String | true | PublicClass.cs | neutral |
| PublicGenericClass.cs:7:17:7:21 | stuff | GitHub.CodeQL | PublicGenericClass<,> | stuff | T | false | PublicGenericClass.cs | |
| PublicGenericClass.cs:12:17:12:26 | stuff2<> | GitHub.CodeQL | PublicGenericClass<,> | stuff2<> | T2 | false | PublicGenericClass.cs | |
| PublicGenericInterface.cs:7:10:7:14 | stuff | GitHub.CodeQL | PublicGenericInterface<> | stuff | T | false | PublicGenericInterface.cs | |
| PublicGenericInterface.cs:9:10:9:19 | stuff2<> | GitHub.CodeQL | PublicGenericInterface<> | stuff2<> | T2 | false | PublicGenericInterface.cs | |
| PublicGenericInterface.cs:11:17:11:27 | staticStuff | GitHub.CodeQL | PublicGenericInterface<> | staticStuff | System.String | false | PublicGenericInterface.cs | |
| PublicInterface.cs:7:10:7:14 | stuff | GitHub.CodeQL | PublicInterface | stuff | System.String | false | PublicInterface.cs | |
| PublicInterface.cs:9:29:9:31 | get_PublicProperty | GitHub.CodeQL | PublicInterface | get_PublicProperty | | false | PublicInterface.cs | |
| PublicInterface.cs:9:34:9:36 | set_PublicProperty | GitHub.CodeQL | PublicInterface | set_PublicProperty | System.String | false | PublicInterface.cs | |
| PublicInterface.cs:11:17:11:27 | staticStuff | GitHub.CodeQL | PublicInterface | staticStuff | System.String | false | PublicInterface.cs | |
24 changes: 24 additions & 0 deletions csharp/ql/test/utils/modeleditor/FrameworkModeEndpoints.ext.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
extensions:
- addsTo:
pack: codeql/csharp-all
extensible: sourceModel
data:
- ["GitHub.CodeQL","PublicClass",true,"sourceStuff","()","","ReturnValue","remote","manual"]

- addsTo:
pack: codeql/csharp-all
extensible: sinkModel
data:
- ["GitHub.CodeQL","PublicClass",true,"sinkStuff","(System.String)","","Argument[0]","sql-injection","manual"]

- addsTo:
pack: codeql/csharp-all
extensible: summaryModel
data:
- ["GitHub.CodeQL","PublicClass",true,"summaryStuff","(System.String)","","Argument[0]","ReturnValue","taint","manual"]

- addsTo:
pack: codeql/csharp-all
extensible: neutralModel
data:
- ["GitHub.CodeQL","PublicClass","neutralStuff","(System.String)","summary","manual"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
utils/modeleditor/FrameworkModeEndpoints.ql
11 changes: 11 additions & 0 deletions csharp/ql/test/utils/modeleditor/NonPublicClass.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace GitHub.CodeQL;

class NonPublicClass
{
public void noCandidates(String here)
{
Console.WriteLine(here);
}
}
Loading
Loading