Make tags for positive and negative examples more precise.

This commit is contained in:
Max Schaefer
2024-01-17 15:57:27 +00:00
parent ae23920a6d
commit 7bc03040ec
5 changed files with 41 additions and 28 deletions

View File

@@ -17,8 +17,8 @@ class Test {
public static void main(String[] args) throws Exception {
AtomicReference<String> reference = new AtomicReference<>(); // uninteresting (parameterless constructor)
reference.set( // $ sinkModel=set(Object):Argument[this]
args[0] // $ negativeExample=set(Object):Argument[0] // modeled as a flow step
); // $ negativeExample=set(Object):ReturnValue // return type is void
args[0] // $ negativeSinkExample=set(Object):Argument[0] // modeled as a flow step
); // $ negativeSourceExample=set(Object):ReturnValue // return type is void
}
public static void callSupplier(Supplier<String> supplier) {
@@ -27,35 +27,35 @@ class Test {
public static void copyFiles(Path source, Path target, CopyOption option) throws Exception {
Files.copy(
source, // $ positiveExample=copy(Path,Path,CopyOption[]):Argument[0](path-injection)
target, // $ positiveExample=copy(Path,Path,CopyOption[]):Argument[1](path-injection)
source, // $ positiveSinkExample=copy(Path,Path,CopyOption[]):Argument[0](path-injection)
target, // $ positiveSinkExample=copy(Path,Path,CopyOption[]):Argument[1](path-injection)
option // no candidate (not modeled, but source and target are modeled)
); // $ sourceModel=copy(Path,Path,CopyOption[]):ReturnValue
}
public static InputStream getInputStream(Path openPath) throws Exception {
return Files.newInputStream(
openPath // $ sinkModel=newInputStream(Path,OpenOption[]):Argument[0] positiveExample=newInputStream(Path,OpenOption[]):Argument[0](path-injection) // sink candidate because "only" ai-modeled, and useful as a candidate in regression testing
openPath // $ sinkModel=newInputStream(Path,OpenOption[]):Argument[0] positiveSinkExample=newInputStream(Path,OpenOption[]):Argument[0](path-injection) // sink candidate because "only" ai-modeled, and useful as a candidate in regression testing
); // $ sourceModel=newInputStream(Path,OpenOption[]):ReturnValue
}
public static InputStream getInputStream(String openPath) throws Exception {
return Test.getInputStream( // the call is not a source candidate (argument to local call)
Paths.get(
openPath // $ negativeExample=get(String,String[]):Argument[0] // modeled as a flow step
openPath // $ negativeSinkExample=get(String,String[]):Argument[0] // modeled as a flow step
) // $ sourceModel=get(String,String[]):ReturnValue
);
}
public static int compareFiles(File f1, File f2) {
return f1.compareTo( // $ negativeExample=compareTo(File):Argument[this]
f2 // $ negativeExample=compareTo(File):Argument[0] // modeled as not a sink
); // $ negativeExample=compareTo(File):ReturnValue // return type is int
return f1.compareTo( // $ negativeSinkExample=compareTo(File):Argument[this]
f2 // $ negativeSinkExample=compareTo(File):Argument[0] // modeled as not a sink
); // $ negativeSourceExample=compareTo(File):ReturnValue // return type is int
}
public static void FilesWalkExample(Path p, FileVisitOption o) throws Exception {
Files.walk(
p, // $ negativeExample=walk(Path,FileVisitOption[]):Argument[0] // modeled as a flow step
p, // $ negativeSinkExample=walk(Path,FileVisitOption[]):Argument[0] // modeled as a flow step
o, // the implicit varargs array is a candidate, annotated on the last line of the call
o // not a candidate (only the first arg corresponding to a varargs array
// is extracted)
@@ -63,7 +63,7 @@ class Test {
}
public static void WebSocketExample(URLConnection c) throws Exception {
c.getInputStream(); // $ sinkModel=getInputStream():Argument[this] positiveExample=getInputStream():ReturnValue(remote) // not a source candidate (manual modeling)
c.getInputStream(); // $ sinkModel=getInputStream():Argument[this] positiveSourceExample=getInputStream():ReturnValue(remote) // not a source candidate (manual modeling)
}
}
@@ -88,16 +88,16 @@ class MoreTests {
public static void FilesListExample(Path p) throws Exception {
Files.list(
Files.createDirectories(
p // $ positiveExample=createDirectories(Path,FileAttribute[]):Argument[0](path-injection)
) // $ sourceModel=createDirectories(Path,FileAttribute[]):ReturnValue negativeExample=list(Path):Argument[0] // modeled as a flow step
p // $ positiveSinkExample=createDirectories(Path,FileAttribute[]):Argument[0](path-injection)
) // $ sourceModel=createDirectories(Path,FileAttribute[]):ReturnValue negativeSinkExample=list(Path):Argument[0] // modeled as a flow step
); // $ sourceModel=list(Path):ReturnValue
Files.delete(
p // $ sinkModel=delete(Path):Argument[0] positiveExample=delete(Path):Argument[0](path-injection)
); // $ negativeExample=delete(Path):ReturnValue // return type is void
p // $ sinkModel=delete(Path):Argument[0] positiveSinkExample=delete(Path):Argument[0](path-injection)
); // $ negativeSourceExample=delete(Path):ReturnValue // return type is void
Files.deleteIfExists(
p // $ sinkModel=deleteIfExists(Path):Argument[0] positiveExample=deleteIfExists(Path):Argument[0](path-injection)
); // $ negativeExample=deleteIfExists(Path):ReturnValue // return type is boolean
p // $ sinkModel=deleteIfExists(Path):Argument[0] positiveSinkExample=deleteIfExists(Path):Argument[0](path-injection)
); // $ negativeSourceExample=deleteIfExists(Path):ReturnValue // return type is boolean
}
}

View File

@@ -23,7 +23,22 @@ signature module TestHelperSig<CandidateSig Candidate> {
module Extraction<CandidateSig Candidate, TestHelperSig<Candidate> TestHelper> implements TestSig {
string getARelevantTag() {
result in ["sourceModel", "sinkModel", "positiveExample", "negativeExample"]
result in [
"sourceModel", "sinkModel", // a candidate source/sink
"positiveSourceExample", "positiveSinkExample", // a known source/sink
"negativeSourceExample", "negativeSinkExample" // a known non-source/non-sink
]
}
/**
* If `extensibleType` is `sourceModel` then the result is `ifSource`, if it
* is `sinkModel` then the result is `ifSink`.
*/
bindingset[extensibleType, ifSource, ifSink]
private string ifSource(string extensibleType, string ifSource, string ifSink) {
extensibleType = "sourceModel" and result = ifSource
or
extensibleType = "sinkModel" and result = ifSink
}
additional predicate selectEndpoint(
@@ -35,13 +50,13 @@ module Extraction<CandidateSig Candidate, TestHelperSig<Candidate> TestHelper> i
suffix = ""
or
TestHelper::isNegativeExample(endpoint, name, signature, input, output, extensibleType) and
tag = "negativeExample" and
tag = "negative" + ifSource(extensibleType, "Source", "Sink") + "Example" and
suffix = ""
or
exists(string endpointType |
TestHelper::isPositiveExample(endpoint, endpointType, name, signature, input, output,
extensibleType) and
tag = "positiveExample" and
tag = "positive" + ifSource(extensibleType, "Source", "Sink") + "Example" and
suffix = "(" + endpointType + ")"
)
}
@@ -56,9 +71,7 @@ module Extraction<CandidateSig Candidate, TestHelperSig<Candidate> TestHelper> i
TestHelper::getEndpointLocation(endpoint) = location and
endpoint.toString() = element and
// for source models only the output is relevant, and vice versa for sink models
if extensibleType = "sourceModel"
then value = name + signature + ":" + output + suffix
else value = name + signature + ":" + input + suffix
value = name + signature + ":" + ifSource(extensibleType, output, input) + suffix
)
}
}

View File

@@ -21,7 +21,7 @@ public class PublicClass {
}
// `input` and `input` are source candidates, but not sink candidates (is-style method)
public Boolean isIgnored(Object input) { // $ negativeExample=isIgnored(Object):Argument[this] sourceModel=isIgnored(Object):Parameter[this] negativeExample=isIgnored(Object):Argument[0] sourceModel=isIgnored(Object):Parameter[0]
public Boolean isIgnored(Object input) { // $ negativeSinkExample=isIgnored(Object):Argument[this] sourceModel=isIgnored(Object):Parameter[this] negativeSinkExample=isIgnored(Object):Argument[0] sourceModel=isIgnored(Object):Parameter[0]
return false;
}
}

View File

@@ -1,8 +1,8 @@
package java.io;
public class File {
public int compareTo( // $ negativeExample=compareTo(File):Argument[this] negativeExample=compareTo(File):Parameter[this] // modeled as neutral
File pathname // $ negativeExample=compareTo(File):Argument[0] negativeExample=compareTo(File):Parameter[0] // modeled as neutral
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
) {
return 0;
}

View File

@@ -10,7 +10,7 @@ import java.nio.file.OpenOption;
public class Files {
public static void copy( // method result is not a candidate source (void)
Path source, // $ positiveExample=copy(Path,OutputStream):Argument[0](path-injection) // manual model exists
Path source, // $ positiveSinkExample=copy(Path,OutputStream):Argument[0](path-injection) // manual model exists
OutputStream out // $ sinkModel=copy(Path,OutputStream):Argument[1]
/* NB: may be worthwhile to implement the
same behavior as in application mode where out would not be a
@@ -23,7 +23,7 @@ public class Files {
}
public static InputStream newInputStream( // $ sourceModel=newInputStream(Path,OpenOption[]):ReturnValue
Path openPath, // $ positiveExample=newInputStream(Path,OpenOption[]):Argument[0](path-injection) sinkModel=newInputStream(Path,OpenOption[]):Argument[0] // known sink, but still a candidate (ai-modeled, and useful as a candidate in regression testing)
Path openPath, // $ positiveSinkExample=newInputStream(Path,OpenOption[]):Argument[0](path-injection) sinkModel=newInputStream(Path,OpenOption[]):Argument[0] // known sink, but still a candidate (ai-modeled, and useful as a candidate in regression testing)
OpenOption... options // $ sinkModel=newInputStream(Path,OpenOption[]):Argument[1]
) throws IOException {
return new FileInputStream(openPath.toFile());