Merge pull request #13432 from michaelnebel/updateissupported

Java/C#: Update telemetry queries to report callables with sink/source neutrals as being supported.
This commit is contained in:
Michael Nebel
2023-08-22 08:39:38 +02:00
committed by GitHub
24 changed files with 30266 additions and 30087 deletions

View File

@@ -175,8 +175,6 @@ class Provenance = Impl::Public::Provenance;
class SummarizedCallable = Impl::Public::SummarizedCallable;
class NeutralCallable = Impl::Public::NeutralCallable;
/**
* An adapter class to add the flow summaries specified on `SyntheticCallable`
* to `SummarizedCallable`.

View File

@@ -2,16 +2,16 @@ private import java
private import DataFlowPrivate
private import DataFlowUtil
private import semmle.code.java.dataflow.InstanceAccess
private import semmle.code.java.dataflow.FlowSummary
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as Impl
private import semmle.code.java.dispatch.VirtualDispatch as VirtualDispatch
private import semmle.code.java.dataflow.TypeFlow
private import semmle.code.java.dispatch.internal.Unification
private module DispatchImpl {
private predicate hasHighConfidenceTarget(Call c) {
exists(SummarizedCallable sc | sc.getACall() = c and not sc.applyGeneratedModel())
exists(Impl::Public::SummarizedCallable sc | sc.getACall() = c and not sc.applyGeneratedModel())
or
exists(NeutralCallable nc | nc.getACall() = c and nc.hasManualModel())
exists(Impl::Public::NeutralSummaryCallable nc | nc.getACall() = c and nc.hasManualModel())
or
exists(Callable srcTgt |
srcTgt = VirtualDispatch::viableCallable(c) and

View File

@@ -296,11 +296,21 @@ module Public {
predicate hasProvenance(Provenance provenance) { provenance = "manual" }
}
/** A callable where there is no flow via the callable. */
class NeutralCallable extends SummarizedCallableBase {
/**
* A callable where there is no flow via the callable.
*/
class NeutralSummaryCallable extends NeutralCallable {
NeutralSummaryCallable() { this.getKind() = "summary" }
}
/**
* A callable that has a neutral model.
*/
class NeutralCallable extends NeutralCallableBase {
private string kind;
private Provenance provenance;
NeutralCallable() { neutralSummaryElement(this, provenance) }
NeutralCallable() { neutralElement(this, kind, provenance) }
/**
* Holds if the neutral is auto generated.
@@ -316,6 +326,11 @@ module Public {
* Holds if the neutral has provenance `p`.
*/
predicate hasProvenance(Provenance p) { p = provenance }
/**
* Gets the kind of the neutral.
*/
string getKind() { result = kind }
}
}
@@ -1318,6 +1333,11 @@ module Private {
/** Gets the string representation of this callable used by `neutral/1`. */
abstract string getCallableCsv();
/**
* Gets the kind of the neutral.
*/
string getKind() { result = super.getKind() }
string toString() { result = super.toString() }
}
@@ -1358,12 +1378,13 @@ module Private {
/**
* Holds if a neutral model `csv` exists (semi-colon separated format). Used for testing purposes.
* The syntax is: "namespace;type;name;signature;provenance"",
* The syntax is: "namespace;type;name;signature;kind;provenance"",
*/
query predicate neutral(string csv) {
exists(RelevantNeutralCallable c |
csv =
c.getCallableCsv() // Callable information
+ c.getKind() + ";" // kind
+ renderProvenanceNeutral(c) // provenance
)
}

View File

@@ -14,6 +14,16 @@ private import semmle.code.java.dataflow.internal.AccessPathSyntax as AccessPath
class SummarizedCallableBase = FlowSummary::SummarizedCallableBase;
/**
* A class of callables that are candidates for neutral modeling.
*/
class NeutralCallableBase extends Callable {
NeutralCallableBase() { this.isSourceDeclaration() }
/** Gets a call that targets this neutral. */
Call getACall() { result.getCallee().getSourceDeclaration() = this }
}
/**
* A module for importing frameworks that define synthetic globals.
*/
@@ -156,13 +166,13 @@ predicate summaryElement(
}
/**
* Holds if a neutral summary model exists for `c` with provenance `provenance`,
* which means that there is no flow through `c`.
* Holds if a neutral model exists for `c` of kind `kind`
* and with provenance `provenance`.
*/
predicate neutralSummaryElement(SummarizedCallableBase c, string provenance) {
predicate neutralElement(NeutralCallableBase c, string kind, string provenance) {
exists(string namespace, string type, string name, string signature |
neutralModel(namespace, type, name, signature, "summary", provenance) and
c.asCallable() = interpretElement(namespace, type, false, name, signature, "")
neutralModel(namespace, type, name, signature, kind, provenance) and
c = interpretElement(namespace, type, false, name, signature, "")
)
}

View File

@@ -45,12 +45,10 @@ private int getNumApis(string package, string apiSubset) {
/** Holds if the given `callable` belongs to the specified `apiSubset`. */
private predicate callableSubset(Callable callable, string apiSubset) {
apiSubset in ["topJdkApis", "allApis"] and
(
if apiSubset = "topJdkApis"
then exists(TopJdkApi topJdkApi | callable = topJdkApi.asCallable())
else apiSubset = "allApis"
)
apiSubset = "topJdkApis" and
callable instanceof TopJdkApi
or
apiSubset = "allApis"
}
/**

View File

@@ -1,7 +1,6 @@
/** Provides classes and predicates for working with Top JDK APIs. */
import java
private import semmle.code.java.dataflow.FlowSummary
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.java.dataflow.ExternalFlow
@@ -287,24 +286,29 @@ predicate hasApiName(Callable c, string apiName) {
}
/** A top JDK API. */
class TopJdkApi extends SummarizedCallableBase {
class TopJdkApi extends Callable {
TopJdkApi() {
this.isSourceDeclaration() and
exists(string apiName |
hasApiName(this.asCallable(), apiName) and
hasApiName(this, apiName) and
topJdkApiName(apiName)
)
}
/** Holds if this API has a manual summary model. */
private predicate hasManualSummary() { this.(SummarizedCallable).hasManualModel() }
private predicate hasManualSummary() {
exists(FlowSummaryImpl::Public::SummarizedCallable sc |
sc.asCallable() = this and sc.hasManualModel()
)
}
/** Holds if this API has a manual neutral model. */
private predicate hasManualNeutral() {
this.(FlowSummaryImpl::Public::NeutralCallable).hasManualModel()
/** Holds if this API has a manual neutral summary model. */
private predicate hasManualNeutralSummary() {
this.(FlowSummaryImpl::Public::NeutralSummaryCallable).hasManualModel()
}
/** Holds if this API has a manual MaD model. */
predicate hasManualMadModel() { this.hasManualSummary() or this.hasManualNeutral() }
predicate hasManualMadModel() { this.hasManualSummary() or this.hasManualNeutralSummary() }
/*
* Note: the following top JDK APIs are not modeled with MaD:
* `java.lang.Runnable#run()`: specialised lambda flow

View File

@@ -79,7 +79,7 @@ class ExternalApi extends Callable {
/** Holds if this API is a known neutral. */
pragma[nomagic]
predicate isNeutral() { this = any(FlowSummaryImpl::Public::NeutralCallable nsc).asCallable() }
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
/**
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a

View File

@@ -11,7 +11,7 @@ where
// top jdk api names for which there isn't a manual model
exists(TopJdkApi topApi |
not topApi.hasManualMadModel() and
hasApiName(topApi.asCallable(), apiName) and
hasApiName(topApi, apiName) and
message = "no manual model"
)
select apiName, message order by apiName

View File

@@ -1,5 +1,6 @@
| java.io.File#File(String) | 2 |
| java.net.URL#URL(String) | 2 |
| java.io.File#compareTo(File) | 1 |
| java.io.FileWriter#FileWriter(File) | 1 |
| java.lang.StringBuilder#append(String) | 1 |
| java.lang.StringBuilder#toString() | 1 |

View File

@@ -17,7 +17,7 @@ class SupportedExternalApis {
map.put("foo", new Object()); // supported summary
map.entrySet().iterator().next().getKey(); // nested class (Map.Entry), supported summaries (entrySet, iterator, next, getKey)
Duration d = java.time.Duration.ofMillis(1000); // supported neutral
Duration d = java.time.Duration.ofMillis(1000); // supported neutral summary
URL github = new URL("https://www.github.com/"); // supported summary
InputStream stream = github.openConnection().getInputStream(); // supported source (getInputStream), supported sink (openConnection)
@@ -25,6 +25,9 @@ class SupportedExternalApis {
new FileWriter(new File("foo")); // supported sink (FileWriter), supported summary (File)
new URL("http://foo").openStream(); // supported sink (openStream), supported summary (URL)
FileUtils.deleteDirectory(new File("foo")); // supported neutral (deleteDirectory), supported summary (File)
File file = new File("foo"); // supported summary (File)
FileUtils.deleteDirectory(file); // supported neutral summary (deleteDirectory)
file.compareTo(file); // supported neutral sink (compareTo)
}
}

View File

@@ -11,15 +11,15 @@ class ExternalApiUsage {
Map<String, Object> map = new HashMap<>();
map.put("foo", new Object());
Duration d = java.time.Duration.ofMillis(1000); // supported as a neutral model
Duration d = java.time.Duration.ofMillis(1000); // supported as a neutral summary model
long l = "foo".length(); // supported as a neutral model
long l = "foo".length(); // supported as a neutral summary model
AtomicReference<String> ref = new AtomicReference<>(); // uninteresting (parameterless constructor)
ref.set("foo"); // supported as a summary model
ref.toString(); // not supported
String.class.isAssignableFrom(Object.class); // parameter with generic type, supported as a neutral model
String.class.isAssignableFrom(Object.class); // parameter with generic type, supported as a neutral summary model
System.out.println(d);
System.out.println(map);