Skip to content

Commit ca892f3

Browse files
committed
feat: add warning in case of bad regex
1 parent 116d6c8 commit ca892f3

File tree

8 files changed

+131
-56
lines changed

8 files changed

+131
-56
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright © 2015 The Gravitee team (http://gravitee.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.gravitee.gateway.core.logging;
17+
18+
import io.gravitee.gateway.reactive.api.ExecutionWarn;
19+
import io.gravitee.gateway.reactive.api.context.base.BaseExecutionContext;
20+
import java.util.Collection;
21+
import java.util.List;
22+
import java.util.function.Predicate;
23+
import java.util.function.Supplier;
24+
import java.util.regex.Pattern;
25+
import java.util.regex.PatternSyntaxException;
26+
import java.util.stream.Stream;
27+
import lombok.Getter;
28+
import lombok.RequiredArgsConstructor;
29+
import lombok.Setter;
30+
import org.jspecify.annotations.Nullable;
31+
32+
@RequiredArgsConstructor
33+
public class LoggableContentType {
34+
35+
private static final String DEFAULT_EXCLUDED_CONTENT_TYPES =
36+
"video.*|audio.*|image.*|application/octet-stream|application/pdf|text/event-stream";
37+
private Predicate<String> excludedContentTypesPattern;
38+
39+
@Setter
40+
@Getter
41+
private String excludedResponseTypes;
42+
43+
private final Supplier<String> customExcludedResponseTypes;
44+
45+
public LoggableContentType() {
46+
this(() -> null);
47+
}
48+
49+
private Predicate<String> matcher(Collection<Supplier<String>> regex, @Nullable BaseExecutionContext ctx) {
50+
return excludedContentTypesPattern != null
51+
? excludedContentTypesPattern
52+
: (excludedContentTypesPattern = regex
53+
.stream()
54+
.map(Supplier::get)
55+
.filter(pattern -> pattern != null && !pattern.isEmpty())
56+
.flatMap(pattern -> {
57+
try {
58+
return Stream.of(Pattern.compile(pattern));
59+
} catch (PatternSyntaxException e) {
60+
if (ctx != null) {
61+
ctx.warnWith(
62+
new ExecutionWarn("BAD_REGEX_CONTENT_TYPE")
63+
.cause(e)
64+
.message(
65+
"Invalid Content-Type filter regex provided ('%s'). Default one will be used.".formatted(
66+
pattern
67+
)
68+
)
69+
);
70+
}
71+
return Stream.empty();
72+
}
73+
})
74+
.findFirst()
75+
.map(Pattern::asPredicate)
76+
.orElse(contentType -> true));
77+
}
78+
79+
/**
80+
* Determines if body can be logged for APIv4
81+
*/
82+
public boolean isContentTypeLoggable(@Nullable final String contentType, BaseExecutionContext ctx) {
83+
return (
84+
contentType == null ||
85+
!matcher(List.of(customExcludedResponseTypes, () -> excludedResponseTypes, () -> DEFAULT_EXCLUDED_CONTENT_TYPES), ctx).test(
86+
contentType
87+
)
88+
);
89+
}
90+
91+
/**
92+
* Determines if body can be logged for APIv2
93+
*/
94+
public boolean isContentTypeLoggable(@Nullable final String contentType, final LoggingContext loggingContext) {
95+
return (
96+
contentType == null ||
97+
!matcher(List.of(loggingContext::getExcludedResponseTypes, () -> DEFAULT_EXCLUDED_CONTENT_TYPES), null).test(contentType)
98+
);
99+
}
100+
}

gravitee-apim-gateway/gravitee-apim-gateway-core/src/main/java/io/gravitee/gateway/core/logging/utils/LoggingUtils.java

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020
import io.gravitee.definition.model.LoggingMode;
2121
import io.gravitee.gateway.api.ExecutionContext;
2222
import io.gravitee.gateway.api.buffer.Buffer;
23+
import io.gravitee.gateway.core.logging.LoggableContentType;
2324
import io.gravitee.gateway.core.logging.LoggingContext;
2425
import io.gravitee.gateway.reactive.core.v4.analytics.BufferUtils;
2526
import jakarta.annotation.Nonnull;
2627
import jakarta.annotation.Nullable;
27-
import java.util.regex.Pattern;
2828

2929
/**
3030
* @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
@@ -34,10 +34,7 @@
3434
*/
3535
public final class LoggingUtils {
3636

37-
private static final String DEFAULT_EXCLUDED_CONTENT_TYPES =
38-
"video.*|audio.*|image.*|application/octet-stream|application/pdf|text/event-stream";
39-
40-
private static Pattern EXCLUDED_CONTENT_TYPES_PATTERN;
37+
private static final LoggableContentType loggableContentType = new LoggableContentType();
4138

4239
@Nullable
4340
public static LoggingContext getLoggingContext(final Logging logging) {
@@ -76,17 +73,7 @@ public static boolean isContentTypeLoggable(final String contentType, final Exec
7673
}
7774

7875
public static boolean isContentTypeLoggable(final String contentType, final LoggingContext loggingContext) {
79-
// init pattern
80-
if (EXCLUDED_CONTENT_TYPES_PATTERN == null) {
81-
try {
82-
final String responseTypes = loggingContext.getExcludedResponseTypes();
83-
EXCLUDED_CONTENT_TYPES_PATTERN = Pattern.compile(responseTypes);
84-
} catch (Exception e) {
85-
EXCLUDED_CONTENT_TYPES_PATTERN = Pattern.compile(DEFAULT_EXCLUDED_CONTENT_TYPES);
86-
}
87-
}
88-
89-
return contentType == null || !EXCLUDED_CONTENT_TYPES_PATTERN.matcher(contentType).find();
76+
return loggableContentType.isContentTypeLoggable(contentType, loggingContext);
9077
}
9178

9279
public static boolean isRequestHeadersLoggable(final ExecutionContext executionContext) {

gravitee-apim-gateway/gravitee-apim-gateway-core/src/main/java/io/gravitee/gateway/reactive/core/v4/analytics/LoggingContext.java

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@
1818
import io.gravitee.common.utils.SizeUtils;
1919
import io.gravitee.definition.model.ConditionSupplier;
2020
import io.gravitee.definition.model.v4.analytics.logging.Logging;
21+
import io.gravitee.gateway.core.logging.LoggableContentType;
22+
import io.gravitee.gateway.reactive.api.context.base.BaseExecutionContext;
2123
import io.gravitee.gateway.report.guard.LogGuardService;
22-
import java.util.regex.Pattern;
23-
import java.util.regex.PatternSyntaxException;
24-
import java.util.stream.Stream;
2524
import lombok.Getter;
2625
import lombok.RequiredArgsConstructor;
2726
import lombok.Setter;
@@ -35,22 +34,20 @@
3534
@RequiredArgsConstructor
3635
public class LoggingContext implements ConditionSupplier {
3736

38-
private static final String DEFAULT_EXCLUDED_CONTENT_TYPES =
39-
"video.*|audio.*|image.*|application/octet-stream|application/pdf|text/event-stream";
40-
4137
protected final Logging logging;
4238

4339
@Getter
4440
private int maxSizeLogMessage = -1;
4541

4642
@Setter
47-
@Getter
48-
private String excludedResponseTypes;
43+
private LogGuardService logGuardService;
4944

50-
private Pattern excludedContentTypesPattern;
45+
private final LoggableContentType loggableContentType;
5146

52-
@Setter
53-
private LogGuardService logGuardService;
47+
public LoggingContext(Logging logging) {
48+
this.logging = logging;
49+
loggableContentType = new LoggableContentType(logging::getOverrideContentTypeValidation);
50+
}
5451

5552
@Override
5653
public String getCondition() {
@@ -105,6 +102,14 @@ public boolean endpointResponsePayload() {
105102
return logging.getMode().isEndpoint() && logging.getPhase().isResponse() && logging.getContent().isPayload();
106103
}
107104

105+
public String getExcludedResponseTypes() {
106+
return loggableContentType.getExcludedResponseTypes();
107+
}
108+
109+
public void setExcludedResponseTypes(String excludedResponseTypes) {
110+
loggableContentType.setExcludedResponseTypes(excludedResponseTypes);
111+
}
112+
108113
/**
109114
* Define the max size of the logging (for each payload, whatever it's about the request / response / consumer / proxy)
110115
* Which means that if size is define to 5, it will be 5 x 4 = 20 (at most).
@@ -123,27 +128,8 @@ public void setMaxSizeLogMessage(String maxSizeLogMessage) {
123128
}
124129
}
125130

126-
public boolean isContentTypeLoggable(final String contentType) {
127-
// init pattern
128-
if (excludedContentTypesPattern == null) {
129-
this.excludedContentTypesPattern = Stream.of(
130-
logging.getOverrideContentTypeValidation(),
131-
excludedResponseTypes,
132-
DEFAULT_EXCLUDED_CONTENT_TYPES
133-
)
134-
.filter(pattern -> pattern != null && !pattern.isEmpty())
135-
.flatMap(pattern -> {
136-
try {
137-
return Stream.of(Pattern.compile(pattern));
138-
} catch (PatternSyntaxException e) {
139-
return Stream.empty();
140-
}
141-
})
142-
.findFirst()
143-
.orElse(null);
144-
}
145-
146-
return contentType == null || !excludedContentTypesPattern.matcher(contentType).find();
131+
public boolean isContentTypeLoggable(final String contentType, BaseExecutionContext ctx) {
132+
return loggableContentType.isContentTypeLoggable(contentType, ctx);
147133
}
148134

149135
/**

gravitee-apim-gateway/gravitee-apim-gateway-handlers/gravitee-apim-gateway-handlers-api/src/main/java/io/gravitee/gateway/reactive/handlers/api/v4/analytics/logging/LoggingHook.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public Completable pre(String id, HttpExecutionContext ctx, @Nullable ExecutionP
5050

5151
if (log != null && loggingContext != null) {
5252
if (loggingContext.endpointRequest()) {
53-
((LogEndpointRequest) log.getEndpointRequest()).capture();
53+
((LogEndpointRequest) log.getEndpointRequest()).capture(ctx);
5454
}
5555

5656
if (loggingContext.endpointResponseHeaders() || loggingContext.endpointResponse()) {
@@ -77,7 +77,7 @@ public Completable post(String id, HttpExecutionContext ctx, @Nullable Execution
7777
}
7878

7979
if (log != null && loggingContext != null && loggingContext.endpointResponse()) {
80-
((LogEndpointResponse) log.getEndpointResponse()).capture();
80+
((LogEndpointResponse) log.getEndpointResponse()).capture(ctx);
8181

8282
final HttpResponseInternal response = ((HttpExecutionContextInternal) ctx).response();
8383
response.setHeaders(((LogHeadersCaptor) response.headers()).getDelegate());

gravitee-apim-gateway/gravitee-apim-gateway-handlers/gravitee-apim-gateway-handlers-api/src/main/java/io/gravitee/gateway/reactive/handlers/api/v4/analytics/logging/request/LogRequest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.gravitee.gateway.api.buffer.Buffer;
1919
import io.gravitee.gateway.api.http.HttpHeaderNames;
2020
import io.gravitee.gateway.api.http.HttpHeaders;
21+
import io.gravitee.gateway.reactive.api.context.base.BaseExecutionContext;
2122
import io.gravitee.gateway.reactive.api.context.http.HttpPlainRequest;
2223
import io.gravitee.gateway.reactive.core.v4.analytics.BufferUtils;
2324
import io.gravitee.gateway.reactive.core.v4.analytics.LoggingContext;
@@ -38,8 +39,8 @@ protected LogRequest(LoggingContext loggingContext, HttpPlainRequest request) {
3839
this.setMethod(request.method());
3940
}
4041

41-
public void capture() {
42-
if (isLogPayload() && loggingContext.isContentTypeLoggable(request.headers().get(HttpHeaderNames.CONTENT_TYPE))) {
42+
public void capture(BaseExecutionContext ctx) {
43+
if (isLogPayload() && loggingContext.isContentTypeLoggable(request.headers().get(HttpHeaderNames.CONTENT_TYPE), ctx)) {
4344
final Buffer buffer = Buffer.buffer();
4445

4546
if (loggingContext.isBodyLoggable()) {

gravitee-apim-gateway/gravitee-apim-gateway-handlers/gravitee-apim-gateway-handlers-api/src/main/java/io/gravitee/gateway/reactive/handlers/api/v4/analytics/logging/response/LogResponse.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.gravitee.gateway.api.buffer.Buffer;
1919
import io.gravitee.gateway.api.http.HttpHeaderNames;
2020
import io.gravitee.gateway.api.http.HttpHeaders;
21+
import io.gravitee.gateway.reactive.api.context.base.BaseExecutionContext;
2122
import io.gravitee.gateway.reactive.api.context.http.HttpPlainResponse;
2223
import io.gravitee.gateway.reactive.core.v4.analytics.BufferUtils;
2324
import io.gravitee.gateway.reactive.core.v4.analytics.LoggingContext;
@@ -37,8 +38,8 @@ protected LogResponse(LoggingContext loggingContext, HttpPlainResponse response)
3738
this.response = response;
3839
}
3940

40-
public void capture() {
41-
if (isLogPayload() && loggingContext.isContentTypeLoggable(response.headers().get(HttpHeaderNames.CONTENT_TYPE))) {
41+
public void capture(BaseExecutionContext ctx) {
42+
if (isLogPayload() && loggingContext.isContentTypeLoggable(response.headers().get(HttpHeaderNames.CONTENT_TYPE), ctx)) {
4243
final Buffer buffer = Buffer.buffer();
4344
if (loggingContext.isBodyLoggable()) {
4445
response.chunks(

gravitee-apim-gateway/gravitee-apim-gateway-handlers/gravitee-apim-gateway-handlers-api/src/main/java/io/gravitee/gateway/reactive/handlers/api/v4/processor/logging/LogRequestProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public Completable execute(final HttpExecutionContextInternal ctx) {
5151
LoggingContext loggingContext = analyticsContext.getLoggingContext();
5252

5353
if (log != null && loggingContext.entrypointRequest()) {
54-
((LogEntrypointRequest) log.getEntrypointRequest()).capture();
54+
((LogEntrypointRequest) log.getEntrypointRequest()).capture(ctx);
5555
}
5656
});
5757
}

gravitee-apim-gateway/gravitee-apim-gateway-handlers/gravitee-apim-gateway-handlers-api/src/main/java/io/gravitee/gateway/reactive/handlers/api/v4/processor/logging/LogResponseProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public Completable execute(final HttpExecutionContextInternal ctx) {
5151
LoggingContext loggingContext = analyticsContext.getLoggingContext();
5252

5353
if (log != null && loggingContext.entrypointResponse()) {
54-
((LogEntrypointResponse) log.getEntrypointResponse()).capture();
54+
((LogEntrypointResponse) log.getEntrypointResponse()).capture(ctx);
5555
}
5656
});
5757
}

0 commit comments

Comments
 (0)