All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.codemodder.remediation.headerinjection.DefaultHeaderInjectionRemediator Maven / Gradle / Ivy

There is a newer version: 0.97.9
Show newest version
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());
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy