6060import java .util .ArrayList ;
6161import java .util .Collections ;
6262import java .util .HashMap ;
63- import java .util .HashSet ;
6463import java .util .List ;
6564import java .util .Map ;
6665import java .util .Set ;
6766import java .util .concurrent .ConcurrentHashMap ;
67+ import java .util .concurrent .atomic .AtomicBoolean ;
6868import okio .Okio ;
6969import org .slf4j .Logger ;
7070import org .slf4j .LoggerFactory ;
@@ -97,12 +97,16 @@ public class AppSecConfigServiceImpl implements AppSecConfigService {
9797 .build ()
9898 .adapter (Types .newParameterizedType (Map .class , String .class , Object .class ));
9999
100- private boolean hasUserWafConfig ;
101- private boolean defaultConfigActivated ;
102- private final Set <String > usedDDWafConfigKeys = new HashSet <>();
100+ private volatile boolean hasUserWafConfig ;
101+ private volatile boolean defaultConfigActivated ;
102+ private final AtomicBoolean subscribedToRulesAndData = new AtomicBoolean ();
103+ private final Set <String > usedDDWafConfigKeys =
104+ Collections .newSetFromMap (new ConcurrentHashMap <>());
105+ private final Set <String > emptyConfigKeys = Collections .newSetFromMap (new ConcurrentHashMap <>());
103106 private final String DEFAULT_WAF_CONFIG_RULE = "DEFAULT_WAF_CONFIG" ;
104107 private String currentRuleVersion ;
105108 private List <AppSecModule > modulesToUpdateVersionIn ;
109+ private long rulesAndDataCapabilities ;
106110
107111 public AppSecConfigServiceImpl (
108112 Config tracerConfig ,
@@ -122,13 +126,15 @@ private void subscribeConfigurationPoller() {
122126 subscribeAsmFeatures ();
123127
124128 if (!hasUserWafConfig ) {
125- subscribeRulesAndData ();
129+ updateRulesAndDataSubscription ();
126130 } else {
127131 log .debug ("Will not subscribe to ASM, ASM_DD and ASM_DATA (AppSec custom rules in use)" );
128132 }
129133
130134 this .configurationPoller .addConfigurationEndListener (applyRemoteConfigListener );
135+ }
131136
137+ private long buildRulesAndDataCapabilities () {
132138 long capabilities =
133139 CAPABILITY_ASM_DD_RULES
134140 | CAPABILITY_ASM_IP_BLOCKING
@@ -154,13 +160,36 @@ private void subscribeConfigurationPoller() {
154160 capabilities |= CAPABILITY_ASM_RASP_LFI ;
155161 }
156162 }
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+ }
158175 }
159176
160177 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 (rulesAndDataCapabilities );
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 (rulesAndDataCapabilities );
192+ }
164193 }
165194
166195 public void modulesToUpdateVersionIn (List <AppSecModule > modules ) {
@@ -176,20 +205,26 @@ private class AppSecConfigChangesListener implements ProductListener {
176205 public void accept (ConfigKey configKey , byte [] content , PollingRateHinter pollingRateHinter )
177206 throws IOException {
178207 maybeInitializeDefaultConfig ();
179-
208+ final String key = configKey . toString ();
180209 if (content == null ) {
181- try {
182- wafBuilder .removeConfig (configKey .toString ());
183- } catch (UnclassifiedWafException e ) {
184- throw new RuntimeException (e );
210+ if (!emptyConfigKeys .remove (key )) {
211+ try {
212+ wafBuilder .removeConfig (key );
213+ } catch (UnclassifiedWafException e ) {
214+ throw new RuntimeException (e );
215+ }
185216 }
186217 } else {
187218 Map <String , Object > contentMap =
188219 ADAPTER .fromJson (Okio .buffer (Okio .source (new ByteArrayInputStream (content ))));
189- try {
190- handleWafUpdateResultReport (configKey .toString (), contentMap );
191- } catch (AppSecModule .AppSecModuleActivationException e ) {
192- throw new RuntimeException (e );
220+ if (contentMap == null || contentMap .isEmpty ()) {
221+ emptyConfigKeys .add (key );
222+ } else {
223+ try {
224+ handleWafUpdateResultReport (key , contentMap );
225+ } catch (AppSecModule .AppSecModuleActivationException e ) {
226+ throw new RuntimeException (e );
227+ }
193228 }
194229 }
195230 }
@@ -282,7 +317,6 @@ private void subscribeAsmFeatures() {
282317 Product .ASM_FEATURES ,
283318 AppSecFeaturesDeserializer .INSTANCE ,
284319 (configKey , newConfig , hinter ) -> {
285- maybeInitializeDefaultConfig ();
286320 if (newConfig == null ) {
287321 mergedAsmFeatures .removeConfig (configKey );
288322 } else {
@@ -341,6 +375,8 @@ public void init() {
341375 }
342376 this .mergedAsmFeatures .clear ();
343377 this .usedDDWafConfigKeys .clear ();
378+ this .emptyConfigKeys .clear ();
379+ this .rulesAndDataCapabilities = buildRulesAndDataCapabilities ();
344380
345381 if (wafConfig .isEmpty ()) {
346382 throw new IllegalStateException ("Expected default waf config to be available" );
@@ -353,9 +389,12 @@ public void init() {
353389 }
354390
355391 public void maybeSubscribeConfigPolling () {
392+ final ProductActivation appSecActivation = tracerConfig .getAppSecActivation ();
393+ if (appSecActivation == ProductActivation .FULLY_DISABLED ) {
394+ return ; // shouldn't happen but just in case.
395+ }
356396 if (this .configurationPoller != null ) {
357- if (hasUserWafConfig
358- && tracerConfig .getAppSecActivation () == ProductActivation .FULLY_ENABLED ) {
397+ if (hasUserWafConfig && appSecActivation == ProductActivation .FULLY_ENABLED ) {
359398 log .info (
360399 "AppSec will not use remote config because "
361400 + "there is a custom user configuration and AppSec is explicitly enabled" );
@@ -494,6 +533,7 @@ public void close() {
494533 this .configurationPoller .removeListeners (Product .ASM );
495534 this .configurationPoller .removeListeners (Product .ASM_FEATURES );
496535 this .configurationPoller .removeConfigurationEndListener (applyRemoteConfigListener );
536+ this .subscribedToRulesAndData .set (false );
497537 this .configurationPoller .stop ();
498538 if (this .wafBuilder != null ) {
499539 this .wafBuilder .close ();
@@ -526,6 +566,7 @@ private void setAppSecActivation(final AppSecFeatures.Asm asm) {
526566 if (AppSecSystem .isActive () != newState ) {
527567 log .info ("AppSec {} (runtime)" , newState ? "enabled" : "disabled" );
528568 AppSecSystem .setActive (newState );
569+ updateRulesAndDataSubscription ();
529570 }
530571 }
531572
0 commit comments