io.codemodder.codemods.SpringAbsoluteCookieTimeoutCodemod Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-codemods Show documentation
Show all versions of core-codemods Show documentation
Codemods for fixing common errors across many Java projects
package io.codemodder.codemods;
import static io.codemodder.CodemodParameter.*;
import io.codemodder.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This codemod will set the absolute timeout for Spring session cookies in application.properties
* if it's missing or too high.
*/
@Codemod(
id = "pixee:java/spring-absolute-cookie-timeout",
importance = Importance.MEDIUM,
reviewGuidance = ReviewGuidance.MERGE_AFTER_REVIEW)
public final class SpringAbsoluteCookieTimeoutCodemod extends RawFileChanger {
private final Duration safeDuration;
private final Parameter timeout;
@Inject
public SpringAbsoluteCookieTimeoutCodemod(
final @CodemodParameter(
question =
"How long should the absolute timeout (not idle timeout!) be for Spring session cookies (enter 10m for 10 minutes, 8h for 8 hours, 2d for 2 days, etc.)?",
name = "timeout",
type = ParameterType.STRING,
label =
"the max-age for Spring session cookies to be set via the server.servlet.session.timeout field",
defaultValue = "8h",
validationPattern = "\\d+[smhd]") Parameter timeout) {
this.timeout = timeout;
String defaultValue = timeout.getDefaultValue();
String count = defaultValue.substring(0, defaultValue.length() - 1);
String units = defaultValue.substring(defaultValue.length() - 1);
this.safeDuration = parseExistingValueFromLine(count, units);
}
@Override
public CodemodFileScanningResult visitFile(final CodemodInvocationContext context)
throws IOException {
Path path = context.path();
if (!"application.properties".equalsIgnoreCase(path.getFileName().toString())) {
return CodemodFileScanningResult.none();
} else if (!inExpectedDir(context.codeDirectory().asPath().relativize(path))) {
return CodemodFileScanningResult.none();
}
List changes = new ArrayList<>();
LineIncludesExcludes lineIncludesExcludes = context.lineIncludesExcludes();
List lines = Files.readAllLines(path);
boolean mustAdd = true;
for (int currentLine = 1; currentLine < lines.size() + 1; currentLine++) {
if (!lineIncludesExcludes.matches(currentLine)) {
continue;
}
String line = lines.get(currentLine - 1).trim();
Matcher matcher = timeoutPattern.matcher(line);
if (matcher.matches()) {
try {
Duration foundDuration = parseExistingValueFromLine(matcher.group(3), matcher.group(4));
if (foundDuration.compareTo(safeDuration) <= 0) {
mustAdd = false;
continue;
}
String parameter = timeout.getValue(path, currentLine);
changes.add(CodemodChange.from(currentLine, timeout, parameter));
lines.set(currentLine - 1, "server.servlet.session.timeout=" + parameter);
mustAdd = false;
} catch (Exception e) {
LOG.error("Problem parsing session timeout value from line: `{}`", line);
}
}
}
if (mustAdd) {
String parameter = timeout.getValue(path, lines.size());
changes.add(CodemodChange.from(lines.size(), timeout, parameter));
List newLines = new ArrayList<>(lines);
newLines.add("server.servlet.session.timeout=" + parameter);
lines = newLines;
}
if (!changes.isEmpty()) {
Files.write(path, lines);
return CodemodFileScanningResult.withOnlyChanges(changes);
}
return CodemodFileScanningResult.none();
}
private boolean inExpectedDir(final Path relativePath) {
return relativePath.toString().contains("src/main/resources");
}
private Duration parseExistingValueFromLine(final String number, final String unit) {
long value = Integer.parseInt(number);
return switch (unit) {
case "m" -> Duration.ofMinutes(value);
case "s" -> Duration.ofSeconds(value);
case "h" -> Duration.ofHours(value);
case "d" -> Duration.ofDays(value);
case "w" -> Duration.ofDays(value * 7);
case "y" -> Duration.ofDays(value * 365);
default -> throw new IllegalArgumentException("Unknown unit: " + unit);
};
}
private static final Pattern timeoutPattern =
Pattern.compile("server\\.servlet\\.session\\.timeout(\\s)*=(\\s)*(\\d+)([mshdwy])");
private static final Logger LOG =
LoggerFactory.getLogger(SpringAbsoluteCookieTimeoutCodemod.class);
}