Skip to content

Commit 51532a8

Browse files
Fix NPE in AppSecConfigServiceImpl (#9165)
1 parent 795d68a commit 51532a8

File tree

3 files changed

+80
-19
lines changed

3 files changed

+80
-19
lines changed

dd-java-agent/appsec/src/main/java/com/datadog/appsec/config/AppSecConfigServiceImpl.java

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ public class AppSecConfigServiceImpl implements AppSecConfigService {
7575
private final ConfigurationPoller configurationPoller;
7676
private WafBuilder wafBuilder;
7777

78-
private MergedAsmFeatures mergedAsmFeatures;
79-
private volatile boolean initialized;
78+
private final MergedAsmFeatures mergedAsmFeatures = new MergedAsmFeatures();
8079

8180
private final ConcurrentHashMap<String, SubconfigListener> subconfigListeners =
8281
new ConcurrentHashMap<>();
@@ -173,9 +172,7 @@ private class AppSecConfigChangesListener implements ProductListener {
173172
@Override
174173
public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollingRateHinter)
175174
throws IOException {
176-
if (!initialized) {
177-
throw new IllegalStateException();
178-
}
175+
maybeInitializeDefaultConfig();
179176

180177
if (content == null) {
181178
try {
@@ -219,8 +216,8 @@ public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollin
219216
}
220217
defaultConfigActivated = false;
221218
}
222-
super.accept(configKey, content, pollingRateHinter);
223219
usedDDWafConfigKeys.add(configKey.toString());
220+
super.accept(configKey, content, pollingRateHinter);
224221
}
225222

226223
@Override
@@ -282,13 +279,7 @@ private void subscribeAsmFeatures() {
282279
Product.ASM_FEATURES,
283280
AppSecFeaturesDeserializer.INSTANCE,
284281
(configKey, newConfig, hinter) -> {
285-
if (!hasUserWafConfig && !defaultConfigActivated) {
286-
// features activated in runtime
287-
init();
288-
}
289-
if (!initialized) {
290-
throw new IllegalStateException();
291-
}
282+
maybeInitializeDefaultConfig();
292283
if (newConfig == null) {
293284
mergedAsmFeatures.removeConfig(configKey);
294285
} else {
@@ -305,10 +296,7 @@ private void subscribeAsmFeatures() {
305296

306297
private void distributeSubConfigurations(
307298
String key, AppSecModuleConfigurer.Reconfiguration reconfiguration) {
308-
if (usedDDWafConfigKeys.isEmpty() && !defaultConfigActivated && !hasUserWafConfig) {
309-
// no config left in the WAF builder, add the default config
310-
init();
311-
}
299+
maybeInitializeDefaultConfig();
312300
for (Map.Entry<String, SubconfigListener> entry : subconfigListeners.entrySet()) {
313301
SubconfigListener listener = entry.getValue();
314302
try {
@@ -320,6 +308,13 @@ private void distributeSubConfigurations(
320308
}
321309
}
322310

311+
private void maybeInitializeDefaultConfig() {
312+
if (usedDDWafConfigKeys.isEmpty() && !hasUserWafConfig && !defaultConfigActivated) {
313+
// no config left in the WAF builder, add the default config
314+
init();
315+
}
316+
}
317+
323318
@Override
324319
public void init() {
325320
Map<String, Object> wafConfig;
@@ -341,8 +336,8 @@ public void init() {
341336
} else {
342337
hasUserWafConfig = true;
343338
}
344-
this.mergedAsmFeatures = new MergedAsmFeatures();
345-
this.initialized = true;
339+
this.mergedAsmFeatures.clear();
340+
this.usedDDWafConfigKeys.clear();
346341

347342
if (wafConfig.isEmpty()) {
348343
throw new IllegalStateException("Expected default waf config to be available");

dd-java-agent/appsec/src/main/java/com/datadog/appsec/config/MergedAsmFeatures.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,9 @@ private void mergeAutoUserInstrum(
4949
}
5050
target.autoUserInstrum = newValue;
5151
}
52+
53+
public void clear() {
54+
mergedData = null;
55+
configs.clear();
56+
}
5257
}

dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/config/AppSecConfigServiceImplSpecification.groovy

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,67 @@ class AppSecConfigServiceImplSpecification extends DDSpecification {
759759
p.toFile().delete()
760760
}
761761

762+
// https://github.com/DataDog/dd-trace-java/issues/9159
763+
void 'test initialization issues while applying remote config'() {
764+
setup:
765+
final key = new ParsedConfigKey('Test', '1234', 1, 'ASM_DD', 'ID')
766+
final service = new AppSecConfigServiceImpl(config, poller, reconf)
767+
config.getAppSecActivation() >> ProductActivation.ENABLED_INACTIVE
768+
769+
when:
770+
service.maybeSubscribeConfigPolling()
771+
772+
then:
773+
1 * poller.addListener(Product.ASM_DD, _) >> {
774+
listeners.savedWafDataChangesListener = it[1]
775+
}
776+
777+
when:
778+
listeners.savedWafDataChangesListener.accept(key, '''{"rules_override": [{"rules_target": [{"rule_id": "foo"}], "enabled": false}]}'''.getBytes(), NOOP)
779+
780+
then:
781+
noExceptionThrown()
782+
}
783+
784+
void 'config keys are added and removed to the set when receiving ASM_DD payloads'() {
785+
setup:
786+
final key = new ParsedConfigKey('Test', '1234', 1, 'ASM_DD', 'ID')
787+
final service = new AppSecConfigServiceImpl(config, poller, reconf)
788+
config.getAppSecActivation() >> ProductActivation.ENABLED_INACTIVE
789+
790+
when:
791+
service.maybeSubscribeConfigPolling()
792+
793+
then:
794+
1 * poller.addListener(Product.ASM_DD, _) >> {
795+
listeners.savedWafDataChangesListener = it[1]
796+
}
797+
1 * poller.addListener(Product.ASM_FEATURES, _, _) >> {
798+
listeners.savedFeaturesDeserializer = it[1]
799+
listeners.savedFeaturesListener = it[2]
800+
}
801+
802+
when:
803+
listeners.savedFeaturesListener.accept('asm_features conf',
804+
listeners.savedFeaturesDeserializer.deserialize('{"asm":{"enabled": true}}'.bytes),
805+
NOOP)
806+
807+
then:
808+
service.usedDDWafConfigKeys.empty
809+
810+
when:
811+
listeners.savedWafDataChangesListener.accept(key, '''{"rules_override": [{"rules_target": [{"rule_id": "foo"}], "enabled": false}]}'''.getBytes(), NOOP)
812+
813+
then:
814+
service.usedDDWafConfigKeys.toList() == [key.toString()]
815+
816+
when:
817+
listeners.savedWafDataChangesListener.remove(key, NOOP)
818+
819+
then:
820+
service.usedDDWafConfigKeys.empty
821+
}
822+
762823
private static AppSecFeatures autoUserInstrum(String mode) {
763824
return new AppSecFeatures().tap { features ->
764825
features.autoUserInstrum = new AppSecFeatures.AutoUserInstrum().tap { instrum ->

0 commit comments

Comments
 (0)