Merge pull request #14200 from github/koesie10/add-csharp-model-editor-queries

C#: Add VS Code model editor queries
This commit is contained in:
Koen Vlaswinkel
2023-09-28 10:12:57 +02:00
committed by GitHub
22 changed files with 429 additions and 18 deletions

View File

@@ -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 {
/**

View File

@@ -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
@@ -10,24 +9,10 @@ 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()
}
@@ -35,7 +20,7 @@ private predicate isUninteresting(DotNet::Callable c) {
/**
* 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

View File

@@ -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()) }
}

View File

@@ -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

View File

@@ -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(), _) }
}

View File

@@ -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

View File

@@ -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 }
}

View File

@@ -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
}

View File

@@ -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 |

View File

@@ -0,0 +1 @@
utils/modeleditor/ApplicationModeEndpoints.ql

View File

@@ -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 | |

View File

@@ -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"]

View File

@@ -0,0 +1 @@
utils/modeleditor/FrameworkModeEndpoints.ql

View File

@@ -0,0 +1,11 @@
using System;
namespace GitHub.CodeQL;
class NonPublicClass
{
public void noCandidates(String here)
{
Console.WriteLine(here);
}
}

View File

@@ -0,0 +1,54 @@
using System;
namespace GitHub.CodeQL;
public class PublicClass : PublicInterface
{
public void stuff(String arg)
{
Console.WriteLine(arg);
}
public static void staticStuff(String arg)
{
Console.WriteLine(arg);
}
protected void protectedStuff(String arg)
{
Console.WriteLine(arg + Console.ReadLine());
}
private void privateStuff(String arg)
{
Console.Write(Console.BackgroundColor);
Console.ForegroundColor = ConsoleColor.Red;
}
internal void internalStuff(String arg)
{
Console.WriteLine(arg);
}
string PublicInterface.PublicProperty { get; set; }
public string summaryStuff(String arg)
{
return arg;
}
public string sourceStuff()
{
return "stuff";
}
public void sinkStuff(String arg)
{
// do nothing
}
public void neutralStuff(String arg)
{
// do nothing
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace GitHub.CodeQL;
public class PublicGenericClass<T, T2> : PublicGenericInterface<T>
{
public void stuff(T arg)
{
Console.WriteLine(arg);
}
public void stuff2<T2>(T2 arg)
{
Console.WriteLine(arg);
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace GitHub.CodeQL;
public interface PublicGenericInterface<T>
{
void stuff(T arg);
void stuff2<T2>(T2 arg);
static void staticStuff(String arg)
{
Console.WriteLine(arg);
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace GitHub.CodeQL;
public interface PublicInterface
{
void stuff(String arg);
string PublicProperty { get; set; }
static void staticStuff(String arg)
{
Console.WriteLine(arg);
}
}

View File

@@ -30,4 +30,5 @@
- /Diagnostics/Internal/.*/
- exclude:
tags contain:
- modeleditor
- modelgenerator

View File

@@ -31,4 +31,5 @@
- /Diagnostics/Internal/.*/
- exclude:
tags contain:
- modeleditor
- modelgenerator

View File

@@ -42,4 +42,5 @@
- /Diagnostics/Internal/.*/
- exclude:
tags contain:
- modeleditor
- model-generator

View File

@@ -36,4 +36,5 @@
- /Diagnostics/Internal/.*/
- exclude:
tags contain:
- modeleditor
- modelgenerator