-
Notifications
You must be signed in to change notification settings - Fork 7
Codemod design improvement #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,36 +1,40 @@ | ||
| package io.codemodder.codemods; | ||
|
|
||
| import com.contrastsecurity.sarif.Region; | ||
| import com.github.javaparser.ast.visitor.ModifierVisitor; | ||
| import io.codemodder.ChangeConstructorTypeVisitor; | ||
| import static io.codemodder.JavaParserUtils.addImportIfMissing; | ||
|
|
||
| import com.contrastsecurity.sarif.Result; | ||
| import com.github.javaparser.ast.CompilationUnit; | ||
| import com.github.javaparser.ast.expr.ObjectCreationExpr; | ||
| import io.codemodder.Codemod; | ||
| import io.codemodder.CodemodInvocationContext; | ||
| import io.codemodder.FileWeavingContext; | ||
| import io.codemodder.ReviewGuidance; | ||
| import io.codemodder.RuleSarif; | ||
| import io.codemodder.providers.sarif.semgrep.SemgrepJavaParserChanger; | ||
| import io.codemodder.providers.sarif.semgrep.SemgrepScan; | ||
| import java.util.List; | ||
| import java.security.SecureRandom; | ||
| import javax.inject.Inject; | ||
|
|
||
| /** Turns {@link java.util.Random} into {@link java.security.SecureRandom}. */ | ||
| @Codemod( | ||
| id = "pixee:java/secure-random", | ||
| author = "arshan@pixee.ai", | ||
| reviewGuidance = ReviewGuidance.MERGE_WITHOUT_REVIEW) | ||
| public final class SecureRandomCodemod extends SemgrepJavaParserChanger { | ||
| public final class SecureRandomCodemod extends SemgrepJavaParserChanger<ObjectCreationExpr> { | ||
|
|
||
| @Inject | ||
| public SecureRandomCodemod( | ||
| @SemgrepScan(pathToYaml = "/secure-random.yaml", ruleId = "secure-random") | ||
| final RuleSarif sarif) { | ||
| super(sarif); | ||
| super(sarif, ObjectCreationExpr.class); | ||
| } | ||
|
|
||
| @Override | ||
| public ModifierVisitor<FileWeavingContext> createVisitor( | ||
| final CodemodInvocationContext context, final List<Region> regions) { | ||
| return new ChangeConstructorTypeVisitor( | ||
| regions, "java.security.SecureRandom", context.codemodId()); | ||
| public void onSemgrepResultFound( | ||
| final CodemodInvocationContext context, | ||
| final CompilationUnit cu, | ||
| final ObjectCreationExpr objectCreationExpr, | ||
| final Result result) { | ||
| objectCreationExpr.setType("SecureRandom"); | ||
| addImportIfMissing(cu, SecureRandom.class.getName()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,10 @@ | ||
| package io.codemodder; | ||
|
|
||
| import com.github.javaparser.ast.visitor.ModifierVisitor; | ||
| import java.util.Optional; | ||
| import com.github.javaparser.ast.CompilationUnit; | ||
|
|
||
| /** {@inheritDoc} Uses JavaParser to change Java source files. */ | ||
| public interface JavaParserChanger extends Changer { | ||
|
|
||
| /** | ||
| * Creates a visitor for a given Java source file, or not. It's up to the implementing type to | ||
| * determine if and how source file should be changed. | ||
| */ | ||
| Optional<ModifierVisitor<FileWeavingContext>> createModifierVisitor( | ||
| CodemodInvocationContext context); | ||
| /** */ | ||
| void visit(final CodemodInvocationContext context, final CompilationUnit cu); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,40 +1,64 @@ | ||
| package io.codemodder.providers.sarif.semgrep; | ||
|
|
||
| import com.contrastsecurity.sarif.Region; | ||
| import com.github.javaparser.ast.visitor.ModifierVisitor; | ||
| import com.contrastsecurity.sarif.Result; | ||
| import com.github.javaparser.ast.CompilationUnit; | ||
| import com.github.javaparser.ast.Node; | ||
| import io.codemodder.CodemodInvocationContext; | ||
| import io.codemodder.FileWeavingContext; | ||
| import io.codemodder.JavaParserChanger; | ||
| import io.codemodder.JavaParserUtils; | ||
| import io.codemodder.RuleSarif; | ||
| import io.codemodder.Weave; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.Optional; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| /** Provides base functionality for making JavaParser-based changes with Semgrep. */ | ||
| public abstract class SemgrepJavaParserChanger implements JavaParserChanger { | ||
| public abstract class SemgrepJavaParserChanger<T extends Node> implements JavaParserChanger { | ||
|
|
||
| protected final RuleSarif sarif; | ||
| private final Class<? extends Node> nodeType; | ||
|
|
||
| protected SemgrepJavaParserChanger(final RuleSarif semgrepSarif) { | ||
| protected SemgrepJavaParserChanger( | ||
| final RuleSarif semgrepSarif, final Class<? extends Node> nodeType) { | ||
| this.sarif = Objects.requireNonNull(semgrepSarif); | ||
| this.nodeType = Objects.requireNonNull(nodeType); | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<ModifierVisitor<FileWeavingContext>> createModifierVisitor( | ||
| final CodemodInvocationContext context) { | ||
| List<Region> regions = sarif.getRegionsFromResultsByRule(context.path()); | ||
| return !regions.isEmpty() ? Optional.of(createVisitor(context, regions)) : Optional.empty(); | ||
| public final void visit(final CodemodInvocationContext context, final CompilationUnit cu) { | ||
|
|
||
| List<Result> results = sarif.getResultsByPath(context.path()); | ||
| List<? extends Node> allNodes = cu.findAll(nodeType); | ||
|
|
||
| for (Result result : results) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's fine this way but a small suggestion using streams: List<Region> regions = results.stream().map(r -> r.getLocations().get(0).getPhysicalLocation().getRegion()).collect(Collectors.toList())
var allMatchedNodes = allNodes.stream().filter(n -> regions.stream().anyMatch(r -> JavaParserUtils.regionMatchesNode(n, r))); Then iterate/filter/collect the resulting Stream |
||
| for (Node node : allNodes) { | ||
| Region region = result.getLocations().get(0).getPhysicalLocation().getRegion(); | ||
| if (!node.getClass().isAssignableFrom(nodeType)) { | ||
| logger.error("Unexpected node encountered in {}:{}", context.path(), region); | ||
| return; | ||
| } | ||
| if (JavaParserUtils.regionMatchesNodeStart(node, region)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may match undesired nodes since it only checks for overlapping. For example in Also you need to check if semgrep's region in the SARIF matches JavaParser ranges exactly. I know CodeQL does not (it includes an extra column). |
||
| onSemgrepResultFound(context, cu, (T) node, result); | ||
| FileWeavingContext changeRecorder = context.changeRecorder(); | ||
| changeRecorder.addWeave(Weave.from(region.getStartLine(), context.codemodId())); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Creates a visitor for the given context and locations. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the javadoc here, It does not create a visitor anymore. |
||
| * | ||
| * @param context the context of this files transformation | ||
| * @param regions the places in this file that have been identified as needing change by the | ||
| * static analysis | ||
| * @return a visitor that will perform the necessary changes in the given source code file | ||
| * positions | ||
| * @param cu the parsed model of the file being transformed | ||
| * @param node the node to act on | ||
| * @param result the given SARIF result to act on | ||
| */ | ||
| public abstract ModifierVisitor<FileWeavingContext> createVisitor( | ||
| CodemodInvocationContext context, List<Region> regions); | ||
| public abstract void onSemgrepResultFound( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Small suggestion: make it return |
||
| CodemodInvocationContext context, CompilationUnit cu, T node, Result result); | ||
|
|
||
| private static final Logger logger = LoggerFactory.getLogger(SemgrepJavaParserChanger.class); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this since we have
ASTTransforms::addImportIfMissing.