Skip to content

Instantly share code, notes, and snippets.

@jkschneider
Created December 5, 2023 20:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jkschneider/c29ebe9e260847e058391f3c07a3993d to your computer and use it in GitHub Desktop.
Save jkschneider/c29ebe9e260847e058391f3c07a3993d to your computer and use it in GitHub Desktop.

How many places does my birthday exist in our source code?

Let's get a bit more specific. How many places does the year of my birthdate exist in a literal somewhere in the codebase. Comments don't count. Variable names don't count.

Can you imagine doing something like this with a regular expression?

// 12-05-1983
class Hey {
  void itsYourBirthday() {
    int year = 1983; // match!
  }

  /* An oddly specific method about 1983 */
  int importantThingsThatHappenedIn1983() {
    return 0;
  }
}

Opportunity for an OpenRewrite recipe! OpenRewrite operates on a Lossless Semantic Tree (LST), which is kind of like a super super enriched Abstract Syntax Tree (AST). Recipes use the visitor pattern at the most fundamental level to intercept certain types of syntax and check (and possibly transform!) them.

@Value
@EqualsAndHashCode(callSuper = false)
public class FindLiterals extends Recipe {
    @Option(displayName = "Pattern",
            description = "A regular expression pattern to match literals against.",
            example = "file://")
    String pattern;

    @Override
    public String getDisplayName() {
        return "Find literals";
    }

    @Override
    public String getDescription() {
        return "Find literals matching a pattern.";
    }

    @Override
    public TreeVisitor<?, ExecutionContext> getVisitor() {
        Pattern compiledPattern = Pattern.compile(pattern);
        return new JavaIsoVisitor<ExecutionContext>() {
            @Override
            public J.Literal visitLiteral(J.Literal literal, ExecutionContext ctx) {
                if (literal.getValueSource() != null) {
                    if (literal.getType() == JavaType.Primitive.String) {
                        if (!literal.getValueSource().isEmpty() && compiledPattern.matcher(literal.getValueSource().substring(1, literal.getValueSource().length() - 1)).matches()) {
                            return SearchResult.found(literal);
                        }
                    }
                    if (compiledPattern.matcher(literal.getValueSource()).matches()) {
                        return SearchResult.found(literal);
                    }
                }
                return literal;
            }
        };
    }
}

Can you spot the potential security vulnerability inherent in this recipe? Jonathan Leitschuh can. Even better, can you write a recipe that fixes this and every other similar vulnerability?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment