Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ea9b046

Browse filesBrowse files
authored
Merge pull request #18793 from jcogs33/jcogs33/java/spring-boot-actuators-promo
Java: Promote Spring Boot Actuators query from experimental
2 parents da720b8 + ad63dd9 commit ea9b046
Copy full SHA for ea9b046

File tree

Expand file treeCollapse file tree

21 files changed

+698
-350
lines changed
Filter options
Expand file treeCollapse file tree

21 files changed

+698
-350
lines changed
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Provides classes for working with Spring classes and interfaces from
3+
* `org.springframework.boot.*`.
4+
*/
5+
6+
import java
7+
8+
/**
9+
* The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`.
10+
*/
11+
class SpringEndpointRequest extends Class {
12+
SpringEndpointRequest() {
13+
this.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
14+
"EndpointRequest")
15+
}
16+
}
17+
18+
/** A call to `EndpointRequest.toAnyEndpoint` method. */
19+
class SpringToAnyEndpointCall extends MethodCall {
20+
SpringToAnyEndpointCall() {
21+
this.getMethod().hasName("toAnyEndpoint") and
22+
this.getMethod().getDeclaringType() instanceof SpringEndpointRequest
23+
}
24+
}
+124Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/**
2+
* Provides classes for working with Spring classes and interfaces from
3+
* `org.springframework.security.*`.
4+
*/
5+
6+
import java
7+
8+
/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
9+
class SpringHttpSecurity extends Class {
10+
SpringHttpSecurity() {
11+
this.hasQualifiedName("org.springframework.security.config.annotation.web.builders",
12+
"HttpSecurity")
13+
}
14+
}
15+
16+
/**
17+
* The class
18+
* `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer$AuthorizedUrl`
19+
* or the class
20+
* `org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer$AuthorizedUrl`.
21+
*/
22+
class SpringAuthorizedUrl extends Class {
23+
SpringAuthorizedUrl() {
24+
this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
25+
[
26+
"ExpressionUrlAuthorizationConfigurer<HttpSecurity>$AuthorizedUrl<>",
27+
"AuthorizeHttpRequestsConfigurer<HttpSecurity>$AuthorizedUrl<>"
28+
])
29+
}
30+
}
31+
32+
/**
33+
* The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
34+
*/
35+
class SpringAbstractRequestMatcherRegistry extends Class {
36+
SpringAbstractRequestMatcherRegistry() {
37+
this.hasQualifiedName("org.springframework.security.config.annotation.web",
38+
"AbstractRequestMatcherRegistry<AuthorizedUrl<>>")
39+
}
40+
}
41+
42+
/**
43+
* A call to the `HttpSecurity.authorizeRequests` method.
44+
*
45+
* Note: this method is deprecated and scheduled for removal
46+
* in Spring Security 7.0.
47+
*/
48+
class SpringAuthorizeRequestsCall extends MethodCall {
49+
SpringAuthorizeRequestsCall() {
50+
this.getMethod().hasName("authorizeRequests") and
51+
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
52+
}
53+
}
54+
55+
/**
56+
* A call to the `HttpSecurity.authorizeHttpRequests` method.
57+
*
58+
* Note: the no-argument version of this method is deprecated
59+
* and scheduled for removal in Spring Security 7.0.
60+
*/
61+
class SpringAuthorizeHttpRequestsCall extends MethodCall {
62+
SpringAuthorizeHttpRequestsCall() {
63+
this.getMethod().hasName("authorizeHttpRequests") and
64+
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
65+
}
66+
}
67+
68+
/**
69+
* A call to the `HttpSecurity.requestMatcher` method.
70+
*
71+
* Note: this method was removed in Spring Security 6.0.
72+
* It was replaced by `securityMatcher`.
73+
*/
74+
class SpringRequestMatcherCall extends MethodCall {
75+
SpringRequestMatcherCall() {
76+
this.getMethod().hasName("requestMatcher") and
77+
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
78+
}
79+
}
80+
81+
/**
82+
* A call to the `HttpSecurity.requestMatchers` method.
83+
*
84+
* Note: this method was removed in Spring Security 6.0.
85+
* It was replaced by `securityMatchers`.
86+
*/
87+
class SpringRequestMatchersCall extends MethodCall {
88+
SpringRequestMatchersCall() {
89+
this.getMethod().hasName("requestMatchers") and
90+
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
91+
}
92+
}
93+
94+
/** A call to the `HttpSecurity.securityMatcher` method. */
95+
class SpringSecurityMatcherCall extends MethodCall {
96+
SpringSecurityMatcherCall() {
97+
this.getMethod().hasName("securityMatcher") and
98+
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
99+
}
100+
}
101+
102+
/** A call to the `HttpSecurity.securityMatchers` method. */
103+
class SpringSecurityMatchersCall extends MethodCall {
104+
SpringSecurityMatchersCall() {
105+
this.getMethod().hasName("securityMatchers") and
106+
this.getMethod().getDeclaringType() instanceof SpringHttpSecurity
107+
}
108+
}
109+
110+
/** A call to the `AuthorizedUrl.permitAll` method. */
111+
class SpringPermitAllCall extends MethodCall {
112+
SpringPermitAllCall() {
113+
this.getMethod().hasName("permitAll") and
114+
this.getMethod().getDeclaringType() instanceof SpringAuthorizedUrl
115+
}
116+
}
117+
118+
/** A call to the `AbstractRequestMatcherRegistry.anyRequest` method. */
119+
class SpringAnyRequestCall extends MethodCall {
120+
SpringAnyRequestCall() {
121+
this.getMethod().hasName("anyRequest") and
122+
this.getMethod().getDeclaringType() instanceof SpringAbstractRequestMatcherRegistry
123+
}
124+
}
+110Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/** Provides classes and predicates to reason about exposed actuators in Spring Boot. */
2+
3+
import java
4+
private import semmle.code.java.frameworks.spring.SpringSecurity
5+
private import semmle.code.java.frameworks.spring.SpringBoot
6+
7+
/**
8+
* A call to an `HttpSecurity` matcher method with argument
9+
* `EndpointRequest.toAnyEndpoint()`.
10+
*/
11+
private class HttpSecurityMatcherCall extends MethodCall {
12+
HttpSecurityMatcherCall() {
13+
(
14+
this instanceof SpringRequestMatcherCall or
15+
this instanceof SpringSecurityMatcherCall
16+
) and
17+
this.getArgument(0) instanceof SpringToAnyEndpointCall
18+
}
19+
}
20+
21+
/**
22+
* A call to an `HttpSecurity` matchers method with lambda
23+
* argument `EndpointRequest.toAnyEndpoint()`.
24+
*/
25+
private class HttpSecurityMatchersCall extends MethodCall {
26+
HttpSecurityMatchersCall() {
27+
(
28+
this instanceof SpringRequestMatchersCall or
29+
this instanceof SpringSecurityMatchersCall
30+
) and
31+
this.getArgument(0).(LambdaExpr).getExprBody() instanceof SpringToAnyEndpointCall
32+
}
33+
}
34+
35+
/**
36+
* A call to an `AbstractRequestMatcherRegistry.requestMatchers` method with
37+
* argument `EndpointRequest.toAnyEndpoint()`.
38+
*/
39+
private class RegistryRequestMatchersCall extends MethodCall {
40+
RegistryRequestMatchersCall() {
41+
this.getMethod().hasName("requestMatchers") and
42+
this.getMethod().getDeclaringType() instanceof SpringAbstractRequestMatcherRegistry and
43+
this.getAnArgument() instanceof SpringToAnyEndpointCall
44+
}
45+
}
46+
47+
/** A call to an `HttpSecurity` method that authorizes requests. */
48+
private class AuthorizeCall extends MethodCall {
49+
AuthorizeCall() {
50+
this instanceof SpringAuthorizeRequestsCall or
51+
this instanceof SpringAuthorizeHttpRequestsCall
52+
}
53+
}
54+
55+
/** Holds if `permitAllCall` is called on request(s) mapped to actuator endpoint(s). */
56+
predicate permitsSpringBootActuators(SpringPermitAllCall permitAllCall) {
57+
exists(AuthorizeCall authorizeCall |
58+
// .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
59+
authorizeCall.getQualifier() instanceof HttpSecurityMatcherCall
60+
or
61+
// .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
62+
authorizeCall.getQualifier() instanceof HttpSecurityMatchersCall
63+
|
64+
// [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
65+
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
66+
authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
67+
(
68+
permitAllCall.getQualifier() instanceof SpringAnyRequestCall or
69+
permitAllCall.getQualifier() instanceof RegistryRequestMatchersCall
70+
)
71+
or
72+
// [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
73+
// [...].authorizeRequests().anyRequest().permitAll()
74+
authorizeCall.getNumArgument() = 0 and
75+
exists(RegistryRequestMatchersCall registryRequestMatchersCall |
76+
registryRequestMatchersCall.getQualifier() = authorizeCall and
77+
permitAllCall.getQualifier() = registryRequestMatchersCall
78+
)
79+
or
80+
exists(SpringAnyRequestCall anyRequestCall |
81+
anyRequestCall.getQualifier() = authorizeCall and
82+
permitAllCall.getQualifier() = anyRequestCall
83+
)
84+
)
85+
or
86+
exists(AuthorizeCall authorizeCall |
87+
// http.authorizeRequests([...]).[...]
88+
authorizeCall.getQualifier() instanceof VarAccess
89+
|
90+
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
91+
authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
92+
permitAllCall.getQualifier() instanceof RegistryRequestMatchersCall
93+
or
94+
// [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
95+
authorizeCall.getNumArgument() = 0 and
96+
exists(RegistryRequestMatchersCall registryRequestMatchersCall |
97+
registryRequestMatchersCall.getQualifier() = authorizeCall and
98+
permitAllCall.getQualifier() = registryRequestMatchersCall
99+
)
100+
or
101+
exists(Variable v, HttpSecurityMatcherCall matcherCall |
102+
// http.securityMatcher(EndpointRequest.toAnyEndpoint());
103+
// http.authorizeRequests([...].permitAll())
104+
v.getAnAccess() = authorizeCall.getQualifier() and
105+
v.getAnAccess() = matcherCall.getQualifier() and
106+
authorizeCall.getArgument(0).(LambdaExpr).getExprBody() = permitAllCall and
107+
permitAllCall.getQualifier() instanceof SpringAnyRequestCall
108+
)
109+
)
110+
}
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@Configuration(proxyBeanMethods = false)
2+
public class CustomSecurityConfiguration {
3+
4+
@Bean
5+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
6+
// BAD: Unauthenticated access to Spring Boot actuator endpoints is allowed
7+
http.securityMatcher(EndpointRequest.toAnyEndpoint());
8+
http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());
9+
return http.build();
10+
}
11+
12+
}
13+
14+
@Configuration(proxyBeanMethods = false)
15+
public class CustomSecurityConfiguration {
16+
17+
@Bean
18+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
19+
// GOOD: only users with ENDPOINT_ADMIN role are allowed to access the actuator endpoints
20+
http.securityMatcher(EndpointRequest.toAnyEndpoint());
21+
http.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
22+
return http.build();
23+
}
24+
25+
}
+36Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Spring Boot includes features called actuators that let you monitor and interact with your
7+
web application. Exposing unprotected actuator endpoints can lead to information disclosure or
8+
even to remote code execution.</p>
9+
</overview>
10+
11+
<recommendation>
12+
<p>Since actuator endpoints may contain sensitive information, carefully consider when to expose them,
13+
and secure them as you would any sensitive URL. Actuators are secured by default when using Spring
14+
Security without a custom configuration. If you wish to define a custom security configuration,
15+
consider only allowing users with certain roles to access these endpoints.
16+
</p>
17+
18+
</recommendation>
19+
20+
<example>
21+
<p>In the first example, the custom security configuration allows unauthenticated access to all
22+
actuator endpoints. This may lead to sensitive information disclosure and should be avoided.</p>
23+
24+
<p>In the second example, only users with <code>ENDPOINT_ADMIN</code> role are allowed to access
25+
the actuator endpoints.</p>
26+
27+
<sample src="SpringBootActuators.java" />
28+
</example>
29+
30+
<references>
31+
<li>
32+
Spring Boot Reference Documentation:
33+
<a href="https://docs.spring.io/spring-boot/reference/actuator/endpoints.html">Endpoints</a>.
34+
</li>
35+
</references>
36+
</qhelp>
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name Exposed Spring Boot actuators
3+
* @description Exposing Spring Boot actuators may lead to information leak from the internal application,
4+
* or even to remote code execution.
5+
* @kind problem
6+
* @problem.severity error
7+
* @security-severity 6.5
8+
* @precision high
9+
* @id java/spring-boot-exposed-actuators
10+
* @tags security
11+
* external/cwe/cwe-200
12+
*/
13+
14+
import java
15+
import semmle.code.java.frameworks.spring.SpringSecurity
16+
import semmle.code.java.security.SpringBootActuatorsQuery
17+
18+
from SpringPermitAllCall permitAllCall
19+
where permitsSpringBootActuators(permitAllCall)
20+
select permitAllCall, "Unauthenticated access to Spring Boot actuator is allowed."
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: newQuery
3+
---
4+
* 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).

‎java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.java

Copy file name to clipboardExpand all lines: java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.java
-22Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.