io.codemodder.remediation.headerinjection.DefaultHeaderInjectionRemediator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of codemodder-base Show documentation
Show all versions of codemodder-base Show documentation
Base framework for writing codemods in Java
package io.codemodder.remediation.headerinjection;
import static io.codemodder.javaparser.JavaParserTransformer.wrap;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import io.codemodder.CodemodChange;
import io.codemodder.CodemodFileScanningResult;
import io.codemodder.codetf.DetectorRule;
import io.codemodder.codetf.FixedFinding;
import io.codemodder.remediation.LegacyFixCandidate;
import io.codemodder.remediation.LegacyFixCandidateSearchResults;
import io.codemodder.remediation.LegacyFixCandidateSearcher;
import io.codemodder.remediation.MethodOrConstructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
final class DefaultHeaderInjectionRemediator implements HeaderInjectionRemediator {
private static final Set setHeaderNames = Set.of("setHeader", "addHeader");
private static final String validatorMethodName = "stripNewlines";
private final String fixMethodCode;
DefaultHeaderInjectionRemediator() {
this.fixMethodCode =
"""
private static String stripNewlines(final String s) {
return s.replaceAll("[\\n\\r]", "");
}
""";
}
@Override
public CodemodFileScanningResult remediateAll(
final CompilationUnit cu,
final String path,
final DetectorRule detectorRule,
final List issuesForFile,
final Function getKey,
final Function getStartLine,
final Function getEndLine,
final Function getStartColumn) {
LegacyFixCandidateSearcher searcher =
new LegacyFixCandidateSearcher.Builder()
.withMatcher(mce -> mce.isMethodCallWithNameIn(setHeaderNames))
.withMatcher(MethodOrConstructor::isMethodCallWithScope)
.withMatcher(mce -> mce.getArguments().size() == 2)
.withMatcher(mce -> !(mce.getArguments().get(1) instanceof StringLiteralExpr))
.build();
LegacyFixCandidateSearchResults results =
searcher.search(
cu,
path,
detectorRule,
issuesForFile,
getKey,
getStartLine,
getEndLine,
getStartColumn);
List changes = new ArrayList<>();
for (LegacyFixCandidate legacyFixCandidate : results.fixCandidates()) {
List issues = legacyFixCandidate.issues();
MethodCallExpr setHeaderCall = legacyFixCandidate.call().asMethodCall();
Expression headerValueArgument = setHeaderCall.getArgument(1);
wrap(headerValueArgument).withScopelessMethod(validatorMethodName);
// add the validation method if it's not already present
ClassOrInterfaceDeclaration parentClass =
setHeaderCall.findAncestor(ClassOrInterfaceDeclaration.class).get();
if (parentClass.isInterface()) {
MethodCallExpr inlinedStripCall =
new MethodCallExpr(
headerValueArgument,
"replaceAll",
NodeList.nodeList(new StringLiteralExpr("[\\n\\r]"), new StringLiteralExpr("")));
setHeaderCall.getArguments().set(1, inlinedStripCall);
} else {
boolean alreadyHasResourceValidationCallPresent =
parentClass.findAll(MethodDeclaration.class).stream()
.anyMatch(
md ->
md.getNameAsString().equals(validatorMethodName)
&& md.getParameters().size() == 1
&& md.getParameters().get(0).getTypeAsString().equals("String"));
if (!alreadyHasResourceValidationCallPresent) {
// one might be tempted to cache this result, but then it will be a shared resource with
// shared CST metadata and cause bugs
MethodDeclaration fixMethod = StaticJavaParser.parseMethodDeclaration(fixMethodCode);
// add the method to the class
parentClass.addMember(fixMethod);
}
}
// all the line numbers should be the same, so we just grab the first one
int line = getStartLine.apply(legacyFixCandidate.issues().get(0));
List fixedFindings =
issues.stream()
.map(issue -> new FixedFinding(getKey.apply(issue), detectorRule))
.toList();
changes.add(CodemodChange.from(line, List.of(), fixedFindings));
}
return CodemodFileScanningResult.from(changes, results.unfixableFindings());
}
}