Skip to content

Commit

Permalink
Merge pull request #14200 from github/koesie10/add-csharp-model-edito…
Browse files Browse the repository at this point in the history
…r-queries

C#: Add VS Code model editor queries
  • Loading branch information
koesie10 authored Sep 28, 2023
2 parents 3949914 + 922ff7b commit 0f4f987
Show file tree
Hide file tree
Showing 22 changed files with 429 additions and 18 deletions.
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 @@ -10,32 +9,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
45 changes: 45 additions & 0 deletions csharp/ql/src/utils/modeleditor/ApplicationModeEndpointsQuery.qll
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
}
14 changes: 14 additions & 0 deletions csharp/ql/test/utils/modeleditor/ApplicationModeEndpoints.expected
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
18 changes: 18 additions & 0 deletions csharp/ql/test/utils/modeleditor/FrameworkModeEndpoints.expected
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

0 comments on commit 0f4f987

Please sign in to comment.