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

io.codemodder.codemods.OptimizeJacksonStringUsageCodemod Maven / Gradle / Ivy

The newest version!
package io.codemodder.codemods;

import static io.codemodder.RegionNodeMatcher.EXACT_MATCH;
import static io.codemodder.Sarif.getLastDataFlowRegion;
import static io.codemodder.javaparser.ASTExpectations.expect;

import com.contrastsecurity.sarif.Region;
import com.contrastsecurity.sarif.Result;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import io.codemodder.*;
import io.codemodder.ast.ASTs;
import io.codemodder.javaparser.ChangesResult;
import io.codemodder.providers.sarif.semgrep.SemgrepScan;
import java.util.Optional;
import javax.inject.Inject;

@Codemod(
    id = "pixee:java/optimize-jackson-string-usage",
    importance = Importance.MEDIUM,
    reviewGuidance = ReviewGuidance.MERGE_AFTER_CURSORY_REVIEW)
public final class OptimizeJacksonStringUsageCodemod
    extends SarifPluginJavaParserChanger {

  @Inject
  public OptimizeJacksonStringUsageCodemod(
      @SemgrepScan(ruleId = "optimize-jackson-string-usage") RuleSarif semgrepSarif) {
    super(
        semgrepSarif,
        ExpressionStmt.class,
        SourceCodeRegionExtractor.FROM_SARIF_FIRST_THREADFLOW_EVENT);
  }

  /**
   * The rule pattern identifies any dataflow from IOUtils#toString() to ObjectMapper#readValue().
   * However, we can't hope to handle every instance of that pattern. This codemod will operate when
   * the given requirements hold:
   *
   * 
    *
  1. The IOUtils#toString() call is a simple variable declaration assignment. *
  2. The resulting String is used nowhere else besides the readValue() call. *
* * We've configured the {@link SourceCodeRegionExtractor} to pull the first data flow event, which * is the IOUtils#toString() call. */ @Override public ChangesResult onResultFound( final CodemodInvocationContext context, final CompilationUnit cu, final ExpressionStmt varDeclStmt, final Result result) { Optional toStringCall = expect(varDeclStmt) // make sure it's not in a try-with-resources, foreach declaration, etc .withBlockParent() // make sure it's a variable declaration statement .toBeVariableDeclarationStatement() // make sure it's local and not a multi-variable declaration .toBeSingleLocalVariableDefinition() // make sure its only used the one place we expect .withDirectReferenceCount(1) // make sure it's a method call initializer .toBeInitializedByMethodCall() .result(); if (toStringCall.isEmpty()) { return ChangesResult.noChanges; } String streamVariableName = toStringCall.get().getArgument(0).asNameExpr().getNameAsString(); Region lastRegion = getLastDataFlowRegion(result); Optional readValueCallOpt = ASTs.findMethodBodyFrom(varDeclStmt).get().findAll(ExpressionStmt.class).stream() .filter(stmt -> stmt.getRange().isPresent()) .filter( stmt -> EXACT_MATCH.matches( SourceCodeRegion.fromSarif(lastRegion), stmt.getRange().get())) .map(stmt -> stmt.getExpression().asVariableDeclarationExpr()) .map(vde -> vde.getVariable(0).getInitializer().get().asMethodCallExpr()) .findFirst(); // just for robustness, but should never happen if (readValueCallOpt.isEmpty()) { return ChangesResult.noChanges; } // All the checks have passed, we begin the fix MethodCallExpr readValueCall = readValueCallOpt.get(); // We've successfully located the readValue() call -- first we update the argument readValueCall.setArgument(0, new NameExpr(streamVariableName)); // now we remove the IOUtils#toString() assignment statement varDeclStmt.remove(); return ChangesResult.changesApplied; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy