60
60
import java .util .ArrayList ;
61
61
import java .util .Collections ;
62
62
import java .util .HashMap ;
63
- import java .util .HashSet ;
64
63
import java .util .List ;
65
64
import java .util .Map ;
66
65
import java .util .Set ;
67
66
import java .util .concurrent .ConcurrentHashMap ;
67
+ import java .util .concurrent .atomic .AtomicBoolean ;
68
68
import okio .Okio ;
69
69
import org .slf4j .Logger ;
70
70
import org .slf4j .LoggerFactory ;
@@ -99,7 +99,11 @@ public class AppSecConfigServiceImpl implements AppSecConfigService {
99
99
100
100
private boolean hasUserWafConfig ;
101
101
private boolean defaultConfigActivated ;
102
- private final Set <String > usedDDWafConfigKeys = new HashSet <>();
102
+ private final AtomicBoolean subscribedToRulesAndData = new AtomicBoolean ();
103
+ private final Set <String > usedDDWafConfigKeys =
104
+ Collections .newSetFromMap (new ConcurrentHashMap <>());
105
+ private final Set <String > ignoredConfigKeys =
106
+ Collections .newSetFromMap (new ConcurrentHashMap <>());
103
107
private final String DEFAULT_WAF_CONFIG_RULE = "DEFAULT_WAF_CONFIG" ;
104
108
private String currentRuleVersion ;
105
109
private List <AppSecModule > modulesToUpdateVersionIn ;
@@ -122,13 +126,15 @@ private void subscribeConfigurationPoller() {
122
126
subscribeAsmFeatures ();
123
127
124
128
if (!hasUserWafConfig ) {
125
- subscribeRulesAndData ();
129
+ updateRulesAndDataSubscription ();
126
130
} else {
127
131
log .debug ("Will not subscribe to ASM, ASM_DD and ASM_DATA (AppSec custom rules in use)" );
128
132
}
129
133
130
134
this .configurationPoller .addConfigurationEndListener (applyRemoteConfigListener );
135
+ }
131
136
137
+ private long getRulesAndDataCapabilities () {
132
138
long capabilities =
133
139
CAPABILITY_ASM_DD_RULES
134
140
| CAPABILITY_ASM_IP_BLOCKING
@@ -154,13 +160,36 @@ private void subscribeConfigurationPoller() {
154
160
capabilities |= CAPABILITY_ASM_RASP_LFI ;
155
161
}
156
162
}
157
- this .configurationPoller .addCapabilities (capabilities );
163
+ return capabilities ;
164
+ }
165
+
166
+ private void updateRulesAndDataSubscription () {
167
+ if (hasUserWafConfig ) {
168
+ return ; // do nothing if the customer has custom rules
169
+ }
170
+ if (AppSecSystem .isActive ()) {
171
+ subscribeRulesAndData ();
172
+ } else {
173
+ unsubscribeRulesAndData ();
174
+ }
158
175
}
159
176
160
177
private void subscribeRulesAndData () {
161
- this .configurationPoller .addListener (Product .ASM_DD , new AppSecConfigChangesDDListener ());
162
- this .configurationPoller .addListener (Product .ASM_DATA , new AppSecConfigChangesListener ());
163
- this .configurationPoller .addListener (Product .ASM , new AppSecConfigChangesListener ());
178
+ if (subscribedToRulesAndData .compareAndSet (false , true )) {
179
+ this .configurationPoller .addListener (Product .ASM_DD , new AppSecConfigChangesDDListener ());
180
+ this .configurationPoller .addListener (Product .ASM_DATA , new AppSecConfigChangesListener ());
181
+ this .configurationPoller .addListener (Product .ASM , new AppSecConfigChangesListener ());
182
+ this .configurationPoller .addCapabilities (getRulesAndDataCapabilities ());
183
+ }
184
+ }
185
+
186
+ private void unsubscribeRulesAndData () {
187
+ if (subscribedToRulesAndData .compareAndSet (true , false )) {
188
+ this .configurationPoller .removeListeners (Product .ASM_DD );
189
+ this .configurationPoller .removeListeners (Product .ASM_DATA );
190
+ this .configurationPoller .removeListeners (Product .ASM );
191
+ this .configurationPoller .removeCapabilities (getRulesAndDataCapabilities ());
192
+ }
164
193
}
165
194
166
195
public void modulesToUpdateVersionIn (List <AppSecModule > modules ) {
@@ -175,19 +204,21 @@ private class AppSecConfigChangesListener implements ProductListener {
175
204
@ Override
176
205
public void accept (ConfigKey configKey , byte [] content , PollingRateHinter pollingRateHinter )
177
206
throws IOException {
178
- maybeInitializeDefaultConfig ();
179
-
180
207
if (content == null ) {
181
- try {
182
- wafBuilder .removeConfig (configKey .toString ());
183
- } catch (UnclassifiedWafException e ) {
184
- throw new RuntimeException (e );
185
- }
208
+ remove (configKey , pollingRateHinter );
209
+ return ;
210
+ }
211
+ final String key = configKey .toString ();
212
+ Map <String , Object > contentMap =
213
+ ADAPTER .fromJson (Okio .buffer (Okio .source (new ByteArrayInputStream (content ))));
214
+ if (contentMap == null || contentMap .isEmpty ()) {
215
+ ignoredConfigKeys .add (key );
186
216
} else {
187
- Map <String , Object > contentMap =
188
- ADAPTER .fromJson (Okio .buffer (Okio .source (new ByteArrayInputStream (content ))));
217
+ ignoredConfigKeys .remove (key );
189
218
try {
190
- handleWafUpdateResultReport (configKey .toString (), contentMap );
219
+ beforeApply (key , contentMap );
220
+ maybeInitializeDefaultConfig ();
221
+ handleWafUpdateResultReport (key , contentMap );
191
222
} catch (AppSecModule .AppSecModuleActivationException e ) {
192
223
throw new RuntimeException (e );
193
224
}
@@ -197,19 +228,32 @@ public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollin
197
228
@ Override
198
229
public void remove (ConfigKey configKey , PollingRateHinter pollingRateHinter )
199
230
throws IOException {
200
- accept (configKey , null , pollingRateHinter );
231
+ final String key = configKey .toString ();
232
+ if (ignoredConfigKeys .remove (key )) {
233
+ return ;
234
+ }
235
+ try {
236
+ maybeInitializeDefaultConfig ();
237
+ wafBuilder .removeConfig (key );
238
+ afterRemove (key );
239
+ } catch (UnclassifiedWafException e ) {
240
+ throw new RuntimeException (e );
241
+ }
201
242
}
202
243
203
244
@ Override
204
245
public void commit (PollingRateHinter pollingRateHinter ) {
205
246
// no action needed
206
247
}
248
+
249
+ protected void beforeApply (final String key , final Map <String , Object > contentMap ) {}
250
+
251
+ protected void afterRemove (final String key ) {}
207
252
}
208
253
209
254
private class AppSecConfigChangesDDListener extends AppSecConfigChangesListener {
210
255
@ Override
211
- public void accept (ConfigKey configKey , byte [] content , PollingRateHinter pollingRateHinter )
212
- throws IOException {
256
+ protected void beforeApply (final String key , final Map <String , Object > config ) {
213
257
if (defaultConfigActivated ) { // if we get any config, remove the default one
214
258
log .debug ("Removing default config" );
215
259
try {
@@ -219,15 +263,12 @@ public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollin
219
263
}
220
264
defaultConfigActivated = false ;
221
265
}
222
- usedDDWafConfigKeys .add (configKey .toString ());
223
- super .accept (configKey , content , pollingRateHinter );
266
+ usedDDWafConfigKeys .add (key );
224
267
}
225
268
226
269
@ Override
227
- public void remove (ConfigKey configKey , PollingRateHinter pollingRateHinter )
228
- throws IOException {
229
- super .remove (configKey , pollingRateHinter );
230
- usedDDWafConfigKeys .remove (configKey .toString ());
270
+ protected void afterRemove (final String key ) {
271
+ usedDDWafConfigKeys .remove (key );
231
272
}
232
273
}
233
274
@@ -282,7 +323,6 @@ private void subscribeAsmFeatures() {
282
323
Product .ASM_FEATURES ,
283
324
AppSecFeaturesDeserializer .INSTANCE ,
284
325
(configKey , newConfig , hinter ) -> {
285
- maybeInitializeDefaultConfig ();
286
326
if (newConfig == null ) {
287
327
mergedAsmFeatures .removeConfig (configKey );
288
328
} else {
@@ -339,8 +379,6 @@ public void init() {
339
379
} else {
340
380
hasUserWafConfig = true ;
341
381
}
342
- this .mergedAsmFeatures .clear ();
343
- this .usedDDWafConfigKeys .clear ();
344
382
345
383
if (wafConfig .isEmpty ()) {
346
384
throw new IllegalStateException ("Expected default waf config to be available" );
@@ -353,9 +391,12 @@ public void init() {
353
391
}
354
392
355
393
public void maybeSubscribeConfigPolling () {
394
+ final ProductActivation appSecActivation = tracerConfig .getAppSecActivation ();
395
+ if (appSecActivation == ProductActivation .FULLY_DISABLED ) {
396
+ return ; // shouldn't happen but just in case.
397
+ }
356
398
if (this .configurationPoller != null ) {
357
- if (hasUserWafConfig
358
- && tracerConfig .getAppSecActivation () == ProductActivation .FULLY_ENABLED ) {
399
+ if (hasUserWafConfig && appSecActivation == ProductActivation .FULLY_ENABLED ) {
359
400
log .info (
360
401
"AppSec will not use remote config because "
361
402
+ "there is a custom user configuration and AppSec is explicitly enabled" );
@@ -494,6 +535,7 @@ public void close() {
494
535
this .configurationPoller .removeListeners (Product .ASM );
495
536
this .configurationPoller .removeListeners (Product .ASM_FEATURES );
496
537
this .configurationPoller .removeConfigurationEndListener (applyRemoteConfigListener );
538
+ this .subscribedToRulesAndData .set (false );
497
539
this .configurationPoller .stop ();
498
540
if (this .wafBuilder != null ) {
499
541
this .wafBuilder .close ();
@@ -526,6 +568,7 @@ private void setAppSecActivation(final AppSecFeatures.Asm asm) {
526
568
if (AppSecSystem .isActive () != newState ) {
527
569
log .info ("AppSec {} (runtime)" , newState ? "enabled" : "disabled" );
528
570
AppSecSystem .setActive (newState );
571
+ updateRulesAndDataSubscription ();
529
572
}
530
573
}
531
574
0 commit comments