Merge pull request #18793 from jcogs33/jcogs33/java/spring-boot-actuators-promo

Java: Promote Spring Boot Actuators query from experimental
This commit is contained in:
Jami
2025-03-11 14:42:14 -04:00
committed by GitHub
21 changed files with 698 additions and 350 deletions

View File

@@ -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 SpringEndpointRequest extends Class {
SpringEndpointRequest() {
this.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
"EndpointRequest")
}
}
/** A call to `EndpointRequest.toAnyEndpoint` method. */
class SpringToAnyEndpointCall extends MethodCall {
SpringToAnyEndpointCall() {
this.getMethod().hasName("toAnyEndpoint") and
this.getMethod().getDeclaringType() instanceof SpringEndpointRequest
}
}

View File

@@ -0,0 +1,124 @@
/**
* 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 SpringHttpSecurity extends Class {
SpringHttpSecurity() {
this.hasQualifiedName("org.springframework.security.config.annotation.web.builders",
"HttpSecurity")
}
}
/**
* The class
* `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer$AuthorizedUrl`
* or the class
* `org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer$AuthorizedUrl`.
*/
class SpringAuthorizedUrl extends Class {
SpringAuthorizedUrl() {
this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
[
"ExpressionUrlAuthorizationConfigurer<HttpSecurity>$AuthorizedUrl<>",
"AuthorizeHttpRequestsConfigurer<HttpSecurity>$AuthorizedUrl<>"
])
}
}
/**
* The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
*/
class SpringAbstractRequestMatcherRegistry extends Class {
SpringAbstractRequestMatcherRegistry() {
this.hasQualifiedName("org.springframework.security.config.annotation.web",
"AbstractRequestMatcherRegistry<AuthorizedUrl<>>")
}
}
/**
* A call to the `HttpSecurity.authorizeRequests` method.
*
* Note: this method is deprecated and scheduled for removal
* in Spring Security 7.0.
*/
class SpringAuthorizeRequestsCall extends MethodCall {
SpringAuthorizeRequestsCall() {
this.getMethod().hasName("authorizeRequests") and
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
/**
* A call to the `HttpSecurity.authorizeHttpRequests` method.
*
* Note: the no-argument version of this method is deprecated
* and scheduled for removal in Spring Security 7.0.
*/
class SpringAuthorizeHttpRequestsCall extends MethodCall {
SpringAuthorizeHttpRequestsCall() {
this.getMethod().hasName("authorizeHttpRequests") and
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
/**
* A call to the `HttpSecurity.requestMatcher` method.
*
* Note: this method was removed in Spring Security 6.0.
* It was replaced by `securityMatcher`.
*/
class SpringRequestMatcherCall extends MethodCall {
SpringRequestMatcherCall() {
this.getMethod().hasName("requestMatcher") and
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
/**
* A call to the `HttpSecurity.requestMatchers` method.
*
* Note: this method was removed in Spring Security 6.0.
* It was replaced by `securityMatchers`.
*/
class SpringRequestMatchersCall extends MethodCall {
SpringRequestMatchersCall() {
this.getMethod().hasName("requestMatchers") and
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
/** A call to the `HttpSecurity.securityMatcher` method. */
class SpringSecurityMatcherCall extends MethodCall {
SpringSecurityMatcherCall() {
this.getMethod().hasName("securityMatcher") and
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
/** A call to the `HttpSecurity.securityMatchers` method. */
class SpringSecurityMatchersCall extends MethodCall {
SpringSecurityMatchersCall() {
this.getMethod().hasName("securityMatchers") and
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
}
}
/** A call to the `AuthorizedUrl.permitAll` method. */
class SpringPermitAllCall extends MethodCall {
SpringPermitAllCall() {
this.getMethod().hasName("permitAll") and
this.getMethod().getDeclaringType() instanceof SpringAuthorizedUrl
}
}
/** A call to the `AbstractRequestMatcherRegistry.anyRequest` method. */
class SpringAnyRequestCall extends MethodCall {
SpringAnyRequestCall() {
this.getMethod().hasName("anyRequest") and
this.getMethod().getDeclaringType() instanceof SpringAbstractRequestMatcherRegistry
}
}

View File

@@ -0,0 +1,110 @@
/** Provides classes and predicates to reason about exposed actuators in Spring Boot. */
import java
private import semmle.code.java.frameworks.spring.SpringSecurity
private import semmle.code.java.frameworks.spring.SpringBoot
/**
* A call to an `HttpSecurity` matcher method with argument
* `EndpointRequest.toAnyEndpoint()`.
*/
private class HttpSecurityMatcherCall extends MethodCall {
HttpSecurityMatcherCall() {
(
this instanceof SpringRequestMatcherCall or
this instanceof SpringSecurityMatcherCall
) and
this.getArgument(0) instanceof SpringToAnyEndpointCall
}
}
/**
* A call to an `HttpSecurity` matchers method with lambda
* argument `EndpointRequest.toAnyEndpoint()`.
*/
private class HttpSecurityMatchersCall extends MethodCall {
HttpSecurityMatchersCall() {
(
this instanceof SpringRequestMatchersCall or
this instanceof SpringSecurityMatchersCall
) and
this.getArgument(0).(LambdaExpr).getExprBody() instanceof SpringToAnyEndpointCall
}
}
/**
* A call to an `AbstractRequestMatcherRegistry.requestMatchers` method with
* argument `EndpointRequest.toAnyEndpoint()`.
*/
private class RegistryRequestMatchersCall extends MethodCall {
RegistryRequestMatchersCall() {
this.getMethod().hasName("requestMatchers") and
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 SpringAuthorizeRequestsCall or
this instanceof SpringAuthorizeHttpRequestsCall
}
}
/** Holds if `permitAllCall` is called on request(s) mapped to actuator endpoint(s). */
predicate permitsSpringBootActuators(SpringPermitAllCall permitAllCall) {
exists(AuthorizeCall authorizeCall |
// .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
authorizeCall.getQualifier() instanceof HttpSecurityMatcherCall
or
// .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
authorizeCall.getQualifier() instanceof HttpSecurityMatchersCall
|
// [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
(
permitAllCall.getQualifier() instanceof SpringAnyRequestCall or
permitAllCall.getQualifier() instanceof RegistryRequestMatchersCall
)
or
// [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
// [...].authorizeRequests().anyRequest().permitAll()
authorizeCall.getNumArgument() = 0 and
exists(RegistryRequestMatchersCall registryRequestMatchersCall |
registryRequestMatchersCall.getQualifier() = authorizeCall and
permitAllCall.getQualifier() = registryRequestMatchersCall
)
or
exists(SpringAnyRequestCall anyRequestCall |
anyRequestCall.getQualifier() = authorizeCall and
permitAllCall.getQualifier() = anyRequestCall
)
)
or
exists(AuthorizeCall authorizeCall |
// http.authorizeRequests([...]).[...]
authorizeCall.getQualifier() instanceof VarAccess
|
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
permitAllCall.getQualifier() instanceof RegistryRequestMatchersCall
or
// [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
authorizeCall.getNumArgument() = 0 and
exists(RegistryRequestMatchersCall registryRequestMatchersCall |
registryRequestMatchersCall.getQualifier() = authorizeCall and
permitAllCall.getQualifier() = registryRequestMatchersCall
)
or
exists(Variable v, HttpSecurityMatcherCall 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 SpringAnyRequestCall
)
)
}

View File

@@ -0,0 +1,25 @@
@Configuration(proxyBeanMethods = false)
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();
}
}
@Configuration(proxyBeanMethods = false)
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();
}
}

View File

@@ -0,0 +1,36 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>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.</p>
</overview>
<recommendation>
<p>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 to access these endpoints.
</p>
</recommendation>
<example>
<p>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.</p>
<p>In the second example, only users with <code>ENDPOINT_ADMIN</code> role are allowed to access
the actuator endpoints.</p>
<sample src="SpringBootActuators.java" />
</example>
<references>
<li>
Spring Boot Reference Documentation:
<a href="https://docs.spring.io/spring-boot/reference/actuator/endpoints.html">Endpoints</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Exposed Spring Boot actuators
* @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
* @security-severity 6.5
* @precision high
* @id java/spring-boot-exposed-actuators
* @tags security
* external/cwe/cwe-200
*/
import java
import semmle.code.java.frameworks.spring.SpringSecurity
import semmle.code.java.security.SpringBootActuatorsQuery
from SpringPermitAllCall permitAllCall
where permitsSpringBootActuators(permitAllCall)
select permitAllCall, "Unauthenticated access to Spring Boot actuator is allowed."

View File

@@ -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, 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).

View File

@@ -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();
}
}

View File

@@ -1,39 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>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.</p>
</overview>
<recommendation>
<p>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 Securitys 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 <code>RequestMatcher</code> objects that can be used in
combination with Spring Security.</p>
</recommendation>
<example>
<p>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.</p>
<p>In the second example, only users with <code>ENDPOINT_ADMIN</code> role are allowed to access
the actuator endpoints.</p>
<sample src="SpringBootActuators.java" />
</example>
<references>
<li>
Spring Boot documentation:
<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html">Actuators</a>.
</li>
<li>
<a href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">Exploiting Spring Boot Actuators</a>
</li>
</references>
</qhelp>

View File

@@ -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."
}

View File

@@ -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<HttpSecurity>$AuthorizedUrl<>")
}
}
/**
* The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
*/
class TypeAbstractRequestMatcherRegistry extends Class {
TypeAbstractRequestMatcherRegistry() {
this.hasQualifiedName("org.springframework.security.config.annotation.web",
"AbstractRequestMatcherRegistry<AuthorizedUrl<>>")
}
}
/**
* 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
}
}

View File

@@ -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. |

View File

@@ -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();
}
}

View File

@@ -1 +0,0 @@
experimental/Security/CWE/CWE-016/SpringBootActuators.ql

View File

@@ -0,0 +1,281 @@
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
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
}
protected void configure2(HttpSecurity http) throws Exception {
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(); // $ hasExposedSpringBootActuator
}
protected void configure4(HttpSecurity http) throws Exception {
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll(); // $ hasExposedSpringBootActuator
}
protected void configure5(HttpSecurity http) throws Exception {
http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll(); // $ hasExposedSpringBootActuator
}
protected void configure6(HttpSecurity http) throws Exception {
http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()); // $ hasExposedSpringBootActuator
}
protected void configure7(HttpSecurity http) throws Exception {
http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll(); // $ hasExposedSpringBootActuator
}
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();
}
// 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();
}
// 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
public void securityFilterChain2(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"));
}
}

View File

@@ -0,0 +1,19 @@
import java
import semmle.code.java.frameworks.spring.SpringSecurity
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(SpringPermitAllCall permitAllCall | permitsSpringBootActuators(permitAllCall) |
permitAllCall.getLocation() = location and
element = permitAllCall.toString() and
value = ""
)
}
}
import MakeTest<SpringBootActuatorsTest>

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../../stubs/springframework-5.3.8

View File

@@ -9,6 +9,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
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<DefaultSecurityFilterChain, HttpSecurity>
@@ -18,6 +19,14 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
return this;
}
public HttpSecurity securityMatcher(RequestMatcher requestMatcher) {
return this;
}
public HttpSecurity securityMatcher(String... patterns) {
return this;
}
public HttpSecurity authorizeRequests(
Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer)
throws Exception {
@@ -29,6 +38,17 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
return null;
}
public HttpSecurity authorizeHttpRequests(
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
throws Exception {
return this;
}
public AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizeHttpRequests()
throws Exception {
return null;
}
public HttpSecurity requestMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) {
return this;
}
@@ -37,6 +57,14 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
return null;
}
public HttpSecurity securityMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) {
return this;
}
public RequestMatcherConfigurer securityMatchers() {
return null;
}
public CsrfConfigurer<HttpSecurity> csrf() {
return null;
}

View File

@@ -0,0 +1,22 @@
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<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
public final class AuthorizationManagerRequestMatcherRegistry extends
AbstractRequestMatcherRegistry<AuthorizedUrl> {
}
public class AuthorizedUrl {
public AuthorizationManagerRequestMatcherRegistry permitAll() {
return null;
}
public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
return null;
}
}
}

View File

@@ -12,5 +12,9 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
public ExpressionInterceptUrlRegistry permitAll() {
return null;
}
public ExpressionInterceptUrlRegistry hasRole(String role) {
return null;
}
}
}