From 2ce5920c5e9b387410358b23d4f70b0d023f4535 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Sun, 16 Feb 2025 12:10:36 -0500
Subject: [PATCH 01/17] Java: copy out of experimental
---
.../CWE/CWE-016/SpringBootActuators.java | 22 +++
.../CWE/CWE-016/SpringBootActuators.qhelp | 39 +++++
.../CWE/CWE-016/SpringBootActuators.ql | 20 +++
.../CWE/CWE-016/SpringBootActuators.qll | 157 ++++++++++++++++++
.../CWE-016/SpringBootActuators.expected | 7 +
.../security/CWE-016/SpringBootActuators.java | 104 ++++++++++++
.../CWE-016/SpringBootActuators.qlref | 1 +
.../test/query-tests/security/CWE-016/options | 1 +
8 files changed, 351 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-016/SpringBootActuators.java
create mode 100644 java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qhelp
create mode 100644 java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
create mode 100644 java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll
create mode 100644 java/ql/test/query-tests/security/CWE-016/SpringBootActuators.expected
create mode 100644 java/ql/test/query-tests/security/CWE-016/SpringBootActuators.java
create mode 100644 java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref
create mode 100644 java/ql/test/query-tests/security/CWE-016/options
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.java b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.java
new file mode 100644
index 000000000000..5aec49837cac
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.java
@@ -0,0 +1,22 @@
+@Configuration(proxyBeanMethods = false)
+public class SpringBootActuators extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // BAD: Unauthenticated access to Spring Boot actuator endpoints is allowed
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
+ requests.anyRequest().permitAll());
+ }
+}
+
+@Configuration(proxyBeanMethods = false)
+public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // GOOD: only users with ENDPOINT_ADMIN role are allowed to access the actuator endpoints
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
+ requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
+ http.httpBasic();
+ }
+}
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qhelp b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qhelp
new file mode 100644
index 000000000000..53ee653aaff3
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qhelp
@@ -0,0 +1,39 @@
+
+
+
+Spring Boot includes a number of additional features called actuators that let you monitor
+and interact with your web application. Exposing unprotected actuator endpoints via JXM or HTTP
+can, however, lead to information disclosure or even to remote code execution vulnerability.
+
+
+
+Since actuator endpoints may contain sensitive information, careful consideration should be
+given about when to expose them. You should take care to secure exposed HTTP endpoints in the same
+way that you would any other sensitive URL. If Spring Security is present, endpoints are secured by
+default using Spring Security’s content-negotiation strategy. If you wish to configure custom
+security for HTTP endpoints, for example, only allow users with a certain role to access them,
+Spring Boot provides some convenient RequestMatcher
objects that can be used in
+combination with Spring Security.
+
+
+
+In the first example, the custom security configuration allows unauthenticated access to all
+actuator endpoints. This may lead to sensitive information disclosure and should be avoided.
+In the second example, only users with ENDPOINT_ADMIN
role are allowed to access
+the actuator endpoints.
+
+
+
+
+
+
+Spring Boot documentation:
+Actuators.
+
+
+Exploiting Spring Boot Actuators
+
+
+
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
new file mode 100644
index 000000000000..574336074254
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
@@ -0,0 +1,20 @@
+/**
+ * @name Exposed Spring Boot actuators
+ * @description Exposing Spring Boot actuators may lead to internal application's information leak
+ * or even to remote code execution.
+ * @kind problem
+ * @problem.severity error
+ * @precision high
+ * @id java/spring-boot-exposed-actuators
+ * @tags security
+ * experimental
+ * external/cwe/cwe-16
+ */
+
+import java
+deprecated import SpringBootActuators
+
+deprecated query predicate problems(PermitAllCall permitAllCall, string message) {
+ permitAllCall.permitsSpringBootActuators() and
+ message = "Unauthenticated access to Spring Boot actuator is allowed."
+}
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll
new file mode 100644
index 000000000000..881f2a131720
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll
@@ -0,0 +1,157 @@
+deprecated module;
+
+import java
+
+/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
+class TypeHttpSecurity extends Class {
+ TypeHttpSecurity() {
+ this.hasQualifiedName("org.springframework.security.config.annotation.web.builders",
+ "HttpSecurity")
+ }
+}
+
+/**
+ * The class
+ * `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer`.
+ */
+class TypeAuthorizedUrl extends Class {
+ TypeAuthorizedUrl() {
+ this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
+ "ExpressionUrlAuthorizationConfigurer$AuthorizedUrl<>")
+ }
+}
+
+/**
+ * The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
+ */
+class TypeAbstractRequestMatcherRegistry extends Class {
+ TypeAbstractRequestMatcherRegistry() {
+ this.hasQualifiedName("org.springframework.security.config.annotation.web",
+ "AbstractRequestMatcherRegistry>")
+ }
+}
+
+/**
+ * The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`.
+ */
+class TypeEndpointRequest extends Class {
+ TypeEndpointRequest() {
+ this.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
+ "EndpointRequest")
+ }
+}
+
+/** A call to `EndpointRequest.toAnyEndpoint` method. */
+class ToAnyEndpointCall extends MethodCall {
+ ToAnyEndpointCall() {
+ this.getMethod().hasName("toAnyEndpoint") and
+ this.getMethod().getDeclaringType() instanceof TypeEndpointRequest
+ }
+}
+
+/**
+ * A call to `HttpSecurity.requestMatcher` method with argument `RequestMatcher.toAnyEndpoint()`.
+ */
+class RequestMatcherCall extends MethodCall {
+ RequestMatcherCall() {
+ this.getMethod().hasName("requestMatcher") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
+ this.getArgument(0) instanceof ToAnyEndpointCall
+ }
+}
+
+/**
+ * A call to `HttpSecurity.requestMatchers` method with lambda argument
+ * `RequestMatcher.toAnyEndpoint()`.
+ */
+class RequestMatchersCall extends MethodCall {
+ RequestMatchersCall() {
+ this.getMethod().hasName("requestMatchers") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
+ this.getArgument(0).(LambdaExpr).getExprBody() instanceof ToAnyEndpointCall
+ }
+}
+
+/** A call to `HttpSecurity.authorizeRequests` method. */
+class AuthorizeRequestsCall extends MethodCall {
+ AuthorizeRequestsCall() {
+ this.getMethod().hasName("authorizeRequests") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ }
+}
+
+/** A call to `AuthorizedUrl.permitAll` method. */
+class PermitAllCall extends MethodCall {
+ PermitAllCall() {
+ this.getMethod().hasName("permitAll") and
+ this.getMethod().getDeclaringType() instanceof TypeAuthorizedUrl
+ }
+
+ /** Holds if `permitAll` is called on request(s) mapped to actuator endpoint(s). */
+ predicate permitsSpringBootActuators() {
+ exists(AuthorizeRequestsCall authorizeRequestsCall |
+ // .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
+ authorizeRequestsCall.getQualifier() instanceof RequestMatcherCall
+ or
+ // .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
+ authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall
+ |
+ // [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
+ // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
+ authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and
+ (
+ this.getQualifier() instanceof AnyRequestCall or
+ this.getQualifier() instanceof RegistryRequestMatchersCall
+ )
+ or
+ // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
+ // [...].authorizeRequests().anyRequest().permitAll()
+ authorizeRequestsCall.getNumArgument() = 0 and
+ exists(RegistryRequestMatchersCall registryRequestMatchersCall |
+ registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
+ this.getQualifier() = registryRequestMatchersCall
+ )
+ or
+ exists(AnyRequestCall anyRequestCall |
+ anyRequestCall.getQualifier() = authorizeRequestsCall and
+ this.getQualifier() = anyRequestCall
+ )
+ )
+ or
+ exists(AuthorizeRequestsCall authorizeRequestsCall |
+ // http.authorizeRequests([...]).[...]
+ authorizeRequestsCall.getQualifier() instanceof VarAccess
+ |
+ // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
+ authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and
+ this.getQualifier() instanceof RegistryRequestMatchersCall
+ or
+ // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
+ authorizeRequestsCall.getNumArgument() = 0 and
+ exists(RegistryRequestMatchersCall registryRequestMatchersCall |
+ registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
+ this.getQualifier() = registryRequestMatchersCall
+ )
+ )
+ }
+}
+
+/** A call to `AbstractRequestMatcherRegistry.anyRequest` method. */
+class AnyRequestCall extends MethodCall {
+ AnyRequestCall() {
+ this.getMethod().hasName("anyRequest") and
+ this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry
+ }
+}
+
+/**
+ * A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument
+ * `RequestMatcher.toAnyEndpoint()`.
+ */
+class RegistryRequestMatchersCall extends MethodCall {
+ RegistryRequestMatchersCall() {
+ this.getMethod().hasName("requestMatchers") and
+ this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and
+ this.getAnArgument() instanceof ToAnyEndpointCall
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.expected b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.expected
new file mode 100644
index 000000000000..f2874e3694d1
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.expected
@@ -0,0 +1,7 @@
+| SpringBootActuators.java:6:88:6:120 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
+| SpringBootActuators.java:10:5:10:137 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
+| SpringBootActuators.java:14:5:14:149 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
+| SpringBootActuators.java:18:5:18:101 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
+| SpringBootActuators.java:22:5:22:89 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
+| SpringBootActuators.java:26:40:26:108 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
+| SpringBootActuators.java:30:5:30:113 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.java b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.java
new file mode 100644
index 000000000000..da59919fbe6c
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.java
@@ -0,0 +1,104 @@
+import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+
+public class SpringBootActuators {
+ protected void configure(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest().permitAll());
+ }
+
+ protected void configure2(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
+ }
+
+ protected void configure3(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
+ }
+
+ protected void configure4(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll();
+ }
+
+ protected void configure5(HttpSecurity http) throws Exception {
+ http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
+ }
+
+ protected void configure6(HttpSecurity http) throws Exception {
+ http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll());
+ }
+
+ protected void configure7(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll();
+ }
+
+ protected void configureOk1(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOk2(HttpSecurity http) throws Exception {
+ http.requestMatchers().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOk3(HttpSecurity http) throws Exception {
+ http.authorizeRequests().anyRequest().permitAll();
+ }
+
+ protected void configureOk4(HttpSecurity http) throws Exception {
+ http.authorizeRequests(authz -> authz.anyRequest().permitAll());
+ }
+
+ protected void configureOkSafeEndpoints1(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.to("health", "info")).authorizeRequests(requests -> requests.anyRequest().permitAll());
+ }
+
+ protected void configureOkSafeEndpoints2(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.to("health")).authorizeRequests().requestMatchers(EndpointRequest.to("health")).permitAll();
+ }
+
+ protected void configureOkSafeEndpoints3(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll();
+ }
+
+ protected void configureOkSafeEndpoints4(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.to("health", "info")).authorizeRequests().anyRequest().permitAll();
+ }
+
+ protected void configureOkSafeEndpoints5(HttpSecurity http) throws Exception {
+ http.authorizeRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll();
+ }
+
+ protected void configureOkSafeEndpoints6(HttpSecurity http) throws Exception {
+ http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.to("health", "info")).permitAll());
+ }
+
+ protected void configureOkSafeEndpoints7(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeRequests().anyRequest().permitAll();
+ }
+
+ protected void configureOkNoPermitAll1(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest());
+ }
+
+ protected void configureOkNoPermitAll2(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOkNoPermitAll3(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOkNoPermitAll4(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest();
+ }
+
+ protected void configureOkNoPermitAll5(HttpSecurity http) throws Exception {
+ http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOkNoPermitAll6(HttpSecurity http) throws Exception {
+ http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()));
+ }
+
+ protected void configureOkNoPermitAll7(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest();
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref
new file mode 100644
index 000000000000..ec49ecd718c2
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-016/SpringBootActuators.ql
diff --git a/java/ql/test/query-tests/security/CWE-016/options b/java/ql/test/query-tests/security/CWE-016/options
new file mode 100644
index 000000000000..06ec85dc706c
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-016/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.3.8
From 978834bd9cc0ec542e45988a82613a0c7a14e40e Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Sun, 16 Feb 2025 12:16:14 -0500
Subject: [PATCH 02/17] Java: remove deprecations
---
java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql | 9 ++++-----
java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll | 2 --
2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
index 574336074254..b700e691550f 100644
--- a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
+++ b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
@@ -12,9 +12,8 @@
*/
import java
-deprecated import SpringBootActuators
+import SpringBootActuators
-deprecated query predicate problems(PermitAllCall permitAllCall, string message) {
- permitAllCall.permitsSpringBootActuators() and
- message = "Unauthenticated access to Spring Boot actuator is allowed."
-}
+from PermitAllCall permitAllCall
+where permitAllCall.permitsSpringBootActuators()
+select permitAllCall, "Unauthenticated access to Spring Boot actuator is allowed."
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll
index 881f2a131720..195de7a1b8be 100644
--- a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll
+++ b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll
@@ -1,5 +1,3 @@
-deprecated module;
-
import java
/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
From 089a491d5a332f4aa43e53e518b79088673f38af Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Sun, 16 Feb 2025 12:20:23 -0500
Subject: [PATCH 03/17] Java: fix tests; update for non-experimental directory
---
.../test/query-tests/security/CWE-016/SpringBootActuators.qlref | 2 +-
java/ql/test/query-tests/security/CWE-016/options | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref
index ec49ecd718c2..abd5f2a75991 100644
--- a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref
@@ -1 +1 @@
-experimental/Security/CWE/CWE-016/SpringBootActuators.ql
+Security/CWE/CWE-016/SpringBootActuators.ql
diff --git a/java/ql/test/query-tests/security/CWE-016/options b/java/ql/test/query-tests/security/CWE-016/options
index 06ec85dc706c..38d1d754b69c 100644
--- a/java/ql/test/query-tests/security/CWE-016/options
+++ b/java/ql/test/query-tests/security/CWE-016/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.3.8
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.3.8
From 5e5bc2afe9d4264a226897b49a8903b37bfebaa1 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Sun, 16 Feb 2025 12:30:42 -0500
Subject: [PATCH 04/17] Java: remove experimental files
---
.../CWE/CWE-016/SpringBootActuators.java | 22 ---
.../CWE/CWE-016/SpringBootActuators.qhelp | 39 -----
.../CWE/CWE-016/SpringBootActuators.ql | 20 ---
.../CWE/CWE-016/SpringBootActuators.qll | 157 ------------------
.../CWE-016/SpringBootActuators.expected | 7 -
.../security/CWE-016/SpringBootActuators.java | 104 ------------
.../CWE-016/SpringBootActuators.qlref | 1 -
7 files changed, 350 deletions(-)
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.java
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qhelp
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.ql
delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qll
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.expected
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.java
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.qlref
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.java b/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.java
deleted file mode 100644
index 538620550efc..000000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.java
+++ /dev/null
@@ -1,22 +0,0 @@
-@Configuration(proxyBeanMethods = false)
-public class SpringBootActuators extends WebSecurityConfigurerAdapter {
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- // BAD: Unauthenticated access to Spring Boot actuator endpoints is allowed
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
- requests.anyRequest().permitAll());
- }
-}
-
-@Configuration(proxyBeanMethods = false)
-public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- // GOOD: only users with ENDPOINT_ADMIN role are allowed to access the actuator endpoints
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
- requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
- http.httpBasic();
- }
-}
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qhelp b/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qhelp
deleted file mode 100644
index 53ee653aaff3..000000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qhelp
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-Spring Boot includes a number of additional features called actuators that let you monitor
-and interact with your web application. Exposing unprotected actuator endpoints via JXM or HTTP
-can, however, lead to information disclosure or even to remote code execution vulnerability.
-
-
-
-Since actuator endpoints may contain sensitive information, careful consideration should be
-given about when to expose them. You should take care to secure exposed HTTP endpoints in the same
-way that you would any other sensitive URL. If Spring Security is present, endpoints are secured by
-default using Spring Security’s content-negotiation strategy. If you wish to configure custom
-security for HTTP endpoints, for example, only allow users with a certain role to access them,
-Spring Boot provides some convenient RequestMatcher
objects that can be used in
-combination with Spring Security.
-
-
-
-In the first example, the custom security configuration allows unauthenticated access to all
-actuator endpoints. This may lead to sensitive information disclosure and should be avoided.
-In the second example, only users with ENDPOINT_ADMIN
role are allowed to access
-the actuator endpoints.
-
-
-
-
-
-
-Spring Boot documentation:
-Actuators.
-
-
-Exploiting Spring Boot Actuators
-
-
-
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.ql b/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.ql
deleted file mode 100644
index 574336074254..000000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.ql
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * @name Exposed Spring Boot actuators
- * @description Exposing Spring Boot actuators may lead to internal application's information leak
- * or even to remote code execution.
- * @kind problem
- * @problem.severity error
- * @precision high
- * @id java/spring-boot-exposed-actuators
- * @tags security
- * experimental
- * external/cwe/cwe-16
- */
-
-import java
-deprecated import SpringBootActuators
-
-deprecated query predicate problems(PermitAllCall permitAllCall, string message) {
- permitAllCall.permitsSpringBootActuators() and
- message = "Unauthenticated access to Spring Boot actuator is allowed."
-}
diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qll b/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qll
deleted file mode 100644
index 881f2a131720..000000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qll
+++ /dev/null
@@ -1,157 +0,0 @@
-deprecated module;
-
-import java
-
-/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
-class TypeHttpSecurity extends Class {
- TypeHttpSecurity() {
- this.hasQualifiedName("org.springframework.security.config.annotation.web.builders",
- "HttpSecurity")
- }
-}
-
-/**
- * The class
- * `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer`.
- */
-class TypeAuthorizedUrl extends Class {
- TypeAuthorizedUrl() {
- this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
- "ExpressionUrlAuthorizationConfigurer$AuthorizedUrl<>")
- }
-}
-
-/**
- * The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
- */
-class TypeAbstractRequestMatcherRegistry extends Class {
- TypeAbstractRequestMatcherRegistry() {
- this.hasQualifiedName("org.springframework.security.config.annotation.web",
- "AbstractRequestMatcherRegistry>")
- }
-}
-
-/**
- * The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`.
- */
-class TypeEndpointRequest extends Class {
- TypeEndpointRequest() {
- this.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
- "EndpointRequest")
- }
-}
-
-/** A call to `EndpointRequest.toAnyEndpoint` method. */
-class ToAnyEndpointCall extends MethodCall {
- ToAnyEndpointCall() {
- this.getMethod().hasName("toAnyEndpoint") and
- this.getMethod().getDeclaringType() instanceof TypeEndpointRequest
- }
-}
-
-/**
- * A call to `HttpSecurity.requestMatcher` method with argument `RequestMatcher.toAnyEndpoint()`.
- */
-class RequestMatcherCall extends MethodCall {
- RequestMatcherCall() {
- this.getMethod().hasName("requestMatcher") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
- this.getArgument(0) instanceof ToAnyEndpointCall
- }
-}
-
-/**
- * A call to `HttpSecurity.requestMatchers` method with lambda argument
- * `RequestMatcher.toAnyEndpoint()`.
- */
-class RequestMatchersCall extends MethodCall {
- RequestMatchersCall() {
- this.getMethod().hasName("requestMatchers") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
- this.getArgument(0).(LambdaExpr).getExprBody() instanceof ToAnyEndpointCall
- }
-}
-
-/** A call to `HttpSecurity.authorizeRequests` method. */
-class AuthorizeRequestsCall extends MethodCall {
- AuthorizeRequestsCall() {
- this.getMethod().hasName("authorizeRequests") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
- }
-}
-
-/** A call to `AuthorizedUrl.permitAll` method. */
-class PermitAllCall extends MethodCall {
- PermitAllCall() {
- this.getMethod().hasName("permitAll") and
- this.getMethod().getDeclaringType() instanceof TypeAuthorizedUrl
- }
-
- /** Holds if `permitAll` is called on request(s) mapped to actuator endpoint(s). */
- predicate permitsSpringBootActuators() {
- exists(AuthorizeRequestsCall authorizeRequestsCall |
- // .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
- authorizeRequestsCall.getQualifier() instanceof RequestMatcherCall
- or
- // .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
- authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall
- |
- // [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
- // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
- authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and
- (
- this.getQualifier() instanceof AnyRequestCall or
- this.getQualifier() instanceof RegistryRequestMatchersCall
- )
- or
- // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
- // [...].authorizeRequests().anyRequest().permitAll()
- authorizeRequestsCall.getNumArgument() = 0 and
- exists(RegistryRequestMatchersCall registryRequestMatchersCall |
- registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
- this.getQualifier() = registryRequestMatchersCall
- )
- or
- exists(AnyRequestCall anyRequestCall |
- anyRequestCall.getQualifier() = authorizeRequestsCall and
- this.getQualifier() = anyRequestCall
- )
- )
- or
- exists(AuthorizeRequestsCall authorizeRequestsCall |
- // http.authorizeRequests([...]).[...]
- authorizeRequestsCall.getQualifier() instanceof VarAccess
- |
- // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
- authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and
- this.getQualifier() instanceof RegistryRequestMatchersCall
- or
- // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
- authorizeRequestsCall.getNumArgument() = 0 and
- exists(RegistryRequestMatchersCall registryRequestMatchersCall |
- registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
- this.getQualifier() = registryRequestMatchersCall
- )
- )
- }
-}
-
-/** A call to `AbstractRequestMatcherRegistry.anyRequest` method. */
-class AnyRequestCall extends MethodCall {
- AnyRequestCall() {
- this.getMethod().hasName("anyRequest") and
- this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry
- }
-}
-
-/**
- * A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument
- * `RequestMatcher.toAnyEndpoint()`.
- */
-class RegistryRequestMatchersCall extends MethodCall {
- RegistryRequestMatchersCall() {
- this.getMethod().hasName("requestMatchers") and
- this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and
- this.getAnArgument() instanceof ToAnyEndpointCall
- }
-}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.expected b/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.expected
deleted file mode 100644
index f2874e3694d1..000000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.expected
+++ /dev/null
@@ -1,7 +0,0 @@
-| SpringBootActuators.java:6:88:6:120 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:10:5:10:137 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:14:5:14:149 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:18:5:18:101 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:22:5:22:89 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:26:40:26:108 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:30:5:30:113 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.java b/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.java
deleted file mode 100644
index da59919fbe6c..000000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.java
+++ /dev/null
@@ -1,104 +0,0 @@
-import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-
-public class SpringBootActuators {
- protected void configure(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest().permitAll());
- }
-
- protected void configure2(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
- }
-
- protected void configure3(HttpSecurity http) throws Exception {
- http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
- }
-
- protected void configure4(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll();
- }
-
- protected void configure5(HttpSecurity http) throws Exception {
- http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
- }
-
- protected void configure6(HttpSecurity http) throws Exception {
- http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll());
- }
-
- protected void configure7(HttpSecurity http) throws Exception {
- http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll();
- }
-
- protected void configureOk1(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint());
- }
-
- protected void configureOk2(HttpSecurity http) throws Exception {
- http.requestMatchers().requestMatchers(EndpointRequest.toAnyEndpoint());
- }
-
- protected void configureOk3(HttpSecurity http) throws Exception {
- http.authorizeRequests().anyRequest().permitAll();
- }
-
- protected void configureOk4(HttpSecurity http) throws Exception {
- http.authorizeRequests(authz -> authz.anyRequest().permitAll());
- }
-
- protected void configureOkSafeEndpoints1(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.to("health", "info")).authorizeRequests(requests -> requests.anyRequest().permitAll());
- }
-
- protected void configureOkSafeEndpoints2(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.to("health")).authorizeRequests().requestMatchers(EndpointRequest.to("health")).permitAll();
- }
-
- protected void configureOkSafeEndpoints3(HttpSecurity http) throws Exception {
- http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll();
- }
-
- protected void configureOkSafeEndpoints4(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.to("health", "info")).authorizeRequests().anyRequest().permitAll();
- }
-
- protected void configureOkSafeEndpoints5(HttpSecurity http) throws Exception {
- http.authorizeRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll();
- }
-
- protected void configureOkSafeEndpoints6(HttpSecurity http) throws Exception {
- http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.to("health", "info")).permitAll());
- }
-
- protected void configureOkSafeEndpoints7(HttpSecurity http) throws Exception {
- http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeRequests().anyRequest().permitAll();
- }
-
- protected void configureOkNoPermitAll1(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest());
- }
-
- protected void configureOkNoPermitAll2(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
- }
-
- protected void configureOkNoPermitAll3(HttpSecurity http) throws Exception {
- http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
- }
-
- protected void configureOkNoPermitAll4(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest();
- }
-
- protected void configureOkNoPermitAll5(HttpSecurity http) throws Exception {
- http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
- }
-
- protected void configureOkNoPermitAll6(HttpSecurity http) throws Exception {
- http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()));
- }
-
- protected void configureOkNoPermitAll7(HttpSecurity http) throws Exception {
- http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest();
- }
-}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.qlref b/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.qlref
deleted file mode 100644
index ec49ecd718c2..000000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-016/SpringBootActuators.ql
From 8064e8f1f9f9771784bb2a12b9cbc5d466fa4a64 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 19 Feb 2025 19:03:02 -0500
Subject: [PATCH 05/17] Java: convert tests to inline expectations
---
.../security/SpringBootActuatorsQuery.qll} | 2 ++
.../CWE/CWE-016/SpringBootActuators.ql | 2 +-
.../CWE-016/SpringBootActuators.expected | 7 -------
.../security/CWE-016/SpringBootActuators.qlref | 1 -
.../CWE-016/SpringBootActuatorsTest.expected | 0
...ators.java => SpringBootActuatorsTest.java} | 16 ++++++++--------
.../CWE-016/SpringBootActuatorsTest.ql | 18 ++++++++++++++++++
7 files changed, 29 insertions(+), 17 deletions(-)
rename java/ql/{src/Security/CWE/CWE-016/SpringBootActuators.qll => lib/semmle/code/java/security/SpringBootActuatorsQuery.qll} (98%)
delete mode 100644 java/ql/test/query-tests/security/CWE-016/SpringBootActuators.expected
delete mode 100644 java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref
create mode 100644 java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.expected
rename java/ql/test/query-tests/security/CWE-016/{SpringBootActuators.java => SpringBootActuatorsTest.java} (89%)
create mode 100644 java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
similarity index 98%
rename from java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll
rename to java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
index 195de7a1b8be..9aac9e4fc1a2 100644
--- a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qll
+++ b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
@@ -1,3 +1,5 @@
+/** Provides classes and predicates to reason about exposed actuators in Spring Boot. */
+
import java
/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
index b700e691550f..c74c34284233 100644
--- a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
+++ b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
@@ -12,7 +12,7 @@
*/
import java
-import SpringBootActuators
+import semmle.code.java.security.SpringBootActuatorsQuery
from PermitAllCall permitAllCall
where permitAllCall.permitsSpringBootActuators()
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.expected b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.expected
deleted file mode 100644
index f2874e3694d1..000000000000
--- a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.expected
+++ /dev/null
@@ -1,7 +0,0 @@
-| SpringBootActuators.java:6:88:6:120 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:10:5:10:137 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:14:5:14:149 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:18:5:18:101 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:22:5:22:89 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:26:40:26:108 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
-| SpringBootActuators.java:30:5:30:113 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref b/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref
deleted file mode 100644
index abd5f2a75991..000000000000
--- a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Security/CWE/CWE-016/SpringBootActuators.ql
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.expected b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.expected
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.java b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
similarity index 89%
rename from java/ql/test/query-tests/security/CWE-016/SpringBootActuators.java
rename to java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
index da59919fbe6c..71856f5c1a92 100644
--- a/java/ql/test/query-tests/security/CWE-016/SpringBootActuators.java
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
@@ -1,33 +1,33 @@
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-public class SpringBootActuators {
+public class SpringBootActuatorsTest {
protected void configure(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest().permitAll());
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest().permitAll()); // $ hasExposedSpringBootActuator
}
protected void configure2(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); // $ hasExposedSpringBootActuator
}
protected void configure3(HttpSecurity http) throws Exception {
- http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); // $ hasExposedSpringBootActuator
}
protected void configure4(HttpSecurity http) throws Exception {
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll();
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll(); // $ hasExposedSpringBootActuator
}
protected void configure5(HttpSecurity http) throws Exception {
- http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
+ http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); // $ hasExposedSpringBootActuator
}
protected void configure6(HttpSecurity http) throws Exception {
- http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll());
+ http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()); // $ hasExposedSpringBootActuator
}
protected void configure7(HttpSecurity http) throws Exception {
- http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll();
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll(); // $ hasExposedSpringBootActuator
}
protected void configureOk1(HttpSecurity http) throws Exception {
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql
new file mode 100644
index 000000000000..f397fdb79aab
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql
@@ -0,0 +1,18 @@
+import java
+import semmle.code.java.security.SpringBootActuatorsQuery
+import utils.test.InlineExpectationsTest
+
+module SpringBootActuatorsTest implements TestSig {
+ string getARelevantTag() { result = "hasExposedSpringBootActuator" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "hasExposedSpringBootActuator" and
+ exists(PermitAllCall permitAllCall | permitAllCall.permitsSpringBootActuators() |
+ permitAllCall.getLocation() = location and
+ element = permitAllCall.toString() and
+ value = ""
+ )
+ }
+}
+
+import MakeTest
From 8dfb920e051c3573fe0ca52c429408985bbaa6a3 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 20 Feb 2025 16:26:46 -0500
Subject: [PATCH 06/17] Java: refactor QL, move code to libraries
---
.../java/frameworks/spring/SpringBoot.qll | 24 +++
.../java/frameworks/spring/SpringSecurity.qll | 59 ++++++
.../security/SpringBootActuatorsQuery.qll | 178 ++++++------------
.../CWE/CWE-016/SpringBootActuators.ql | 3 +-
.../CWE-016/SpringBootActuatorsTest.ql | 3 +-
5 files changed, 142 insertions(+), 125 deletions(-)
create mode 100644 java/ql/lib/semmle/code/java/frameworks/spring/SpringBoot.qll
create mode 100644 java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringBoot.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringBoot.qll
new file mode 100644
index 000000000000..864fc4bfcaff
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringBoot.qll
@@ -0,0 +1,24 @@
+/**
+ * Provides classes for working with Spring classes and interfaces from
+ * `org.springframework.boot.*`.
+ */
+
+import java
+
+/**
+ * The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`.
+ */
+class TypeEndpointRequest extends Class {
+ TypeEndpointRequest() {
+ this.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
+ "EndpointRequest")
+ }
+}
+
+/** A call to `EndpointRequest.toAnyEndpoint` method. */
+class ToAnyEndpointCall extends MethodCall {
+ ToAnyEndpointCall() {
+ this.getMethod().hasName("toAnyEndpoint") and
+ this.getMethod().getDeclaringType() instanceof TypeEndpointRequest
+ }
+}
diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
new file mode 100644
index 000000000000..3f11cc8d3ec8
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
@@ -0,0 +1,59 @@
+/**
+ * Provides classes for working with Spring classes and interfaces from
+ * `org.springframework.security.*`.
+ */
+
+import java
+
+/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
+class TypeHttpSecurity extends Class {
+ TypeHttpSecurity() {
+ this.hasQualifiedName("org.springframework.security.config.annotation.web.builders",
+ "HttpSecurity")
+ }
+}
+
+/**
+ * The class
+ * `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer`.
+ */
+class TypeAuthorizedUrl extends Class {
+ TypeAuthorizedUrl() {
+ this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
+ "ExpressionUrlAuthorizationConfigurer$AuthorizedUrl<>")
+ }
+}
+
+/**
+ * The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
+ */
+class TypeAbstractRequestMatcherRegistry extends Class {
+ TypeAbstractRequestMatcherRegistry() {
+ this.hasQualifiedName("org.springframework.security.config.annotation.web",
+ "AbstractRequestMatcherRegistry>")
+ }
+}
+
+/** A call to `HttpSecurity.authorizeRequests` method. */
+class AuthorizeRequestsCall extends MethodCall {
+ AuthorizeRequestsCall() {
+ this.getMethod().hasName("authorizeRequests") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ }
+}
+
+/** A call to `AuthorizedUrl.permitAll` method. */
+class PermitAllCall extends MethodCall {
+ PermitAllCall() {
+ this.getMethod().hasName("permitAll") and
+ this.getMethod().getDeclaringType() instanceof TypeAuthorizedUrl
+ }
+}
+
+/** A call to `AbstractRequestMatcherRegistry.anyRequest` method. */
+class AnyRequestCall extends MethodCall {
+ AnyRequestCall() {
+ this.getMethod().hasName("anyRequest") and
+ this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry
+ }
+}
diff --git a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
index 9aac9e4fc1a2..6ccfa39f3d63 100644
--- a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
@@ -1,58 +1,14 @@
/** Provides classes and predicates to reason about exposed actuators in Spring Boot. */
import java
-
-/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
-class TypeHttpSecurity extends Class {
- TypeHttpSecurity() {
- this.hasQualifiedName("org.springframework.security.config.annotation.web.builders",
- "HttpSecurity")
- }
-}
-
-/**
- * The class
- * `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer`.
- */
-class TypeAuthorizedUrl extends Class {
- TypeAuthorizedUrl() {
- this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
- "ExpressionUrlAuthorizationConfigurer$AuthorizedUrl<>")
- }
-}
+private import semmle.code.java.frameworks.spring.SpringSecurity
+private import semmle.code.java.frameworks.spring.SpringBoot
/**
- * The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
- */
-class TypeAbstractRequestMatcherRegistry extends Class {
- TypeAbstractRequestMatcherRegistry() {
- this.hasQualifiedName("org.springframework.security.config.annotation.web",
- "AbstractRequestMatcherRegistry>")
- }
-}
-
-/**
- * The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`.
- */
-class TypeEndpointRequest extends Class {
- TypeEndpointRequest() {
- this.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
- "EndpointRequest")
- }
-}
-
-/** A call to `EndpointRequest.toAnyEndpoint` method. */
-class ToAnyEndpointCall extends MethodCall {
- ToAnyEndpointCall() {
- this.getMethod().hasName("toAnyEndpoint") and
- this.getMethod().getDeclaringType() instanceof TypeEndpointRequest
- }
-}
-
-/**
- * A call to `HttpSecurity.requestMatcher` method with argument `RequestMatcher.toAnyEndpoint()`.
+ * A call to `HttpSecurity.requestMatcher` method with argument
+ * `RequestMatcher.toAnyEndpoint()`.
*/
-class RequestMatcherCall extends MethodCall {
+private class RequestMatcherCall extends MethodCall {
RequestMatcherCall() {
this.getMethod().hasName("requestMatcher") and
this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
@@ -64,7 +20,7 @@ class RequestMatcherCall extends MethodCall {
* A call to `HttpSecurity.requestMatchers` method with lambda argument
* `RequestMatcher.toAnyEndpoint()`.
*/
-class RequestMatchersCall extends MethodCall {
+private class RequestMatchersCall extends MethodCall {
RequestMatchersCall() {
this.getMethod().hasName("requestMatchers") and
this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
@@ -72,86 +28,62 @@ class RequestMatchersCall extends MethodCall {
}
}
-/** A call to `HttpSecurity.authorizeRequests` method. */
-class AuthorizeRequestsCall extends MethodCall {
- AuthorizeRequestsCall() {
- this.getMethod().hasName("authorizeRequests") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
- }
-}
-
-/** A call to `AuthorizedUrl.permitAll` method. */
-class PermitAllCall extends MethodCall {
- PermitAllCall() {
- this.getMethod().hasName("permitAll") and
- this.getMethod().getDeclaringType() instanceof TypeAuthorizedUrl
- }
-
- /** Holds if `permitAll` is called on request(s) mapped to actuator endpoint(s). */
- predicate permitsSpringBootActuators() {
- exists(AuthorizeRequestsCall authorizeRequestsCall |
- // .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
- authorizeRequestsCall.getQualifier() instanceof RequestMatcherCall
- or
- // .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
- authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall
- |
- // [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
- // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
- authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and
- (
- this.getQualifier() instanceof AnyRequestCall or
- this.getQualifier() instanceof RegistryRequestMatchersCall
- )
- or
- // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
- // [...].authorizeRequests().anyRequest().permitAll()
- authorizeRequestsCall.getNumArgument() = 0 and
- exists(RegistryRequestMatchersCall registryRequestMatchersCall |
- registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
- this.getQualifier() = registryRequestMatchersCall
- )
- or
- exists(AnyRequestCall anyRequestCall |
- anyRequestCall.getQualifier() = authorizeRequestsCall and
- this.getQualifier() = anyRequestCall
- )
- )
- or
- exists(AuthorizeRequestsCall authorizeRequestsCall |
- // http.authorizeRequests([...]).[...]
- authorizeRequestsCall.getQualifier() instanceof VarAccess
- |
- // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
- authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and
- this.getQualifier() instanceof RegistryRequestMatchersCall
- or
- // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
- authorizeRequestsCall.getNumArgument() = 0 and
- exists(RegistryRequestMatchersCall registryRequestMatchersCall |
- registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
- this.getQualifier() = registryRequestMatchersCall
- )
- )
- }
-}
-
-/** A call to `AbstractRequestMatcherRegistry.anyRequest` method. */
-class AnyRequestCall extends MethodCall {
- AnyRequestCall() {
- this.getMethod().hasName("anyRequest") and
- this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry
- }
-}
-
/**
* A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument
* `RequestMatcher.toAnyEndpoint()`.
*/
-class RegistryRequestMatchersCall extends MethodCall {
+private class RegistryRequestMatchersCall extends MethodCall {
RegistryRequestMatchersCall() {
this.getMethod().hasName("requestMatchers") and
this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and
this.getAnArgument() instanceof ToAnyEndpointCall
}
}
+
+/** Holds if `permitAllCall` is called on request(s) mapped to actuator endpoint(s). */
+predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
+ exists(AuthorizeRequestsCall authorizeRequestsCall |
+ // .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
+ authorizeRequestsCall.getQualifier() instanceof RequestMatcherCall
+ or
+ // .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
+ authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall
+ |
+ // [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
+ // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
+ authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
+ (
+ permitAllCall.getQualifier() instanceof AnyRequestCall or
+ permitAllCall.getQualifier() instanceof RegistryRequestMatchersCall
+ )
+ or
+ // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
+ // [...].authorizeRequests().anyRequest().permitAll()
+ authorizeRequestsCall.getNumArgument() = 0 and
+ exists(RegistryRequestMatchersCall registryRequestMatchersCall |
+ registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
+ permitAllCall.getQualifier() = registryRequestMatchersCall
+ )
+ or
+ exists(AnyRequestCall anyRequestCall |
+ anyRequestCall.getQualifier() = authorizeRequestsCall and
+ permitAllCall.getQualifier() = anyRequestCall
+ )
+ )
+ or
+ exists(AuthorizeRequestsCall authorizeRequestsCall |
+ // http.authorizeRequests([...]).[...]
+ authorizeRequestsCall.getQualifier() instanceof VarAccess
+ |
+ // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
+ authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
+ permitAllCall.getQualifier() instanceof RegistryRequestMatchersCall
+ or
+ // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
+ authorizeRequestsCall.getNumArgument() = 0 and
+ exists(RegistryRequestMatchersCall registryRequestMatchersCall |
+ registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
+ permitAllCall.getQualifier() = registryRequestMatchersCall
+ )
+ )
+}
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
index c74c34284233..bac0a72e1441 100644
--- a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
+++ b/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
@@ -12,8 +12,9 @@
*/
import java
+import semmle.code.java.frameworks.spring.SpringSecurity
import semmle.code.java.security.SpringBootActuatorsQuery
from PermitAllCall permitAllCall
-where permitAllCall.permitsSpringBootActuators()
+where permitsSpringBootActuators(permitAllCall)
select permitAllCall, "Unauthenticated access to Spring Boot actuator is allowed."
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql
index f397fdb79aab..87044bb74dbc 100644
--- a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql
@@ -1,4 +1,5 @@
import java
+import semmle.code.java.frameworks.spring.SpringSecurity
import semmle.code.java.security.SpringBootActuatorsQuery
import utils.test.InlineExpectationsTest
@@ -7,7 +8,7 @@ module SpringBootActuatorsTest implements TestSig {
predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasExposedSpringBootActuator" and
- exists(PermitAllCall permitAllCall | permitAllCall.permitsSpringBootActuators() |
+ exists(PermitAllCall permitAllCall | permitsSpringBootActuators(permitAllCall) |
permitAllCall.getLocation() = location and
element = permitAllCall.toString() and
value = ""
From b2469ff8baa1d249efc5b411443027b847b17ad5 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 20 Feb 2025 16:58:42 -0500
Subject: [PATCH 07/17] Java: add APIs and tests for more recent Spring
versions: authorizeHttpRequests, AuthorizeHttpRequestsConfigurer,
securityMatcher(s)
---
.../java/frameworks/spring/SpringSecurity.qll | 29 +++-
.../security/SpringBootActuatorsQuery.qll | 85 +++++++--
.../CWE-016/SpringBootActuatorsTest.java | 163 ++++++++++++++++++
.../annotation/web/builders/HttpSecurity.java | 28 +++
.../AuthorizeHttpRequestsConfigurer.java | 18 ++
5 files changed, 306 insertions(+), 17 deletions(-)
create mode 100644 java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
index 3f11cc8d3ec8..a8de07ca5bc7 100644
--- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
@@ -15,12 +15,17 @@ class TypeHttpSecurity extends Class {
/**
* The class
- * `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer`.
+ * `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer$AuthorizedUrl`
+ * or the class
+ * `org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer$AuthorizedUrl`.
*/
class TypeAuthorizedUrl extends Class {
TypeAuthorizedUrl() {
this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
- "ExpressionUrlAuthorizationConfigurer$AuthorizedUrl<>")
+ [
+ "ExpressionUrlAuthorizationConfigurer$AuthorizedUrl<>",
+ "AuthorizeHttpRequestsConfigurer$AuthorizedUrl<>"
+ ])
}
}
@@ -34,7 +39,12 @@ class TypeAbstractRequestMatcherRegistry extends Class {
}
}
-/** A call to `HttpSecurity.authorizeRequests` method. */
+/**
+ * A call to `HttpSecurity.authorizeRequests` method.
+ *
+ * Note: this API is deprecated and scheduled for removal
+ * in Spring Security 7.0.
+ */
class AuthorizeRequestsCall extends MethodCall {
AuthorizeRequestsCall() {
this.getMethod().hasName("authorizeRequests") and
@@ -42,6 +52,19 @@ class AuthorizeRequestsCall extends MethodCall {
}
}
+/**
+ * A call to `HttpSecurity.authorizeHttpRequests` method.
+ *
+ * Note: the no-argument version of this API is deprecated
+ * and scheduled for removal in Spring Security 7.0.
+ */
+class AuthorizeHttpRequestsCall extends MethodCall {
+ AuthorizeHttpRequestsCall() {
+ this.getMethod().hasName("authorizeHttpRequests") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ }
+}
+
/** A call to `AuthorizedUrl.permitAll` method. */
class PermitAllCall extends MethodCall {
PermitAllCall() {
diff --git a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
index 6ccfa39f3d63..f3635912d5a4 100644
--- a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
@@ -6,7 +6,7 @@ private import semmle.code.java.frameworks.spring.SpringBoot
/**
* A call to `HttpSecurity.requestMatcher` method with argument
- * `RequestMatcher.toAnyEndpoint()`.
+ * `EndpointRequest.toAnyEndpoint()`.
*/
private class RequestMatcherCall extends MethodCall {
RequestMatcherCall() {
@@ -18,7 +18,7 @@ private class RequestMatcherCall extends MethodCall {
/**
* A call to `HttpSecurity.requestMatchers` method with lambda argument
- * `RequestMatcher.toAnyEndpoint()`.
+ * `EndpointRequest.toAnyEndpoint()`.
*/
private class RequestMatchersCall extends MethodCall {
RequestMatchersCall() {
@@ -40,18 +40,75 @@ private class RegistryRequestMatchersCall extends MethodCall {
}
}
+/**
+ * A call to `HttpSecurity.securityMatcher` method with argument
+ * `EndpointRequest.toAnyEndpoint()`.
+ */
+private class SecurityMatcherCall extends MethodCall {
+ SecurityMatcherCall() {
+ this.getMethod().hasName("securityMatcher") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
+ this.getArgument(0) instanceof ToAnyEndpointCall
+ }
+}
+
+/**
+ * A call to `HttpSecurity.securityMatchers` method with lambda argument
+ * `EndpointRequest.toAnyEndpoint()`.
+ */
+private class SecurityMatchersCall extends MethodCall {
+ SecurityMatchersCall() {
+ this.getMethod().hasName("securityMatchers") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
+ this.getArgument(0).(LambdaExpr).getExprBody() instanceof ToAnyEndpointCall
+ }
+}
+
+/**
+ * A call to a method that authorizes requests, e.g. `authorizeRequests` or
+ * `authorizeHttpRequests`.
+ */
+private class AuthorizeCall extends MethodCall {
+ AuthorizeCall() {
+ this instanceof AuthorizeRequestsCall or
+ this instanceof AuthorizeHttpRequestsCall
+ }
+}
+
+/**
+ * A call to a matcher method with argument
+ * `EndpointRequest.toAnyEndpoint()`.
+ */
+private class MatcherCall extends MethodCall {
+ MatcherCall() {
+ this instanceof RequestMatcherCall or
+ this instanceof SecurityMatcherCall
+ }
+}
+
+/**
+ * A call to a matchers method with argument
+ * `EndpointRequest.toAnyEndpoint()`.
+ */
+private class MatchersCall extends MethodCall {
+ MatchersCall() {
+ this instanceof RequestMatchersCall or
+ this instanceof SecurityMatchersCall
+ }
+}
+
/** Holds if `permitAllCall` is called on request(s) mapped to actuator endpoint(s). */
predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
- exists(AuthorizeRequestsCall authorizeRequestsCall |
+ exists(AuthorizeCall authorizeCall |
// .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
- authorizeRequestsCall.getQualifier() instanceof RequestMatcherCall
+ authorizeCall.getQualifier() instanceof MatcherCall
or
// .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
- authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall
+ authorizeCall.getQualifier() instanceof MatchersCall
|
// [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
- authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
+ authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
(
permitAllCall.getQualifier() instanceof AnyRequestCall or
permitAllCall.getQualifier() instanceof RegistryRequestMatchersCall
@@ -59,30 +116,30 @@ predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
or
// [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
// [...].authorizeRequests().anyRequest().permitAll()
- authorizeRequestsCall.getNumArgument() = 0 and
+ authorizeCall.getNumArgument() = 0 and
exists(RegistryRequestMatchersCall registryRequestMatchersCall |
- registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
+ registryRequestMatchersCall.getQualifier() = authorizeCall and
permitAllCall.getQualifier() = registryRequestMatchersCall
)
or
exists(AnyRequestCall anyRequestCall |
- anyRequestCall.getQualifier() = authorizeRequestsCall and
+ anyRequestCall.getQualifier() = authorizeCall and
permitAllCall.getQualifier() = anyRequestCall
)
)
or
- exists(AuthorizeRequestsCall authorizeRequestsCall |
+ exists(AuthorizeCall authorizeCall |
// http.authorizeRequests([...]).[...]
- authorizeRequestsCall.getQualifier() instanceof VarAccess
+ authorizeCall.getQualifier() instanceof VarAccess
|
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
- authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
+ authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
permitAllCall.getQualifier() instanceof RegistryRequestMatchersCall
or
// [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
- authorizeRequestsCall.getNumArgument() = 0 and
+ authorizeCall.getNumArgument() = 0 and
exists(RegistryRequestMatchersCall registryRequestMatchersCall |
- registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
+ registryRequestMatchersCall.getQualifier() = authorizeCall and
permitAllCall.getQualifier() = registryRequestMatchersCall
)
)
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
index 71856f5c1a92..fbe9d2e6e5cd 100644
--- a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
@@ -2,6 +2,7 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
public class SpringBootActuatorsTest {
+ // Spring security version 5.2.3 used `authorizeRequests` and `requestMatcher(s)`
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest().permitAll()); // $ hasExposedSpringBootActuator
}
@@ -101,4 +102,166 @@ protected void configureOkNoPermitAll6(HttpSecurity http) throws Exception {
protected void configureOkNoPermitAll7(HttpSecurity http) throws Exception {
http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest();
}
+
+ // Spring security version 5.5.0 introduced `authorizeHttpRequests`
+ protected void configure_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests(requests -> requests.anyRequest().permitAll()); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure2_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure3_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure4_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest().permitAll(); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure5_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure6_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure7_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest().permitAll(); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configureOk3_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests().anyRequest().permitAll();
+ }
+
+ protected void configureOk4_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
+ }
+
+ protected void configureOkSafeEndpoints1_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.to("health", "info")).authorizeHttpRequests(requests -> requests.anyRequest().permitAll());
+ }
+
+ protected void configureOkSafeEndpoints2_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.to("health")).authorizeHttpRequests().requestMatchers(EndpointRequest.to("health")).permitAll();
+ }
+
+ protected void configureOkSafeEndpoints3_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeHttpRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll();
+ }
+
+ protected void configureOkSafeEndpoints4_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.to("health", "info")).authorizeHttpRequests().anyRequest().permitAll();
+ }
+
+ protected void configureOkSafeEndpoints5_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll();
+ }
+
+ protected void configureOkSafeEndpoints6_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests(requests -> requests.requestMatchers(EndpointRequest.to("health", "info")).permitAll());
+ }
+
+ protected void configureOkSafeEndpoints7_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeHttpRequests().anyRequest().permitAll();
+ }
+
+ protected void configureOkNoPermitAll1_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests(requests -> requests.anyRequest());
+ }
+
+ protected void configureOkNoPermitAll2_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOkNoPermitAll3_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOkNoPermitAll4_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest();
+ }
+
+ protected void configureOkNoPermitAll5_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOkNoPermitAll6_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()));
+ }
+
+ protected void configureOkNoPermitAll7_authorizeHttpRequests(HttpSecurity http) throws Exception {
+ http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest();
+ }
+
+ // Spring security version 5.8.0 introduced `securityMatcher(s)`
+ protected void configure_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests(requests -> requests.anyRequest().permitAll()); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure2_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure3_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure4_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest().permitAll(); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configure7_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest().permitAll(); // $ hasExposedSpringBootActuator
+ }
+
+ protected void configureOk1_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOk2_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatchers().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOkSafeEndpoints1_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.to("health", "info")).authorizeHttpRequests(requests -> requests.anyRequest().permitAll());
+ }
+
+ protected void configureOkSafeEndpoints2_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.to("health")).authorizeHttpRequests().requestMatchers(EndpointRequest.to("health")).permitAll();
+ }
+
+ protected void configureOkSafeEndpoints3_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeHttpRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll();
+ }
+
+ protected void configureOkSafeEndpoints4_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.to("health", "info")).authorizeHttpRequests().anyRequest().permitAll();
+ }
+
+ protected void configureOkSafeEndpoints7_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeHttpRequests().anyRequest().permitAll();
+ }
+
+ protected void configureOkNoPermitAll1_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests(requests -> requests.anyRequest());
+ }
+
+ protected void configureOkNoPermitAll2_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOkNoPermitAll3_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
+ }
+
+ protected void configureOkNoPermitAll4_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest();
+ }
+
+ protected void configureOkNoPermitAll7_securityMatchers(HttpSecurity http) throws Exception {
+ http.securityMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest();
+ }
}
diff --git a/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
index 3dbe33cdeb9c..f900fc74d2fd 100644
--- a/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
+++ b/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
@@ -9,6 +9,7 @@
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
+import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder
@@ -18,6 +19,14 @@ public HttpSecurity requestMatcher(RequestMatcher requestMatcher) {
return this;
}
+ public HttpSecurity securityMatcher(RequestMatcher requestMatcher) {
+ return this;
+ }
+
+ public HttpSecurity securityMatcher(String... patterns) {
+ return this;
+ }
+
public HttpSecurity authorizeRequests(
Customizer.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer)
throws Exception {
@@ -29,6 +38,17 @@ public ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrl
return null;
}
+ public HttpSecurity authorizeHttpRequests(
+ Customizer.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
+ throws Exception {
+ return this;
+ }
+
+ public AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry authorizeHttpRequests()
+ throws Exception {
+ return null;
+ }
+
public HttpSecurity requestMatchers(Customizer requestMatcherCustomizer) {
return this;
}
@@ -37,6 +57,14 @@ public RequestMatcherConfigurer requestMatchers() {
return null;
}
+ public HttpSecurity securityMatchers(Customizer requestMatcherCustomizer) {
+ return this;
+ }
+
+ public RequestMatcherConfigurer securityMatchers() {
+ return null;
+ }
+
public CsrfConfigurer csrf() {
return null;
}
diff --git a/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java b/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
new file mode 100644
index 000000000000..f27a2aeef5ae
--- /dev/null
+++ b/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
@@ -0,0 +1,18 @@
+package org.springframework.security.config.annotation.web.configurers;
+
+import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
+import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
+
+public final class AuthorizeHttpRequestsConfigurer>
+ extends AbstractHttpConfigurer, H> {
+
+ public final class AuthorizationManagerRequestMatcherRegistry extends
+ AbstractRequestMatcherRegistry {
+ }
+
+ public class AuthorizedUrl {
+ public AuthorizationManagerRequestMatcherRegistry permitAll() {
+ return null;
+ }
+ }
+}
From 9e51b014d24a5ce2c5931cb7a46888a966e6eb8c Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Sun, 23 Feb 2025 23:28:10 -0500
Subject: [PATCH 08/17] Java: handle example in Spring docs
---
.../code/java/security/SpringBootActuatorsQuery.qll | 9 +++++++++
.../security/CWE-016/SpringBootActuatorsTest.java | 7 +++++++
2 files changed, 16 insertions(+)
diff --git a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
index f3635912d5a4..d58955a4037e 100644
--- a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
@@ -142,5 +142,14 @@ predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
registryRequestMatchersCall.getQualifier() = authorizeCall and
permitAllCall.getQualifier() = registryRequestMatchersCall
)
+ or
+ exists(Variable v, MatcherCall matcherCall |
+ // http.securityMatcher(EndpointRequest.toAnyEndpoint());
+ // http.authorizeRequests([...].permitAll())
+ v.getAnAccess() = authorizeCall.getQualifier() and
+ v.getAnAccess() = matcherCall.getQualifier() and
+ authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
+ permitAllCall.getQualifier() instanceof AnyRequestCall
+ )
)
}
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
index fbe9d2e6e5cd..516c53e4b206 100644
--- a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
@@ -264,4 +264,11 @@ protected void configureOkNoPermitAll4_securityMatchers(HttpSecurity http) throw
protected void configureOkNoPermitAll7_securityMatchers(HttpSecurity http) throws Exception {
http.securityMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest();
}
+
+ // Spring doc example
+ // https://docs.spring.io/spring-boot/reference/actuator/endpoints.html#actuator.endpoints.security
+ public void securityFilterChain(HttpSecurity http) throws Exception {
+ http.securityMatcher(EndpointRequest.toAnyEndpoint());
+ http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll()); // $ hasExposedSpringBootActuator
+ }
}
From f65a5b9a6625c43c9cc4ce8fd9e1982455627325 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Sun, 23 Feb 2025 23:37:05 -0500
Subject: [PATCH 09/17] Java: add test for qhelp good example
---
.../security/CWE-016/SpringBootActuatorsTest.java | 7 +++++++
.../web/configurers/AuthorizeHttpRequestsConfigurer.java | 4 ++++
.../configurers/ExpressionUrlAuthorizationConfigurer.java | 4 ++++
3 files changed, 15 insertions(+)
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
index 516c53e4b206..2f54cd442b50 100644
--- a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
+++ b/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
@@ -271,4 +271,11 @@ public void securityFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher(EndpointRequest.toAnyEndpoint());
http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll()); // $ hasExposedSpringBootActuator
}
+
+ // QHelp Good example
+ protected void configureQhelpGood(HttpSecurity http) throws Exception {
+ // GOOD: only users with ENDPOINT_ADMIN role are allowed to access the actuator endpoints
+ http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
+ requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
+ }
}
diff --git a/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java b/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
index f27a2aeef5ae..ff54fc7e3d11 100644
--- a/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
+++ b/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
@@ -14,5 +14,9 @@ public class AuthorizedUrl {
public AuthorizationManagerRequestMatcherRegistry permitAll() {
return null;
}
+
+ public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
+ return null;
+ }
}
}
diff --git a/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java b/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java
index 012997dc5024..be4e14019779 100644
--- a/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java
+++ b/java/ql/test/stubs/springframework-5.3.8/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java
@@ -12,5 +12,9 @@ public class AuthorizedUrl {
public ExpressionInterceptUrlRegistry permitAll() {
return null;
}
+
+ public ExpressionInterceptUrlRegistry hasRole(String role) {
+ return null;
+ }
}
}
From 6fe7c7a2334dff8db2df115aa02751cf399f3645 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Feb 2025 11:44:13 -0500
Subject: [PATCH 10/17] Java: some refactoring
---
.../java/frameworks/spring/SpringSecurity.qll | 54 ++++++++++--
.../security/SpringBootActuatorsQuery.qll | 87 +++++--------------
2 files changed, 69 insertions(+), 72 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
index a8de07ca5bc7..901ed06270e4 100644
--- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
@@ -40,9 +40,9 @@ class TypeAbstractRequestMatcherRegistry extends Class {
}
/**
- * A call to `HttpSecurity.authorizeRequests` method.
+ * A call to the `HttpSecurity.authorizeRequests` method.
*
- * Note: this API is deprecated and scheduled for removal
+ * Note: this method is deprecated and scheduled for removal
* in Spring Security 7.0.
*/
class AuthorizeRequestsCall extends MethodCall {
@@ -53,9 +53,9 @@ class AuthorizeRequestsCall extends MethodCall {
}
/**
- * A call to `HttpSecurity.authorizeHttpRequests` method.
+ * A call to the `HttpSecurity.authorizeHttpRequests` method.
*
- * Note: the no-argument version of this API is deprecated
+ * Note: the no-argument version of this method is deprecated
* and scheduled for removal in Spring Security 7.0.
*/
class AuthorizeHttpRequestsCall extends MethodCall {
@@ -65,7 +65,49 @@ class AuthorizeHttpRequestsCall extends MethodCall {
}
}
-/** A call to `AuthorizedUrl.permitAll` method. */
+/**
+ * A call to the `HttpSecurity.requestMatcher` method.
+ *
+ * Note: this method was removed in Spring Security 6.0.
+ * It was replaced by `securityMatcher`.
+ */
+class RequestMatcherCall extends MethodCall {
+ RequestMatcherCall() {
+ this.getMethod().hasName("requestMatcher") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ }
+}
+
+/**
+ * A call to the `HttpSecurity.requestMatchers` method.
+ *
+ * Note: this method was removed in Spring Security 6.0.
+ * It was replaced by `securityMatchers`.
+ */
+class RequestMatchersCall extends MethodCall {
+ RequestMatchersCall() {
+ this.getMethod().hasName("requestMatchers") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ }
+}
+
+/** A call to the `HttpSecurity.securityMatcher` method. */
+class SecurityMatcherCall extends MethodCall {
+ SecurityMatcherCall() {
+ this.getMethod().hasName("securityMatcher") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ }
+}
+
+/** A call to the `HttpSecurity.securityMatchers` method. */
+class SecurityMatchersCall extends MethodCall {
+ SecurityMatchersCall() {
+ this.getMethod().hasName("securityMatchers") and
+ this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ }
+}
+
+/** A call to the `AuthorizedUrl.permitAll` method. */
class PermitAllCall extends MethodCall {
PermitAllCall() {
this.getMethod().hasName("permitAll") and
@@ -73,7 +115,7 @@ class PermitAllCall extends MethodCall {
}
}
-/** A call to `AbstractRequestMatcherRegistry.anyRequest` method. */
+/** A call to the `AbstractRequestMatcherRegistry.anyRequest` method. */
class AnyRequestCall extends MethodCall {
AnyRequestCall() {
this.getMethod().hasName("anyRequest") and
diff --git a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
index d58955a4037e..76607ed794a9 100644
--- a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
@@ -5,32 +5,36 @@ private import semmle.code.java.frameworks.spring.SpringSecurity
private import semmle.code.java.frameworks.spring.SpringBoot
/**
- * A call to `HttpSecurity.requestMatcher` method with argument
+ * A call to an `HttpSecurity` matcher method with argument
* `EndpointRequest.toAnyEndpoint()`.
*/
-private class RequestMatcherCall extends MethodCall {
- RequestMatcherCall() {
- this.getMethod().hasName("requestMatcher") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
+private class HttpSecurityMatcherCall extends MethodCall {
+ HttpSecurityMatcherCall() {
+ (
+ this instanceof RequestMatcherCall or
+ this instanceof SecurityMatcherCall
+ ) and
this.getArgument(0) instanceof ToAnyEndpointCall
}
}
/**
- * A call to `HttpSecurity.requestMatchers` method with lambda argument
- * `EndpointRequest.toAnyEndpoint()`.
+ * A call to an `HttpSecurity` matchers method with lambda
+ * argument `EndpointRequest.toAnyEndpoint()`.
*/
-private class RequestMatchersCall extends MethodCall {
- RequestMatchersCall() {
- this.getMethod().hasName("requestMatchers") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
+private class HttpSecurityMatchersCall extends MethodCall {
+ HttpSecurityMatchersCall() {
+ (
+ this instanceof RequestMatchersCall or
+ this instanceof SecurityMatchersCall
+ ) and
this.getArgument(0).(LambdaExpr).getExprBody() instanceof ToAnyEndpointCall
}
}
/**
- * A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument
- * `RequestMatcher.toAnyEndpoint()`.
+ * A call to an `AbstractRequestMatcherRegistry.requestMatchers` method with
+ * argument `EndpointRequest.toAnyEndpoint()`.
*/
private class RegistryRequestMatchersCall extends MethodCall {
RegistryRequestMatchersCall() {
@@ -40,34 +44,7 @@ private class RegistryRequestMatchersCall extends MethodCall {
}
}
-/**
- * A call to `HttpSecurity.securityMatcher` method with argument
- * `EndpointRequest.toAnyEndpoint()`.
- */
-private class SecurityMatcherCall extends MethodCall {
- SecurityMatcherCall() {
- this.getMethod().hasName("securityMatcher") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
- this.getArgument(0) instanceof ToAnyEndpointCall
- }
-}
-
-/**
- * A call to `HttpSecurity.securityMatchers` method with lambda argument
- * `EndpointRequest.toAnyEndpoint()`.
- */
-private class SecurityMatchersCall extends MethodCall {
- SecurityMatchersCall() {
- this.getMethod().hasName("securityMatchers") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
- this.getArgument(0).(LambdaExpr).getExprBody() instanceof ToAnyEndpointCall
- }
-}
-
-/**
- * A call to a method that authorizes requests, e.g. `authorizeRequests` or
- * `authorizeHttpRequests`.
- */
+/** A call to an `HttpSecurity` method that authorizes requests. */
private class AuthorizeCall extends MethodCall {
AuthorizeCall() {
this instanceof AuthorizeRequestsCall or
@@ -75,36 +52,14 @@ private class AuthorizeCall extends MethodCall {
}
}
-/**
- * A call to a matcher method with argument
- * `EndpointRequest.toAnyEndpoint()`.
- */
-private class MatcherCall extends MethodCall {
- MatcherCall() {
- this instanceof RequestMatcherCall or
- this instanceof SecurityMatcherCall
- }
-}
-
-/**
- * A call to a matchers method with argument
- * `EndpointRequest.toAnyEndpoint()`.
- */
-private class MatchersCall extends MethodCall {
- MatchersCall() {
- this instanceof RequestMatchersCall or
- this instanceof SecurityMatchersCall
- }
-}
-
/** Holds if `permitAllCall` is called on request(s) mapped to actuator endpoint(s). */
predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
exists(AuthorizeCall authorizeCall |
// .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
- authorizeCall.getQualifier() instanceof MatcherCall
+ authorizeCall.getQualifier() instanceof HttpSecurityMatcherCall
or
// .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
- authorizeCall.getQualifier() instanceof MatchersCall
+ authorizeCall.getQualifier() instanceof HttpSecurityMatchersCall
|
// [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
@@ -143,7 +98,7 @@ predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
permitAllCall.getQualifier() = registryRequestMatchersCall
)
or
- exists(Variable v, MatcherCall matcherCall |
+ exists(Variable v, HttpSecurityMatcherCall matcherCall |
// http.securityMatcher(EndpointRequest.toAnyEndpoint());
// http.authorizeRequests([...].permitAll())
v.getAnAccess() = authorizeCall.getQualifier() and
From 53cb30dcd00a7f7a1d32f2694781ecc598b8a9af Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Feb 2025 15:48:28 -0500
Subject: [PATCH 11/17] Java: update metadata, move from CWE-016 to CWE-200
---
.../CWE/{CWE-016 => CWE-200}/SpringBootActuators.java | 0
.../CWE/{CWE-016 => CWE-200}/SpringBootActuators.qhelp | 0
.../Security/CWE/{CWE-016 => CWE-200}/SpringBootActuators.ql | 4 ++--
.../SpringBootActuators}/SpringBootActuatorsTest.expected | 0
.../tests/SpringBootActuators}/SpringBootActuatorsTest.java | 0
.../tests/SpringBootActuators}/SpringBootActuatorsTest.ql | 0
.../semmle/tests/SpringBootActuators}/options | 2 +-
7 files changed, 3 insertions(+), 3 deletions(-)
rename java/ql/src/Security/CWE/{CWE-016 => CWE-200}/SpringBootActuators.java (100%)
rename java/ql/src/Security/CWE/{CWE-016 => CWE-200}/SpringBootActuators.qhelp (100%)
rename java/ql/src/Security/CWE/{CWE-016 => CWE-200}/SpringBootActuators.ql (91%)
rename java/ql/test/query-tests/security/{CWE-016 => CWE-200/semmle/tests/SpringBootActuators}/SpringBootActuatorsTest.expected (100%)
rename java/ql/test/query-tests/security/{CWE-016 => CWE-200/semmle/tests/SpringBootActuators}/SpringBootActuatorsTest.java (100%)
rename java/ql/test/query-tests/security/{CWE-016 => CWE-200/semmle/tests/SpringBootActuators}/SpringBootActuatorsTest.ql (100%)
rename java/ql/test/query-tests/security/{CWE-016 => CWE-200/semmle/tests/SpringBootActuators}/options (62%)
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.java b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.java
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-016/SpringBootActuators.java
rename to java/ql/src/Security/CWE/CWE-200/SpringBootActuators.java
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qhelp b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-016/SpringBootActuators.qhelp
rename to java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp
diff --git a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
similarity index 91%
rename from java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
rename to java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
index bac0a72e1441..97d9b506f785 100644
--- a/java/ql/src/Security/CWE/CWE-016/SpringBootActuators.ql
+++ b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
@@ -4,11 +4,11 @@
* or even to remote code execution.
* @kind problem
* @problem.severity error
+ * @security-severity 6.5
* @precision high
* @id java/spring-boot-exposed-actuators
* @tags security
- * experimental
- * external/cwe/cwe-16
+ * external/cwe/cwe-200
*/
import java
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.expected b/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.expected
similarity index 100%
rename from java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.expected
rename to java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.expected
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java b/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.java
similarity index 100%
rename from java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.java
rename to java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.java
diff --git a/java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql b/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.ql
similarity index 100%
rename from java/ql/test/query-tests/security/CWE-016/SpringBootActuatorsTest.ql
rename to java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.ql
diff --git a/java/ql/test/query-tests/security/CWE-016/options b/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/options
similarity index 62%
rename from java/ql/test/query-tests/security/CWE-016/options
rename to java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/options
index 38d1d754b69c..161a6ddf23d7 100644
--- a/java/ql/test/query-tests/security/CWE-016/options
+++ b/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/options
@@ -1 +1 @@
-//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.3.8
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../../stubs/springframework-5.3.8
From 26e396732a334d02de6f56b013c62f6c3ae20501 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Feb 2025 16:43:19 -0500
Subject: [PATCH 12/17] Java: edit qhelp
---
.../CWE/CWE-200/SpringBootActuators.java | 33 ++++++++++---------
.../CWE/CWE-200/SpringBootActuators.qhelp | 27 +++++++--------
.../SpringBootActuatorsTest.java | 18 +++++-----
3 files changed, 39 insertions(+), 39 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.java b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.java
index 5aec49837cac..5f61127db288 100644
--- a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.java
+++ b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.java
@@ -1,22 +1,25 @@
@Configuration(proxyBeanMethods = false)
-public class SpringBootActuators extends WebSecurityConfigurerAdapter {
+public class CustomSecurityConfiguration {
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ // BAD: Unauthenticated access to Spring Boot actuator endpoints is allowed
+ http.securityMatcher(EndpointRequest.toAnyEndpoint());
+ http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());
+ return http.build();
+ }
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- // BAD: Unauthenticated access to Spring Boot actuator endpoints is allowed
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
- requests.anyRequest().permitAll());
- }
}
@Configuration(proxyBeanMethods = false)
-public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
+public class CustomSecurityConfiguration {
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ // GOOD: only users with ENDPOINT_ADMIN role are allowed to access the actuator endpoints
+ http.securityMatcher(EndpointRequest.toAnyEndpoint());
+ http.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
+ return http.build();
+ }
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- // GOOD: only users with ENDPOINT_ADMIN role are allowed to access the actuator endpoints
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
- requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
- http.httpBasic();
- }
}
diff --git a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp
index 53ee653aaff3..15675545c42f 100644
--- a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp
+++ b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp
@@ -3,24 +3,24 @@
"qhelp.dtd">
-Spring Boot includes a number of additional features called actuators that let you monitor
-and interact with your web application. Exposing unprotected actuator endpoints via JXM or HTTP
-can, however, lead to information disclosure or even to remote code execution vulnerability.
+Spring Boot includes features called actuators that let you monitor and interact with your
+web application. Exposing unprotected actuator endpoints can lead to information disclosure or
+even to remote code execution.
-Since actuator endpoints may contain sensitive information, careful consideration should be
-given about when to expose them. You should take care to secure exposed HTTP endpoints in the same
-way that you would any other sensitive URL. If Spring Security is present, endpoints are secured by
-default using Spring Security’s content-negotiation strategy. If you wish to configure custom
-security for HTTP endpoints, for example, only allow users with a certain role to access them,
-Spring Boot provides some convenient RequestMatcher
objects that can be used in
-combination with Spring Security.
+Since actuator endpoints may contain sensitive information, carefully consider when to expose them,
+and secure them as you would any sensitive URL. Actuators are secured by default when using Spring
+Security without a custom configuration. If you wish to define a custom security configuration,
+consider only allowing users with certain roles access to the endpoints.
+
+
In the first example, the custom security configuration allows unauthenticated access to all
actuator endpoints. This may lead to sensitive information disclosure and should be avoided.
+
In the second example, only users with ENDPOINT_ADMIN
role are allowed to access
the actuator endpoints.
@@ -29,11 +29,8 @@ the actuator endpoints.
-Spring Boot documentation:
-Actuators.
-
-
-Exploiting Spring Boot Actuators
+Spring Boot Reference Documentation:
+Endpoints.
diff --git a/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.java b/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.java
index 2f54cd442b50..4b5d7614eef6 100644
--- a/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.java
+++ b/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.java
@@ -265,17 +265,17 @@ protected void configureOkNoPermitAll7_securityMatchers(HttpSecurity http) throw
http.securityMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeHttpRequests().anyRequest();
}
- // Spring doc example
- // https://docs.spring.io/spring-boot/reference/actuator/endpoints.html#actuator.endpoints.security
- public void securityFilterChain(HttpSecurity http) throws Exception {
- http.securityMatcher(EndpointRequest.toAnyEndpoint());
- http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll()); // $ hasExposedSpringBootActuator
- }
+ // QHelp Bad example
+ public void securityFilterChain1(HttpSecurity http) throws Exception {
+ // BAD: Unauthenticated access to Spring Boot actuator endpoints is allowed
+ http.securityMatcher(EndpointRequest.toAnyEndpoint());
+ http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll()); // $ hasExposedSpringBootActuator
+ }
// QHelp Good example
- protected void configureQhelpGood(HttpSecurity http) throws Exception {
+ public void securityFilterChain2(HttpSecurity http) throws Exception {
// GOOD: only users with ENDPOINT_ADMIN role are allowed to access the actuator endpoints
- http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
- requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
+ http.securityMatcher(EndpointRequest.toAnyEndpoint());
+ http.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
}
}
From c2e859c756ac3e2370c359cec9051e078d53c6f2 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Feb 2025 17:35:23 -0500
Subject: [PATCH 13/17] Java: add change note
---
.../change-notes/2025-02-24-spring-boot-actuators-promo.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md
diff --git a/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md b/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md
new file mode 100644
index 000000000000..0263df2c68c2
--- /dev/null
+++ b/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md
@@ -0,0 +1,4 @@
+---
+category: newQuery
+---
+* The query `java/spring-boot-exposed-actuators` has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally submitted as an experimental query [by @ggolawski](https://github.com/github/codeql/pull/2901).
From 746f022cfa614fa3bb6330ed0e142b8773003f36 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 4 Mar 2025 10:34:16 -0500
Subject: [PATCH 14/17] Java: add 'Spring' prefix to public class names
---
.../java/frameworks/spring/SpringBoot.qll | 10 ++--
.../java/frameworks/spring/SpringSecurity.qll | 60 +++++++++----------
.../security/SpringBootActuatorsQuery.qll | 28 ++++-----
.../CWE/CWE-200/SpringBootActuators.ql | 2 +-
4 files changed, 50 insertions(+), 50 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringBoot.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringBoot.qll
index 864fc4bfcaff..d77e4549e4e7 100644
--- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringBoot.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringBoot.qll
@@ -8,17 +8,17 @@ import java
/**
* The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`.
*/
-class TypeEndpointRequest extends Class {
- TypeEndpointRequest() {
+class SpringEndpointRequest extends Class {
+ SpringEndpointRequest() {
this.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
"EndpointRequest")
}
}
/** A call to `EndpointRequest.toAnyEndpoint` method. */
-class ToAnyEndpointCall extends MethodCall {
- ToAnyEndpointCall() {
+class SpringToAnyEndpointCall extends MethodCall {
+ SpringToAnyEndpointCall() {
this.getMethod().hasName("toAnyEndpoint") and
- this.getMethod().getDeclaringType() instanceof TypeEndpointRequest
+ this.getMethod().getDeclaringType() instanceof SpringEndpointRequest
}
}
diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
index 901ed06270e4..835b679d50a6 100644
--- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringSecurity.qll
@@ -6,8 +6,8 @@
import java
/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
-class TypeHttpSecurity extends Class {
- TypeHttpSecurity() {
+class SpringHttpSecurity extends Class {
+ SpringHttpSecurity() {
this.hasQualifiedName("org.springframework.security.config.annotation.web.builders",
"HttpSecurity")
}
@@ -19,8 +19,8 @@ class TypeHttpSecurity extends Class {
* or the class
* `org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer$AuthorizedUrl`.
*/
-class TypeAuthorizedUrl extends Class {
- TypeAuthorizedUrl() {
+class SpringAuthorizedUrl extends Class {
+ SpringAuthorizedUrl() {
this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
[
"ExpressionUrlAuthorizationConfigurer$AuthorizedUrl<>",
@@ -32,8 +32,8 @@ class TypeAuthorizedUrl extends Class {
/**
* The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
*/
-class TypeAbstractRequestMatcherRegistry extends Class {
- TypeAbstractRequestMatcherRegistry() {
+class SpringAbstractRequestMatcherRegistry extends Class {
+ SpringAbstractRequestMatcherRegistry() {
this.hasQualifiedName("org.springframework.security.config.annotation.web",
"AbstractRequestMatcherRegistry>")
}
@@ -45,10 +45,10 @@ class TypeAbstractRequestMatcherRegistry extends Class {
* Note: this method is deprecated and scheduled for removal
* in Spring Security 7.0.
*/
-class AuthorizeRequestsCall extends MethodCall {
- AuthorizeRequestsCall() {
+class SpringAuthorizeRequestsCall extends MethodCall {
+ SpringAuthorizeRequestsCall() {
this.getMethod().hasName("authorizeRequests") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
@@ -58,10 +58,10 @@ class AuthorizeRequestsCall extends MethodCall {
* Note: the no-argument version of this method is deprecated
* and scheduled for removal in Spring Security 7.0.
*/
-class AuthorizeHttpRequestsCall extends MethodCall {
- AuthorizeHttpRequestsCall() {
+class SpringAuthorizeHttpRequestsCall extends MethodCall {
+ SpringAuthorizeHttpRequestsCall() {
this.getMethod().hasName("authorizeHttpRequests") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
@@ -71,10 +71,10 @@ class AuthorizeHttpRequestsCall extends MethodCall {
* Note: this method was removed in Spring Security 6.0.
* It was replaced by `securityMatcher`.
*/
-class RequestMatcherCall extends MethodCall {
- RequestMatcherCall() {
+class SpringRequestMatcherCall extends MethodCall {
+ SpringRequestMatcherCall() {
this.getMethod().hasName("requestMatcher") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
@@ -84,41 +84,41 @@ class RequestMatcherCall extends MethodCall {
* Note: this method was removed in Spring Security 6.0.
* It was replaced by `securityMatchers`.
*/
-class RequestMatchersCall extends MethodCall {
- RequestMatchersCall() {
+class SpringRequestMatchersCall extends MethodCall {
+ SpringRequestMatchersCall() {
this.getMethod().hasName("requestMatchers") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
/** A call to the `HttpSecurity.securityMatcher` method. */
-class SecurityMatcherCall extends MethodCall {
- SecurityMatcherCall() {
+class SpringSecurityMatcherCall extends MethodCall {
+ SpringSecurityMatcherCall() {
this.getMethod().hasName("securityMatcher") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
/** A call to the `HttpSecurity.securityMatchers` method. */
-class SecurityMatchersCall extends MethodCall {
- SecurityMatchersCall() {
+class SpringSecurityMatchersCall extends MethodCall {
+ SpringSecurityMatchersCall() {
this.getMethod().hasName("securityMatchers") and
- this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
+ this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
/** A call to the `AuthorizedUrl.permitAll` method. */
-class PermitAllCall extends MethodCall {
- PermitAllCall() {
+class SpringPermitAllCall extends MethodCall {
+ SpringPermitAllCall() {
this.getMethod().hasName("permitAll") and
- this.getMethod().getDeclaringType() instanceof TypeAuthorizedUrl
+ this.getMethod().getDeclaringType() instanceof SpringAuthorizedUrl
}
}
/** A call to the `AbstractRequestMatcherRegistry.anyRequest` method. */
-class AnyRequestCall extends MethodCall {
- AnyRequestCall() {
+class SpringAnyRequestCall extends MethodCall {
+ SpringAnyRequestCall() {
this.getMethod().hasName("anyRequest") and
- this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry
+ this.getMethod().getDeclaringType() instanceof SpringAbstractRequestMatcherRegistry
}
}
diff --git a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
index 76607ed794a9..68c20adabdd1 100644
--- a/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/SpringBootActuatorsQuery.qll
@@ -11,10 +11,10 @@ private import semmle.code.java.frameworks.spring.SpringBoot
private class HttpSecurityMatcherCall extends MethodCall {
HttpSecurityMatcherCall() {
(
- this instanceof RequestMatcherCall or
- this instanceof SecurityMatcherCall
+ this instanceof SpringRequestMatcherCall or
+ this instanceof SpringSecurityMatcherCall
) and
- this.getArgument(0) instanceof ToAnyEndpointCall
+ this.getArgument(0) instanceof SpringToAnyEndpointCall
}
}
@@ -25,10 +25,10 @@ private class HttpSecurityMatcherCall extends MethodCall {
private class HttpSecurityMatchersCall extends MethodCall {
HttpSecurityMatchersCall() {
(
- this instanceof RequestMatchersCall or
- this instanceof SecurityMatchersCall
+ this instanceof SpringRequestMatchersCall or
+ this instanceof SpringSecurityMatchersCall
) and
- this.getArgument(0).(LambdaExpr).getExprBody() instanceof ToAnyEndpointCall
+ this.getArgument(0).(LambdaExpr).getExprBody() instanceof SpringToAnyEndpointCall
}
}
@@ -39,21 +39,21 @@ private class HttpSecurityMatchersCall extends MethodCall {
private class RegistryRequestMatchersCall extends MethodCall {
RegistryRequestMatchersCall() {
this.getMethod().hasName("requestMatchers") and
- this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and
- this.getAnArgument() instanceof ToAnyEndpointCall
+ this.getMethod().getDeclaringType() instanceof SpringAbstractRequestMatcherRegistry and
+ this.getAnArgument() instanceof SpringToAnyEndpointCall
}
}
/** A call to an `HttpSecurity` method that authorizes requests. */
private class AuthorizeCall extends MethodCall {
AuthorizeCall() {
- this instanceof AuthorizeRequestsCall or
- this instanceof AuthorizeHttpRequestsCall
+ this instanceof SpringAuthorizeRequestsCall or
+ this instanceof SpringAuthorizeHttpRequestsCall
}
}
/** Holds if `permitAllCall` is called on request(s) mapped to actuator endpoint(s). */
-predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
+predicate permitsSpringBootActuators(SpringPermitAllCall permitAllCall) {
exists(AuthorizeCall authorizeCall |
// .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
authorizeCall.getQualifier() instanceof HttpSecurityMatcherCall
@@ -65,7 +65,7 @@ predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
(
- permitAllCall.getQualifier() instanceof AnyRequestCall or
+ permitAllCall.getQualifier() instanceof SpringAnyRequestCall or
permitAllCall.getQualifier() instanceof RegistryRequestMatchersCall
)
or
@@ -77,7 +77,7 @@ predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
permitAllCall.getQualifier() = registryRequestMatchersCall
)
or
- exists(AnyRequestCall anyRequestCall |
+ exists(SpringAnyRequestCall anyRequestCall |
anyRequestCall.getQualifier() = authorizeCall and
permitAllCall.getQualifier() = anyRequestCall
)
@@ -104,7 +104,7 @@ predicate permitsSpringBootActuators(PermitAllCall permitAllCall) {
v.getAnAccess() = authorizeCall.getQualifier() and
v.getAnAccess() = matcherCall.getQualifier() and
authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
- permitAllCall.getQualifier() instanceof AnyRequestCall
+ permitAllCall.getQualifier() instanceof SpringAnyRequestCall
)
)
}
diff --git a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
index 97d9b506f785..5818713d058b 100644
--- a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
+++ b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
@@ -15,6 +15,6 @@ import java
import semmle.code.java.frameworks.spring.SpringSecurity
import semmle.code.java.security.SpringBootActuatorsQuery
-from PermitAllCall permitAllCall
+from SpringPermitAllCall permitAllCall
where permitsSpringBootActuators(permitAllCall)
select permitAllCall, "Unauthenticated access to Spring Boot actuator is allowed."
From 82062e2847a0ff3e360117b1e3d066baa1810db6 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 4 Mar 2025 11:15:00 -0500
Subject: [PATCH 15/17] Java: update test
---
.../semmle/tests/SpringBootActuators/SpringBootActuatorsTest.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.ql b/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.ql
index 87044bb74dbc..214886fce511 100644
--- a/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.ql
+++ b/java/ql/test/query-tests/security/CWE-200/semmle/tests/SpringBootActuators/SpringBootActuatorsTest.ql
@@ -8,7 +8,7 @@ module SpringBootActuatorsTest implements TestSig {
predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasExposedSpringBootActuator" and
- exists(PermitAllCall permitAllCall | permitsSpringBootActuators(permitAllCall) |
+ exists(SpringPermitAllCall permitAllCall | permitsSpringBootActuators(permitAllCall) |
permitAllCall.getLocation() = location and
element = permitAllCall.toString() and
value = ""
From 0eec951218346a6cef5be8521acc97f5a3d51d70 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 5 Mar 2025 08:55:51 -0500
Subject: [PATCH 16/17] Java: update change note to mention removal from
Community Packs
---
.../src/change-notes/2025-02-24-spring-boot-actuators-promo.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md b/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md
index 0263df2c68c2..f66c6b33b583 100644
--- a/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md
+++ b/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md
@@ -1,4 +1,4 @@
---
category: newQuery
---
-* The query `java/spring-boot-exposed-actuators` has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally submitted as an experimental query [by @ggolawski](https://github.com/github/codeql/pull/2901).
+* The query `java/spring-boot-exposed-actuators` has been promoted from experimental to the main query pack. Its results will now appear by default, and it will be removed from the [CodeQL Community Packs](https://github.com/GitHubSecurityLab/CodeQL-Community-Packs). This query was originally submitted as an experimental query [by @ggolawski](https://github.com/github/codeql/pull/2901).
From ad63dd946c6b971966fd9111f3bf7706297d1cc9 Mon Sep 17 00:00:00 2001
From: Jami <57204504+jcogs33@users.noreply.github.com>
Date: Mon, 10 Mar 2025 09:01:04 -0400
Subject: [PATCH 17/17] Apply suggestions from docs review
Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com>
---
java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp | 2 +-
java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql | 2 +-
.../src/change-notes/2025-02-24-spring-boot-actuators-promo.md | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp
index 15675545c42f..97ced3576f66 100644
--- a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp
+++ b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.qhelp
@@ -12,7 +12,7 @@ even to remote code execution.
Since actuator endpoints may contain sensitive information, carefully consider when to expose them,
and secure them as you would any sensitive URL. Actuators are secured by default when using Spring
Security without a custom configuration. If you wish to define a custom security configuration,
-consider only allowing users with certain roles access to the endpoints.
+consider only allowing users with certain roles to access these endpoints.
diff --git a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
index 5818713d058b..1e0944062a01 100644
--- a/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
+++ b/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
@@ -1,6 +1,6 @@
/**
* @name Exposed Spring Boot actuators
- * @description Exposing Spring Boot actuators may lead to internal application's information leak
+ * @description Exposing Spring Boot actuators may lead to information leak from the internal application,
* or even to remote code execution.
* @kind problem
* @problem.severity error
diff --git a/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md b/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md
index f66c6b33b583..8f407de95ac2 100644
--- a/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md
+++ b/java/ql/src/change-notes/2025-02-24-spring-boot-actuators-promo.md
@@ -1,4 +1,4 @@
---
category: newQuery
---
-* The query `java/spring-boot-exposed-actuators` has been promoted from experimental to the main query pack. Its results will now appear by default, and it will be removed from the [CodeQL Community Packs](https://github.com/GitHubSecurityLab/CodeQL-Community-Packs). This query was originally submitted as an experimental query [by @ggolawski](https://github.com/github/codeql/pull/2901).
+* The query `java/spring-boot-exposed-actuators` has been promoted from experimental to the main query pack. Its results will now appear by default, and the query itself will be removed from the [CodeQL Community Packs](https://github.com/GitHubSecurityLab/CodeQL-Community-Packs). This query was originally submitted as an experimental query [by @ggolawski](https://github.com/github/codeql/pull/2901).