Skip to content

Commit 4af764e

Browse files
committed
wip
1 parent 716af42 commit 4af764e

File tree

9 files changed

+79
-64
lines changed

9 files changed

+79
-64
lines changed

dd-java-agent/appsec/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ ext {
8282
'com.datadog.appsec.config.AppSecFeatures.ApiSecurity',
8383
'com.datadog.appsec.config.AppSecFeatures.AutoUserInstrum',
8484
'com.datadog.appsec.event.ReplaceableEventProducerService',
85-
'com.datadog.appsec.api.security.ApiSecurityRequestSampler.NoOp',
85+
'com.datadog.appsec.api.security.ApiSecuritySampler.NoOp',
8686
]
8787
excludedClassesBranchCoverage = [
8888
'com.datadog.appsec.gateway.GatewayBridge',

dd-java-agent/appsec/src/main/java/com/datadog/appsec/AppSecSystem.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.datadog.appsec;
22

3-
import com.datadog.appsec.api.security.ApiSecurityRequestSampler;
3+
import com.datadog.appsec.api.security.ApiSecuritySampler;
4+
import com.datadog.appsec.api.security.ApiSecuritySamplerImpl;
45
import com.datadog.appsec.api.security.AppSecSpanPostProcessor;
56
import com.datadog.appsec.blocking.BlockingServiceImpl;
67
import com.datadog.appsec.config.AppSecConfigService;
@@ -68,13 +69,14 @@ private static void doStart(SubscriptionService gw, SharedCommunicationObjects s
6869
EventDispatcher eventDispatcher = new EventDispatcher();
6970
REPLACEABLE_EVENT_PRODUCER.replaceEventProducerService(eventDispatcher);
7071

71-
ApiSecurityRequestSampler requestSampler;
72+
ApiSecuritySampler requestSampler;
7273
if (Config.get().isApiSecurityEnabled()) {
73-
requestSampler = new ApiSecurityRequestSampler();
74+
// TODO: Address support for 1-click enablement
75+
requestSampler = new ApiSecuritySamplerImpl();
7476
SpanPostProcessor.Holder.INSTANCE =
7577
new AppSecSpanPostProcessor(requestSampler, REPLACEABLE_EVENT_PRODUCER);
7678
} else {
77-
requestSampler = new ApiSecurityRequestSampler.NoOp();
79+
requestSampler = new ApiSecuritySampler.NoOp();
7880
}
7981

8082
ConfigurationPoller configurationPoller = sco.configurationPoller(config);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.datadog.appsec.api.security;
2+
3+
import com.datadog.appsec.gateway.AppSecRequestContext;
4+
import javax.annotation.Nonnull;
5+
6+
public interface ApiSecuritySampler {
7+
/**
8+
* Prepare a request context for later sampling decision. This method should be called at request
9+
* end, and is thread-safe. If a request can potentially be sampled, this method will return true.
10+
* If this method returns true, the caller MUST call {@link #releaseOne()} once the context is not
11+
* needed anymore.
12+
*/
13+
boolean preSampleRequest(final @Nonnull AppSecRequestContext ctx);
14+
15+
/** Get the final sampling decision. This method is NOT required to be thread-safe. */
16+
boolean sampleRequest(AppSecRequestContext ctx);
17+
18+
/** Release one permit for the sampler. This must be called after processing a span. */
19+
void releaseOne();
20+
21+
final class NoOp implements ApiSecuritySampler {
22+
@Override
23+
public boolean preSampleRequest(@Nonnull AppSecRequestContext ctx) {
24+
return false;
25+
}
26+
27+
@Override
28+
public boolean sampleRequest(AppSecRequestContext ctx) {
29+
return false;
30+
}
31+
32+
@Override
33+
public void releaseOne() {}
34+
}
35+
}
Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
import org.slf4j.Logger;
1313
import org.slf4j.LoggerFactory;
1414

15-
public class ApiSecurityRequestSampler {
15+
public class ApiSecuritySamplerImpl implements ApiSecuritySampler {
1616

17-
private static final Logger log = LoggerFactory.getLogger(ApiSecurityRequestSampler.class);
17+
private static final Logger log = LoggerFactory.getLogger(ApiSecuritySamplerImpl.class);
1818

1919
/**
2020
* A maximum number of request contexts we'll keep open past the end of request at any given time.
@@ -34,14 +34,14 @@ public class ApiSecurityRequestSampler {
3434
private final TimeSource timeSource;
3535
private final Semaphore counter = new Semaphore(MAX_POST_PROCESSING_TASKS);
3636

37-
public ApiSecurityRequestSampler() {
37+
public ApiSecuritySamplerImpl() {
3838
this(
3939
MAX_SIZE,
4040
(long) (Config.get().getApiSecuritySampleDelay() * 1_000),
4141
SystemTimeSource.INSTANCE);
4242
}
4343

44-
public ApiSecurityRequestSampler(
44+
public ApiSecuritySamplerImpl(
4545
int capacity, long expirationTimeInMs, @Nonnull TimeSource timeSource) {
4646
this.capacity = capacity;
4747
this.expirationTimeInMs = expirationTimeInMs;
@@ -50,12 +50,7 @@ public ApiSecurityRequestSampler(
5050
this.timeSource = timeSource;
5151
}
5252

53-
/**
54-
* Prepare a request context for later sampling decision. This method should be called at request
55-
* end, and is thread-safe. If a request can potentially be sampled, this method will return true.
56-
* If this method returns true, the caller MUST call {@link #releaseOne()} once the context is not
57-
* needed anymore.
58-
*/
53+
@Override
5954
public boolean preSampleRequest(final @Nonnull AppSecRequestContext ctx) {
6055
final String route = ctx.getRoute();
6156
if (route == null) {
@@ -87,6 +82,7 @@ public boolean preSampleRequest(final @Nonnull AppSecRequestContext ctx) {
8782
}
8883

8984
/** Get the final sampling decision. This method is NOT thread-safe. */
85+
@Override
9086
public boolean sampleRequest(AppSecRequestContext ctx) {
9187
if (ctx == null) {
9288
return false;
@@ -99,7 +95,7 @@ public boolean sampleRequest(AppSecRequestContext ctx) {
9995
return updateApiAccessIfExpired(hash);
10096
}
10197

102-
/** Release one permit for the sampler. This must be called after processing a span. */
98+
@Override
10399
public void releaseOne() {
104100
counter.release();
105101
}
@@ -173,20 +169,4 @@ private long computeApiHash(final String route, final String method, final int s
173169
result = 31 * result + statusCode;
174170
return result;
175171
}
176-
177-
public static final class NoOp extends ApiSecurityRequestSampler {
178-
public NoOp() {
179-
super(0, 0, SystemTimeSource.INSTANCE);
180-
}
181-
182-
@Override
183-
public boolean preSampleRequest(@Nonnull AppSecRequestContext ctx) {
184-
return false;
185-
}
186-
187-
@Override
188-
public boolean sampleRequest(AppSecRequestContext ctx) {
189-
return false;
190-
}
191-
}
192172
}

dd-java-agent/appsec/src/main/java/com/datadog/appsec/api/security/AppSecSpanPostProcessor.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@
2121
public class AppSecSpanPostProcessor implements SpanPostProcessor {
2222

2323
private static final Logger log = LoggerFactory.getLogger(AppSecSpanPostProcessor.class);
24-
private final ApiSecurityRequestSampler sampler;
24+
private final ApiSecuritySampler sampler;
2525
private final EventProducerService producerService;
2626

27-
public AppSecSpanPostProcessor(
28-
ApiSecurityRequestSampler sampler, EventProducerService producerService) {
27+
public AppSecSpanPostProcessor(ApiSecuritySampler sampler, EventProducerService producerService) {
2928
this.sampler = sampler;
3029
this.producerService = producerService;
3130
}

dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/GatewayBridge.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import static com.datadog.appsec.gateway.AppSecRequestContext.RESPONSE_HEADERS_ALLOW_LIST;
88

99
import com.datadog.appsec.AppSecSystem;
10-
import com.datadog.appsec.api.security.ApiSecurityRequestSampler;
10+
import com.datadog.appsec.api.security.ApiSecuritySampler;
1111
import com.datadog.appsec.config.TraceSegmentPostProcessor;
1212
import com.datadog.appsec.event.EventProducerService;
1313
import com.datadog.appsec.event.EventProducerService.DataSubscriberInfo;
@@ -83,7 +83,7 @@ public class GatewayBridge {
8383

8484
private final SubscriptionService subscriptionService;
8585
private final EventProducerService producerService;
86-
private final ApiSecurityRequestSampler requestSampler;
86+
private final ApiSecuritySampler requestSampler;
8787
private final List<TraceSegmentPostProcessor> traceSegmentPostProcessors;
8888

8989
// subscriber cache
@@ -109,7 +109,7 @@ public class GatewayBridge {
109109
public GatewayBridge(
110110
SubscriptionService subscriptionService,
111111
EventProducerService producerService,
112-
ApiSecurityRequestSampler requestSampler,
112+
ApiSecuritySampler requestSampler,
113113
List<TraceSegmentPostProcessor> traceSegmentPostProcessors) {
114114
this.subscriptionService = subscriptionService;
115115
this.producerService = producerService;
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import com.datadog.appsec.gateway.AppSecRequestContext
44
import datadog.trace.api.time.ControllableTimeSource
55
import datadog.trace.test.util.DDSpecification
66

7-
class ApiSecurityRequestSamplerTest extends DDSpecification {
7+
class ApiSecuritySamplerTest extends DDSpecification {
88

99
void 'happy path with single request'() {
1010
given:
11-
def ctx = createContext('route1', 'GET', 200)
12-
def sampler = new ApiSecurityRequestSampler()
11+
final ctx = createContext('route1', 'GET', 200)
12+
final sampler = new ApiSecuritySamplerImpl()
1313

1414
when:
1515
final preSampled = sampler.preSampleRequest(ctx)
@@ -29,7 +29,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
2929
given:
3030
AppSecRequestContext ctx1 = createContext('route1', 'GET', 200)
3131
AppSecRequestContext ctx2 = createContext('route1', 'GET', 200)
32-
def sampler = new ApiSecurityRequestSampler()
32+
final sampler = new ApiSecuritySamplerImpl()
3333

3434
when:
3535
final preSampled1 = sampler.preSampleRequest(ctx1)
@@ -52,7 +52,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
5252
given:
5353
final ctx1 = Spy(createContext('route2', 'GET', 200))
5454
final ctx2 = Spy(createContext('route3', 'GET', 200))
55-
final sampler = new ApiSecurityRequestSampler()
55+
final sampler = new ApiSecuritySamplerImpl()
5656
assert sampler.MAX_POST_PROCESSING_TASKS > 0
5757

5858
when: 'exhaust the maximum number of concurrent contexts'
@@ -82,7 +82,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
8282
void 'preSampleRequest with null route'() {
8383
given:
8484
def ctx = createContext(null, 'GET', 200)
85-
def sampler = new ApiSecurityRequestSampler()
85+
def sampler = new ApiSecuritySamplerImpl()
8686

8787
when:
8888
def preSampled = sampler.preSampleRequest(ctx)
@@ -94,7 +94,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
9494
void 'preSampleRequest with null method'() {
9595
given:
9696
def ctx = createContext('route1', null, 200)
97-
def sampler = new ApiSecurityRequestSampler()
97+
def sampler = new ApiSecuritySamplerImpl()
9898

9999
when:
100100
def preSampled = sampler.preSampleRequest(ctx)
@@ -106,7 +106,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
106106
void 'preSampleRequest with 0 status code'() {
107107
given:
108108
def ctx = createContext('route1', 'GET', 0)
109-
def sampler = new ApiSecurityRequestSampler()
109+
def sampler = new ApiSecuritySamplerImpl()
110110

111111
when:
112112
def preSampled = sampler.preSampleRequest(ctx)
@@ -117,7 +117,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
117117

118118
void 'sampleRequest with null context throws'() {
119119
given:
120-
def sampler = new ApiSecurityRequestSampler()
120+
def sampler = new ApiSecuritySamplerImpl()
121121

122122
when:
123123
sampler.preSampleRequest(null)
@@ -128,7 +128,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
128128

129129
void 'sampleRequest without prior preSampleRequest never works'() {
130130
given:
131-
def sampler = new ApiSecurityRequestSampler()
131+
def sampler = new ApiSecuritySamplerImpl()
132132
def ctx = createContext('route1', 'GET', 200)
133133

134134
when:
@@ -147,7 +147,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
147147
timeSource.set(0)
148148
final long expirationTimeInMs = 10L
149149
final long expirationTimeInNs = expirationTimeInMs * 1_000_000
150-
def sampler = new ApiSecurityRequestSampler(10, expirationTimeInMs, timeSource)
150+
def sampler = new ApiSecuritySamplerImpl(10, expirationTimeInMs, timeSource)
151151

152152
when:
153153
def sampled = sampler.sampleRequest(ctx)
@@ -175,7 +175,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
175175
timeSource.set(0)
176176
final long expirationTimeInMs = 10_000
177177
final int maxCapacity = 10
178-
ApiSecurityRequestSampler sampler = new ApiSecurityRequestSampler(10, expirationTimeInMs, timeSource)
178+
ApiSecuritySamplerImpl sampler = new ApiSecuritySamplerImpl(10, expirationTimeInMs, timeSource)
179179

180180
expect:
181181
for (int i = 0; i < maxCapacity * 10; i++) {
@@ -194,7 +194,7 @@ class ApiSecurityRequestSamplerTest extends DDSpecification {
194194
timeSource.set(0)
195195
final long expirationTimeInMs = 10_000
196196
final int maxCapacity = 10
197-
ApiSecurityRequestSampler sampler = new ApiSecurityRequestSampler(10, expirationTimeInMs, timeSource)
197+
ApiSecuritySamplerImpl sampler = new ApiSecuritySamplerImpl(10, expirationTimeInMs, timeSource)
198198

199199
expect:
200200
for (int i = 0; i < maxCapacity * 10; i++) {

dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/api/security/AppSecSpanPostProcessorTest.groovy

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
1313

1414
void 'schema extracted on happy path'() {
1515
given:
16-
def sampler = Mock(ApiSecurityRequestSampler)
16+
def sampler = Mock(ApiSecuritySamplerImpl)
1717
def producer = Mock(EventProducerService)
1818
def subInfo = Mock(EventProducerService.DataSubscriberInfo)
1919
def span = Mock(AgentSpan)
@@ -44,7 +44,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
4444

4545
void 'no schema extracted if sampling is false'() {
4646
given:
47-
def sampler = Mock(ApiSecurityRequestSampler)
47+
def sampler = Mock(ApiSecuritySamplerImpl)
4848
def producer = Mock(EventProducerService)
4949
def span = Mock(AgentSpan)
5050
def reqCtx = Mock(RequestContext)
@@ -68,7 +68,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
6868

6969
void 'permit is released even if request context close throws'() {
7070
given:
71-
def sampler = Mock(ApiSecurityRequestSampler)
71+
def sampler = Mock(ApiSecuritySamplerImpl)
7272
def producer = Mock(EventProducerService)
7373
def span = Mock(AgentSpan)
7474
def reqCtx = Mock(RequestContext)
@@ -95,7 +95,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
9595

9696
void 'context is cleaned up on timeout'() {
9797
given:
98-
def sampler = Mock(ApiSecurityRequestSampler)
98+
def sampler = Mock(ApiSecuritySamplerImpl)
9999
def producer = Mock(EventProducerService)
100100
def span = Mock(AgentSpan)
101101
def reqCtx = Mock(RequestContext)
@@ -118,7 +118,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
118118

119119
void 'process null request context does nothing'() {
120120
given:
121-
def sampler = Mock(ApiSecurityRequestSampler)
121+
def sampler = Mock(ApiSecuritySamplerImpl)
122122
def producer = Mock(EventProducerService)
123123
def span = Mock(AgentSpan)
124124
def processor = new AppSecSpanPostProcessor(sampler, producer)
@@ -134,7 +134,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
134134

135135
void 'process null appsec request context does nothing'() {
136136
given:
137-
def sampler = Mock(ApiSecurityRequestSampler)
137+
def sampler = Mock(ApiSecuritySamplerImpl)
138138
def producer = Mock(EventProducerService)
139139
def span = Mock(AgentSpan)
140140
def reqCtx = Mock(RequestContext)
@@ -152,7 +152,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
152152

153153
void 'process already closed context does nothing'() {
154154
given:
155-
def sampler = Mock(ApiSecurityRequestSampler)
155+
def sampler = Mock(ApiSecuritySamplerImpl)
156156
def producer = Mock(EventProducerService)
157157
def span = Mock(AgentSpan)
158158
def reqCtx = Mock(RequestContext)
@@ -172,7 +172,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
172172

173173
void 'process throws on null span'() {
174174
given:
175-
def sampler = Mock(ApiSecurityRequestSampler)
175+
def sampler = Mock(ApiSecuritySamplerImpl)
176176
def producer = Mock(EventProducerService)
177177
def processor = new AppSecSpanPostProcessor(sampler, producer)
178178

@@ -186,7 +186,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
186186

187187
void 'empty event subscription does not break the process'() {
188188
given:
189-
def sampler = Mock(ApiSecurityRequestSampler)
189+
def sampler = Mock(ApiSecuritySamplerImpl)
190190
def producer = Mock(EventProducerService)
191191
def subInfo = Mock(EventProducerService.DataSubscriberInfo)
192192
def span = Mock(AgentSpan)
@@ -215,7 +215,7 @@ class AppSecSpanPostProcessorTest extends DDSpecification {
215215

216216
void 'expired event subscription does not break the process'() {
217217
given:
218-
def sampler = Mock(ApiSecurityRequestSampler)
218+
def sampler = Mock(ApiSecuritySamplerImpl)
219219
def producer = Mock(EventProducerService)
220220
def subInfo = Mock(EventProducerService.DataSubscriberInfo)
221221
def span = Mock(AgentSpan)

0 commit comments

Comments
 (0)