diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java index 971d0d44b..94d2c20a1 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/CheckerBaseClass.java @@ -27,6 +27,7 @@ import edu.ucr.cs.riple.core.Config; import edu.ucr.cs.riple.core.Context; import edu.ucr.cs.riple.core.checkers.nullaway.NullAway; +import edu.ucr.cs.riple.core.checkers.ucrtaint.UCRTaint; import edu.ucr.cs.riple.core.metadata.index.Error; import edu.ucr.cs.riple.core.module.ModuleInfo; import edu.ucr.cs.riple.injector.location.OnField; @@ -75,6 +76,8 @@ public static Checker getCheckerByName(String name, Context context) { switch (name) { case NullAway.NAME: return new NullAway(context); + case UCRTaint.NAME: + return new UCRTaint(context); default: throw new RuntimeException("Unknown checker name: " + name); } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java new file mode 100644 index 000000000..8e5433606 --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaint.java @@ -0,0 +1,135 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package edu.ucr.cs.riple.core.checkers.ucrtaint; + +import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.Context; +import edu.ucr.cs.riple.core.checkers.CheckerBaseClass; +import edu.ucr.cs.riple.core.injectors.AnnotationInjector; +import edu.ucr.cs.riple.core.metadata.index.Fix; +import edu.ucr.cs.riple.core.metadata.region.Region; +import edu.ucr.cs.riple.core.module.ModuleConfiguration; +import edu.ucr.cs.riple.core.module.ModuleInfo; +import edu.ucr.cs.riple.injector.changes.AddTypeUseMarkerAnnotation; +import edu.ucr.cs.riple.injector.location.Location; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +/** + * Represents UCRTaint checker in + * Annotator. + */ +public class UCRTaint extends CheckerBaseClass { + + /** The name of the checker. This is used to identify the checker in the configuration file. */ + public static final String NAME = "UCRTaint"; + + public UCRTaint(Context context) { + super(context); + } + + @Override + public void preprocess(AnnotationInjector injector) {} + + @Override + public Set deserializeErrors(ModuleInfo module) { + ImmutableSet paths = + module.getModuleConfiguration().stream() + .map(moduleInfo -> moduleInfo.dir.resolve("errors.json")) + .collect(ImmutableSet.toImmutableSet()); + Set errors = new HashSet<>(); + paths.forEach( + path -> { + try { + String content = Files.readString(path, Charset.defaultCharset()); + content = "{ \"errors\": [" + content.substring(0, content.length() - 1) + "]}"; + JSONObject jsonObject = (JSONObject) new JSONParser().parse(content); + JSONArray errorsJson = (JSONArray) jsonObject.get("errors"); + errorsJson.forEach(o -> errors.add(deserializeErrorFromJSON((JSONObject) o, module))); + } catch (IOException | ParseException e) { + throw new RuntimeException(e); + } + }); + return errors; + } + + private UCRTaintError deserializeErrorFromJSON(JSONObject errorsJson, ModuleInfo moduleInfo) { + String errorType = (String) errorsJson.get("messageKey"); + int offset = ((Long) errorsJson.get("offset")).intValue(); + Region region = + new Region( + (String) ((JSONObject) errorsJson.get("region")).get("member"), + (String) ((JSONObject) errorsJson.get("region")).get("class")); + ImmutableSet.Builder builder = ImmutableSet.builder(); + ((JSONArray) errorsJson.get("fixes")) + .forEach( + o -> { + JSONObject fixJson = (JSONObject) o; + Location location = + Location.createLocationFromJSON((JSONObject) fixJson.get("location")); + location.ifField(onField -> extendVariableList(onField, moduleInfo)); + builder.add( + new Fix( + new AddTypeUseMarkerAnnotation( + location, "edu.ucr.cs.riple.taint.ucrtainting.qual.RUntainted"), + errorType, + true)); + }); + return new UCRTaintError(errorType, "", region, offset, builder.build()); + } + + @Override + public void suppressRemainingErrors(AnnotationInjector injector) { + throw new RuntimeException( + "Suppression for remaining errors is not supported for " + NAME + "yet!"); + } + + @Override + public void verifyCheckerCompatibility() {} + + @Override + public void prepareConfigFilesForBuild(ImmutableSet configurations) { + // TODO: implement this once configuration on UCRTaint is finalized. + } + + @Override + public UCRTaintError createError( + String errorType, + String errorMessage, + Region region, + int offset, + ImmutableSet resolvingFixes, + ModuleInfo moduleInfo) { + return new UCRTaintError(errorType, errorMessage, region, offset, resolvingFixes); + } +} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaintError.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaintError.java new file mode 100644 index 000000000..1b05b3031 --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/checkers/ucrtaint/UCRTaintError.java @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package edu.ucr.cs.riple.core.checkers.ucrtaint; + +import com.google.common.collect.ImmutableSet; +import edu.ucr.cs.riple.core.metadata.index.Error; +import edu.ucr.cs.riple.core.metadata.index.Fix; +import edu.ucr.cs.riple.core.metadata.region.Region; + +/** Represents an error reported by {@link UCRTaint}. */ +public class UCRTaintError extends Error { + + public UCRTaintError( + String messageType, + String message, + Region region, + int offset, + ImmutableSet resolvingFixes) { + super(messageType, message, region, offset, resolvingFixes); + } +} diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/region/CompoundRegionRegistry.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/region/CompoundRegionRegistry.java index 8da3412aa..f63c46fbd 100644 --- a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/region/CompoundRegionRegistry.java +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/region/CompoundRegionRegistry.java @@ -56,6 +56,7 @@ public CompoundRegionRegistry(ModuleInfo moduleInfo) { ImmutableSet.of( new FieldRegionRegistry(moduleInfo), methodRegionRegistry, + new LocalVariableRegionRegistry(), new ParameterRegionRegistry(moduleInfo, methodRegionRegistry)); } diff --git a/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/region/LocalVariableRegionRegistry.java b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/region/LocalVariableRegionRegistry.java new file mode 100644 index 000000000..e941d6d87 --- /dev/null +++ b/annotator-core/src/main/java/edu/ucr/cs/riple/core/metadata/region/LocalVariableRegionRegistry.java @@ -0,0 +1,42 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nima Karimipour + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package edu.ucr.cs.riple.core.metadata.region; + +import edu.ucr.cs.riple.injector.location.Location; +import edu.ucr.cs.riple.injector.location.OnLocalVariable; +import java.util.Optional; +import java.util.Set; + +/** Region Registry for Local variables. */ +public class LocalVariableRegionRegistry implements RegionRegistry { + @Override + public Optional> getImpactedRegions(Location location) { + if (!location.isOnLocalVariable()) { + return Optional.empty(); + } + OnLocalVariable localVariable = location.toLocalVariable(); + return Optional.of(Set.of(new Region(localVariable.clazz, localVariable.encMethod.method))); + } +} diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/Location.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/Location.java index a32c7ef81..add5b665b 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/Location.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/Location.java @@ -28,10 +28,12 @@ import com.google.common.collect.Sets; import edu.ucr.cs.riple.injector.Helper; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Objects; import java.util.function.Consumer; import javax.annotation.Nullable; +import org.json.simple.JSONObject; /** Represents a location of an element in the source code. */ public abstract class Location { @@ -57,6 +59,19 @@ public Location(LocationKind type, Path path, String clazz) { this.path = path; } + /** + * Creates an instance of {@link Location} for a given type, and retrieves path and class values + * from the given JSON object. + * + * @param type The type of the location. + * @param json The JSON object containing the path and class values. + */ + public Location(LocationKind type, JSONObject json) { + this.type = type; + this.clazz = (String) json.get("class"); + this.path = Paths.get((String) json.get("path")); + } + /** * Creates an instance of {@link Location} based on values written in a row of a TSV file. These * values should be in order of: @@ -98,6 +113,24 @@ public static Location createLocationFromArrayInfo(String[] values) { throw new RuntimeException("Cannot reach this statement, values: " + Arrays.toString(values)); } + public static Location createLocationFromJSON(JSONObject json) { + String kind = (String) json.get("kind"); + switch (kind) { + case "METHOD": + return new OnMethod(json); + case "FIELD": + return new OnField(json); + case "PARAMETER": + return new OnParameter(json); + case "LOCAL_VARIABLE": + return new OnLocalVariable(json); + case "CLASS": + return new OnClass(json); + default: + throw new RuntimeException("Cannot reach this statement, kind: " + kind); + } + } + /** * If this location is of kind {@link LocationKind#METHOD}, calls the consumer on the location. * diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnClass.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnClass.java index e5cffb497..0ed276afa 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnClass.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnClass.java @@ -27,6 +27,7 @@ import edu.ucr.cs.riple.injector.Helper; import java.nio.file.Path; import java.util.regex.Pattern; +import org.json.simple.JSONObject; /** Represents a location for class element. This location is used to apply changes to a class. */ public class OnClass extends Location { @@ -45,6 +46,10 @@ public OnClass(String path, String clazz) { this(Helper.deserializePath(path), clazz); } + public OnClass(JSONObject json) { + super(LocationKind.CLASS, json); + } + /** * Checks if flat name is for an anonymous class. * diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnField.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnField.java index 9331cfddc..1a87d3e7f 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnField.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnField.java @@ -27,9 +27,11 @@ import edu.ucr.cs.riple.injector.Helper; import java.nio.file.Path; import java.util.Collections; +import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.function.Consumer; +import org.json.simple.JSONObject; /** * Represents a location for field element. This location is used to apply changes to a class field. @@ -59,6 +61,11 @@ public OnField(String path, String clazz, Set variables) { this(Helper.deserializePath(path), clazz, variables); } + public OnField(JSONObject json) { + super(LocationKind.FIELD, json); + this.variables = new HashSet<>(Collections.singletonList((String) json.get("variables"))); + } + @Override public void ifField(Consumer consumer) { consumer.accept(this); diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java index 8de77c97f..3a8fac47b 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnLocalVariable.java @@ -28,6 +28,7 @@ import java.nio.file.Path; import java.util.Objects; import java.util.function.Consumer; +import org.json.simple.JSONObject; /** * Represents a location for local variable element. This location is used to apply changes to a @@ -51,6 +52,12 @@ public OnLocalVariable(String path, String clazz, String encMethod, String varNa this(Helper.deserializePath(path), clazz, encMethod, varName); } + public OnLocalVariable(JSONObject json) { + super(LocationKind.LOCAL_VARIABLE, json); + this.encMethod = new OnMethod(json); + this.varName = (String) json.get("varName"); + } + @Override public void ifLocalVariable(Consumer consumer) { consumer.accept(this); diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnMethod.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnMethod.java index f3c2c1cfc..d25ba12e5 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnMethod.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnMethod.java @@ -30,6 +30,7 @@ import java.nio.file.Path; import java.util.Objects; import java.util.function.Consumer; +import org.json.simple.JSONObject; /** Represents a location for method element. This location is used to apply changes to a method. */ public class OnMethod extends Location { @@ -52,6 +53,12 @@ public OnMethod(String path, String clazz, String method) { this(Helper.deserializePath(path), clazz, method); } + public OnMethod(JSONObject json) { + super(LocationKind.METHOD, json); + this.method = (String) json.get("method"); + this.matcher = new SignatureMatcher(method); + } + /** * Checks if the given method matches the method signature of this location. * diff --git a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnParameter.java b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnParameter.java index 4719c12a0..e52c87545 100644 --- a/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnParameter.java +++ b/injector/src/main/java/edu/ucr/cs/riple/injector/location/OnParameter.java @@ -28,6 +28,7 @@ import java.nio.file.Path; import java.util.Objects; import java.util.function.Consumer; +import org.json.simple.JSONObject; /** * Represents a location for parameter element. This location is used to apply changes to a @@ -50,6 +51,12 @@ public OnParameter(String path, String clazz, String method, int index) { this(Helper.deserializePath(path), clazz, method, index); } + public OnParameter(JSONObject json) { + super(LocationKind.PARAMETER, json); + this.enclosingMethod = new OnMethod(json); + this.index = ((Long) json.get("index")).intValue(); + } + @Override public void ifParameter(Consumer consumer) { consumer.accept(this);