Merge pull request #15554 from github/max-schaefer/automodel-candidate-fixes

Automodel: Improve handling of varargs and overriding in extraction queries
This commit is contained in:
Max Schaefer
2024-02-16 08:51:54 +00:00
committed by GitHub
5 changed files with 117 additions and 50 deletions

View File

@@ -35,14 +35,10 @@ newtype TApplicationModeEndpoint =
arg = DataFlow::getInstanceArgument(call) and
not call instanceof ConstructorCall
} or
TImplicitVarargsArray(Call call, DataFlow::Node arg, int idx) {
TImplicitVarargsArray(Call call, DataFlow::ImplicitVarargsArray arg, int idx) {
AutomodelJavaUtil::isFromSource(call) and
exists(Argument argExpr |
arg.asExpr() = argExpr and
call.getArgument(idx) = argExpr and
argExpr.isVararg() and
not exists(int i | i < idx and call.getArgument(i).(Argument).isVararg())
)
call = arg.getCall() and
idx = call.getCallee().getVaragsParameterIndex()
} or
TMethodReturnValue(Call call) {
AutomodelJavaUtil::isFromSource(call) and
@@ -255,45 +251,74 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2;
predicate isSink(Endpoint e, string kind, string provenance) {
exists(string package, string type, string name, string signature, string ext, string input |
sinkSpec(e, package, type, name, signature, ext, input) and
ExternalFlow::sinkModel(package, type, _, name, [signature, ""], ext, input, kind, provenance)
exists(
string package, string type, boolean subtypes, string name, string signature, string ext,
string input
|
sinkSpec(e, package, type, subtypes, name, signature, ext, input) and
ExternalFlow::sinkModel(package, type, subtypes, name, [signature, ""], ext, input, kind,
provenance)
)
or
isCustomSink(e, kind) and provenance = "custom-sink"
}
predicate isSource(Endpoint e, string kind, string provenance) {
exists(string package, string type, string name, string signature, string ext, string output |
sourceSpec(e, package, type, name, signature, ext, output) and
ExternalFlow::sourceModel(package, type, _, name, [signature, ""], ext, output, kind,
exists(
string package, string type, boolean subtypes, string name, string signature, string ext,
string output
|
sourceSpec(e, package, type, subtypes, name, signature, ext, output) and
ExternalFlow::sourceModel(package, type, subtypes, name, [signature, ""], ext, output, kind,
provenance)
)
}
predicate isNeutral(Endpoint e) {
exists(string package, string type, string name, string signature |
sinkSpec(e, package, type, name, signature, _, _) and
ExternalFlow::neutralModel(package, type, name, [signature, ""], "sink", _)
exists(string package, string type, string name, string signature, string endpointType |
sinkSpec(e, package, type, _, name, signature, _, _) and
endpointType = "sink"
or
sourceSpec(e, package, type, _, name, signature, _, _) and
endpointType = "source"
|
ExternalFlow::neutralModel(package, type, name, [signature, ""], endpointType, _)
)
}
// XXX how to extend to support sources?
additional predicate sinkSpec(
Endpoint e, string package, string type, string name, string signature, string ext, string input
/**
* Holds if the endpoint concerns a callable with the given package, type, name and signature.
*
* If `subtypes` is `false`, only the exact callable is considered. If `true`, the callable and
* all its overrides are considered.
*/
additional predicate endpointCallable(
Endpoint e, string package, string type, boolean subtypes, string name, string signature
) {
e.getCallable().hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(e.getCallable()) and
exists(Callable c |
c = e.getCallable() and subtypes in [true, false]
or
e.getCallable().(Method).getSourceDeclaration().overrides+(c) and subtypes = true
|
c.hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(c)
)
}
additional predicate sinkSpec(
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
string ext, string input
) {
endpointCallable(e, package, type, subtypes, name, signature) and
ext = "" and
input = e.getMaDInput()
}
additional predicate sourceSpec(
Endpoint e, string package, string type, string name, string signature, string ext,
string output
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
string ext, string output
) {
e.getCallable().hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(e.getCallable()) and
endpointCallable(e, package, type, subtypes, name, signature) and
ext = "" and
output = e.getMaDOutput()
}

View File

@@ -209,46 +209,72 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2;
predicate isSink(Endpoint e, string kind, string provenance) {
exists(string package, string type, string name, string signature, string ext, string input |
sinkSpec(e, package, type, name, signature, ext, input) and
ExternalFlow::sinkModel(package, type, _, name, [signature, ""], ext, input, kind, provenance)
exists(
string package, string type, boolean subtypes, string name, string signature, string ext,
string input
|
sinkSpec(e, package, type, subtypes, name, signature, ext, input) and
ExternalFlow::sinkModel(package, type, subtypes, name, [signature, ""], ext, input, kind,
provenance)
)
}
predicate isSource(Endpoint e, string kind, string provenance) {
exists(string package, string type, string name, string signature, string ext, string output |
sourceSpec(e, package, type, name, signature, ext, output) and
ExternalFlow::sourceModel(package, type, _, name, [signature, ""], ext, output, kind,
exists(
string package, string type, boolean subtypes, string name, string signature, string ext,
string output
|
sourceSpec(e, package, type, subtypes, name, signature, ext, output) and
ExternalFlow::sourceModel(package, type, subtypes, name, [signature, ""], ext, output, kind,
provenance)
)
}
predicate isNeutral(Endpoint e) {
exists(string package, string type, string name, string signature |
(
sinkSpec(e, package, type, name, signature, _, _)
or
sourceSpec(e, package, type, name, signature, _, _)
) and
ExternalFlow::neutralModel(package, type, name, [signature, ""], "sink", _)
exists(string package, string type, string name, string signature, string endpointType |
sinkSpec(e, package, type, _, name, signature, _, _) and
endpointType = "sink"
or
sourceSpec(e, package, type, _, name, signature, _, _) and
endpointType = "source"
|
ExternalFlow::neutralModel(package, type, name, [signature, ""], endpointType, _)
)
}
/**
* Holds if the endpoint concerns a callable with the given package, type, name and signature.
*
* If `subtypes` is `false`, only the exact callable is considered. If `true`, the callable and
* all its overrides are considered.
*/
additional predicate endpointCallable(
Endpoint e, string package, string type, boolean subtypes, string name, string signature
) {
exists(Callable c |
c = e.getCallable() and subtypes in [true, false]
or
e.getCallable().(Method).getSourceDeclaration().overrides+(c) and subtypes = true
|
c.hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(c)
)
}
additional predicate sinkSpec(
Endpoint e, string package, string type, string name, string signature, string ext, string input
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
string ext, string input
) {
e.getCallable().hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(e.getCallable()) and
endpointCallable(e, package, type, subtypes, name, signature) and
ext = "" and
input = e.getMaDInput()
}
additional predicate sourceSpec(
Endpoint e, string package, string type, string name, string signature, string ext,
string output
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
string ext, string output
) {
e.getCallable().hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(e.getCallable()) and
endpointCallable(e, package, type, subtypes, name, signature) and
ext = "" and
output = e.getMaDOutput()
}

View File

@@ -40,11 +40,12 @@ class Test {
); // $ sourceModelCandidate=newInputStream(Path,OpenOption[]):ReturnValue
}
public static InputStream getInputStream(String openPath) throws Exception {
public static InputStream getInputStream(String openPath, String otherPath) throws Exception {
return Test.getInputStream( // the call is not a source candidate (argument to local call)
Paths.get(
openPath // $ negativeSinkExample=get(String,String[]):Argument[0] // modeled as a flow step
) // $ sourceModelCandidate=get(String,String[]):ReturnValue
openPath, // $ negativeSinkExample=get(String,String[]):Argument[0] // modeled as a flow step
otherPath
) // $ sourceModelCandidate=get(String,String[]):ReturnValue negativeSinkExample=get(String,String[]):Argument[1]
);
}

View File

@@ -0,0 +1,15 @@
package com.github.codeql.test;
public class MyWriter extends java.io.Writer {
@Override
public void write(char[] cbuf, int off, int len) { // $ sinkModelCandidate=write(char[],int,int):Argument[this] sourceModelCandidate=write(char[],int,int):Parameter[this] sourceModelCandidate=write(char[],int,int):Parameter[0]
}
@Override
public void close() { // $ sinkModelCandidate=close():Argument[this] sourceModelCandidate=close():Parameter[this]
}
@Override
public void flush() { // $ sinkModelCandidate=flush():Argument[this] sourceModelCandidate=flush():Parameter[this]
}
}

View File

@@ -1,8 +1,8 @@
package java.io;
public class File {
public int compareTo( // $ negativeSinkExample=compareTo(File):Argument[this] negativeSourceExample=compareTo(File):Parameter[this] // modeled as neutral
File pathname // $ negativeSinkExample=compareTo(File):Argument[0] negativeSourceExample=compareTo(File):Parameter[0] // modeled as neutral
public int compareTo( // $ negativeSinkExample=compareTo(File):Argument[this] sourceModelCandidate=compareTo(File):Parameter[this] // modeled as neutral for sinks
File pathname // $ negativeSinkExample=compareTo(File):Argument[0] sourceModelCandidate=compareTo(File):Parameter[0] // modeled as neutral for sinks
) {
return 0;
}