Skip to content

Commit

Permalink
CookieSetSecure
Browse files Browse the repository at this point in the history
  • Loading branch information
jkschneider committed Dec 7, 2023
1 parent 4fb2d26 commit 7bc30ee
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 0 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
runtimeOnly("org.springframework.security:spring-security-config:5.+")
runtimeOnly("org.springframework.security:spring-security-web:5.+")
runtimeOnly("jakarta.servlet:jakarta.servlet-api:4.+")
runtimeOnly("javax:javaee-api:7.+")

testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release")
testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.security.servlet;

import fj.data.Option;
import org.openrewrite.*;
import org.openrewrite.analysis.dataflow.DataFlowNode;
import org.openrewrite.analysis.dataflow.DataFlowSpec;
import org.openrewrite.analysis.dataflow.Dataflow;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;

public class CookieSetSecure extends Recipe {

@Override
public String getDisplayName() {
return "Insecure cookies";
}

@Override
public String getDescription() {
return "Check for use of insecure cookies. Cookies should be marked as secure. " +
"This ensures that the cookie is sent only over HTTPS to prevent cross-site scripting attacks.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
MethodMatcher newCookie = new MethodMatcher("javax.servlet.http.Cookie <constructor>(..)");
MethodMatcher setSecure = new MethodMatcher("javax.servlet.http.Cookie setSecure(boolean)");

return Preconditions.check(new UsesMethod<>(newCookie), new JavaIsoVisitor<ExecutionContext>() {

@Override
public J.Block visitBlock(J.Block block, ExecutionContext ctx) {
J.Block b = super.visitBlock(block, ctx);
J.VariableDeclarations insecure = getCursor().getMessage("insecure");
if (insecure != null) {
J.MethodInvocation setSecureFalse = getCursor().getMessage("setSecureFalse");
if (setSecureFalse == null) {
return JavaTemplate.builder("#{any(javax.servlet.http.Cookie)}.setSecure(true);")
.javaParser(JavaParser.fromJavaVersion().classpath("javaee-api"))
.build()
.apply(getCursor(), insecure.getCoordinates().after(),
insecure.getVariables().get(0).getName());
} else {
return JavaTemplate.builder("true").build()
.apply(getCursor(), setSecureFalse.getCoordinates().replaceArguments());
}
}
return b;
}

@Override
public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
if (newCookie.matches(newClass) && getCursor().firstEnclosing(J.VariableDeclarations.class) != null) {
boolean isInsecure = Dataflow.startingAt(getCursor()).findSinks(new DataFlowSpec() {
@Override
public boolean isSource(DataFlowNode srcNode) {
return true;
}

@Override
public boolean isSink(DataFlowNode sinkNode) {
Object value = sinkNode.getCursor().getParentTreeCursor().getValue();
return value instanceof J.MethodInvocation &&
setSecure.matches((J.MethodInvocation) value);
}
}).bind(sinkFlow -> {
for (Cursor sink : sinkFlow.getSinkCursors()) {
J.MethodInvocation setSecure = sink.getParentTreeCursor().getValue();
Expression arg = setSecure.getArguments().get(0);
if (!(arg instanceof J.Literal) || Boolean.TRUE.equals(((J.Literal) arg).getValue())) {
// explicitly setSecure(true)
return Option.some(sinkFlow);
}
getCursor().putMessageOnFirstEnclosing(J.Block.class, "setSecureFalse", setSecure);
}
return Option.none();
})
.isNone();

if (isInsecure) {
// either no setSecure call at all, or setSecure(false)
getCursor().putMessageOnFirstEnclosing(J.Block.class, "insecure",
getCursor().firstEnclosingOrThrow(J.VariableDeclarations.class));
}
}

return super.visitNewClass(newClass, ctx);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2021 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NonNullApi
package org.openrewrite.java.security.servlet;

import org.openrewrite.internal.lang.NonNullApi;
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/rewrite/owasp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ description: >
(or lack thereof), which often lead to exposure of sensitive data. This recipe seeks to remediate these vulnerabilities.
recipeList:
- org.openrewrite.java.spring.security5.search.FindEncryptorsQueryableTextUses
- org.openrewrite.java.security.servlet.CookieSetSecure
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.security.OwaspA03
Expand All @@ -59,6 +60,7 @@ description: >
recipeList:
- org.openrewrite.java.security.RegularExpressionDenialOfService
- org.openrewrite.staticanalysis.NoEqualityInForCondition
- org.openrewrite.java.security.servlet.CookieSetSecure
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.security.OwaspA04
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.security.servlet;

import org.junit.jupiter.api.Test;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

public class CookieSetSecureTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec.recipe(new CookieSetSecure())
.parser(JavaParser.fromJavaVersion().classpath("javaee-api"));
}

@Test
void setSecureFalse() {
//language=java
rewriteRun(
java(
"""
import javax.servlet.http.Cookie;
class Test {
void test() {
Cookie cookie = new Cookie("foo", "bar");
System.out.println("hi");
cookie.setSecure(false);
}
}
""",
"""
import javax.servlet.http.Cookie;
class Test {
void test() {
Cookie cookie = new Cookie("foo", "bar");
System.out.println("hi");
cookie.setSecure(true);
}
}
"""
)
);
}

@Test
void setSecureTrue() {
//language=java
rewriteRun(
java(
"""
import javax.servlet.http.Cookie;
class Test {
void test() {
Cookie cookie = new Cookie("foo", "bar");
System.out.println("hi");
cookie.setSecure(true);
}
}
"""
)
);
}

@Test
void defaultInsecure() {
//language=java
rewriteRun(
java(
"""
import javax.servlet.http.Cookie;
class Test {
void test() {
Cookie cookie = new Cookie("foo", "bar");
System.out.println("hi");
}
}
""",
"""
import javax.servlet.http.Cookie;
class Test {
void test() {
Cookie cookie = new Cookie("foo", "bar");
cookie.setSecure(true);
System.out.println("hi");
}
}
"""
)
);
}
}

0 comments on commit 7bc30ee

Please sign in to comment.