Skip to content

Commit bb99a5d

Browse files
committed
wip
1 parent 98cb8f6 commit bb99a5d

12 files changed

+507
-663
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ private static void loadModules(EventDispatcher eventDispatcher, Monitoring moni
163163
for (AppSecModule module : modules) {
164164
log.debug("Starting appsec module {}", module.getName());
165165
try {
166-
AppSecConfigService.TransactionalAppSecModuleConfigurer cfgObject;
167-
cfgObject = APP_SEC_CONFIG_SERVICE.createAppSecModuleConfigurer();
166+
AppSecConfigService.TransactionalAppSecModuleConfigurer cfgObject =
167+
APP_SEC_CONFIG_SERVICE.createAppSecModuleConfigurer();
168168
module.config(cfgObject, wafBuilder);
169169
cfgObject.commit();
170170
} catch (RuntimeException | AppSecModule.AppSecModuleActivationException t) {

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

+80-66
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.datadog.appsec.util.StandardizedLogging;
3030
import com.datadog.ddwaf.WafBuilder;
3131
import com.datadog.ddwaf.exception.InvalidRuleSetException;
32+
import datadog.remoteconfig.ConfigurationChangesTypedListener;
3233
import datadog.remoteconfig.ConfigurationEndListener;
3334
import datadog.remoteconfig.ConfigurationPoller;
3435
import datadog.remoteconfig.Product;
@@ -131,75 +132,85 @@ private void subscribeConfigurationPoller(WafBuilder wafBuilder) {
131132

132133
private void subscribeRulesAndData(WafBuilder wafBuilder) {
133134
this.configurationPoller.addListener(
134-
Product.ASM_DD,
135-
AppSecConfigDeserializer.INSTANCE,
136-
(configKey, newConfig, hinter) -> {
137-
// read initialized so that the state is currentAppSecConfig is visible
138-
if (!initialized) {
139-
throw new IllegalStateException();
140-
}
141-
if (newConfig == null || newConfig.getRawConfig() == null) {
142-
log.debug("AppSec config given by remote config was pulled. Removing WAF config");
143-
wafBuilder.removeConfig(configKey);
144-
} else {
145-
if (defaultConfigActivated) {
146-
log.debug("Removing default config");
147-
wafBuilder.removeConfig(DEFAULT_WAF_CONFIG_RULE);
148-
defaultConfigActivated = false;
149-
}
150-
try {
151-
wafBuilder.addOrUpdateConfig(configKey, newConfig.getRawConfig());
152-
} catch (InvalidRuleSetException e) {
153-
throw new RuntimeException(e);
154-
}
155-
}
156-
this.currentAppSecConfig.setDdConfig(newConfig);
157-
// base rules can contain all rules/data/exclusions/etc
158-
this.currentAppSecConfig.dirtyStatus.markAllDirty();
159-
});
135+
Product.ASM_DD, AppSecConfigDeserializer.INSTANCE, asmDDTypedListener(wafBuilder));
160136
this.configurationPoller.addListener(
161-
Product.ASM_DATA,
162-
AppSecDataDeserializer.INSTANCE,
163-
(configKey, newConfig, hinter) -> {
164-
if (!initialized) {
165-
throw new IllegalStateException();
166-
}
167-
if (newConfig == null) {
168-
currentAppSecConfig.mergedAsmData.removeConfig(configKey);
169-
wafBuilder.removeConfig(configKey);
170-
} else {
171-
currentAppSecConfig.mergedAsmData.addConfig(configKey, newConfig);
172-
try {
173-
wafBuilder.addOrUpdateConfig(configKey, newConfig.getRawConfig());
174-
} catch (InvalidRuleSetException e) {
175-
throw new RuntimeException(e);
176-
}
177-
}
178-
this.currentAppSecConfig.dirtyStatus.data = true;
179-
});
137+
Product.ASM_DATA, AppSecDataDeserializer.INSTANCE, asmDataTypedListener(wafBuilder));
180138
this.configurationPoller.addListener(
181-
Product.ASM,
182-
AppSecUserConfigDeserializer.INSTANCE,
183-
(configKey, newConfig, hinter) -> {
184-
if (!initialized) {
185-
throw new IllegalStateException();
186-
}
187-
DirtyStatus dirtyStatus;
188-
if (newConfig == null) {
189-
dirtyStatus = currentAppSecConfig.userConfigs.removeConfig(configKey);
190-
wafBuilder.removeConfig(configKey);
191-
} else {
192-
AppSecUserConfig userCfg = newConfig.build(configKey);
193-
dirtyStatus = currentAppSecConfig.userConfigs.addConfig(userCfg);
194-
try {
195-
wafBuilder.addOrUpdateConfig(configKey, newConfig.getRawConfig());
196-
} catch (InvalidRuleSetException e) {
197-
throw new RuntimeException(e);
198-
}
199-
}
139+
Product.ASM, AppSecUserConfigDeserializer.INSTANCE, asmTypedListener(wafBuilder));
140+
}
200141

201-
this.currentAppSecConfig.dirtyStatus.mergeFrom(dirtyStatus);
202-
});
142+
private ConfigurationChangesTypedListener<AppSecUserConfig.Builder> asmTypedListener(
143+
WafBuilder wafBuilder) {
144+
return (configKey, newConfig, hinter) -> {
145+
if (!initialized) {
146+
throw new IllegalStateException();
147+
}
148+
DirtyStatus dirtyStatus;
149+
if (newConfig == null) {
150+
dirtyStatus = currentAppSecConfig.getUserConfigs().removeConfig(configKey);
151+
wafBuilder.removeConfig(configKey);
152+
} else {
153+
AppSecUserConfig userCfg = newConfig.build(configKey);
154+
dirtyStatus = currentAppSecConfig.getUserConfigs().addConfig(userCfg);
155+
try {
156+
wafBuilder.addOrUpdateConfig(configKey, newConfig.getRawConfig());
157+
} catch (InvalidRuleSetException e) {
158+
throw new RuntimeException(e);
159+
}
160+
}
161+
162+
this.currentAppSecConfig.dirtyStatus.mergeFrom(dirtyStatus);
163+
};
164+
}
165+
166+
private ConfigurationChangesTypedListener<AppSecData> asmDataTypedListener(
167+
WafBuilder wafBuilder) {
168+
return (configKey, newConfig, hinter) -> {
169+
if (!initialized) {
170+
throw new IllegalStateException();
171+
}
172+
if (newConfig == null) {
173+
currentAppSecConfig.mergedAsmData.removeConfig(configKey);
174+
wafBuilder.removeConfig(configKey);
175+
} else {
176+
currentAppSecConfig.mergedAsmData.addConfig(configKey, newConfig);
177+
try {
178+
wafBuilder.addOrUpdateConfig(configKey, newConfig.getRawConfig());
179+
} catch (InvalidRuleSetException e) {
180+
log.debug("Could not add or update config {}, {}", configKey, e.ruleSetInfo);
181+
throw new RuntimeException(e);
182+
}
183+
}
184+
this.currentAppSecConfig.dirtyStatus.data = true;
185+
};
186+
}
187+
188+
private ConfigurationChangesTypedListener<AppSecConfig> asmDDTypedListener(
189+
WafBuilder wafBuilder) {
190+
return (configKey, newConfig, hinter) -> {
191+
// read initialized so that the state is currentAppSecConfig is visible
192+
if (!initialized) {
193+
throw new IllegalStateException();
194+
}
195+
if (newConfig == null || newConfig.getRawConfig() == null) {
196+
log.debug("AppSec config given by remote config was pulled. Removing WAF config");
197+
wafBuilder.removeConfig(configKey);
198+
} else {
199+
if (defaultConfigActivated) {
200+
log.debug("Removing default config");
201+
wafBuilder.removeConfig(DEFAULT_WAF_CONFIG_RULE);
202+
defaultConfigActivated = false;
203+
}
204+
try {
205+
wafBuilder.addOrUpdateConfig(configKey, newConfig.getRawConfig());
206+
} catch (InvalidRuleSetException e) {
207+
throw new RuntimeException(e);
208+
}
209+
}
210+
this.currentAppSecConfig.setDdConfig(newConfig);
211+
// base rules can contain all rules/data/exclusions/etc
212+
this.currentAppSecConfig.dirtyStatus.markAllDirty();
213+
};
203214
}
204215

205216
private void subscribeAsmFeatures() {
@@ -213,6 +224,9 @@ private void subscribeAsmFeatures() {
213224
if (newConfig == null) {
214225
mergedAsmFeatures.removeConfig(configKey);
215226
} else {
227+
if (!configKey.equals(CurrentAppSecConfig.DEFAULT_KEY)) {
228+
mergedAsmFeatures.removeConfig(CurrentAppSecConfig.DEFAULT_KEY);
229+
}
216230
mergedAsmFeatures.addConfig(configKey, newConfig);
217231
}
218232
});

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

+14-17
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,27 @@ public class AppSecDataDeserializer implements ConfigurationDeserializer<AppSecD
1818
private AppSecDataDeserializer() {}
1919

2020
@Override
21+
@SuppressWarnings("unchecked")
2122
public AppSecData deserialize(byte[] content) throws IOException {
2223
if (content == null || content.length == 0) {
2324
return null;
2425
}
25-
return deserialize(new ByteArrayInputStream(content));
26-
}
27-
28-
@SuppressWarnings("unchecked")
29-
private AppSecData deserialize(InputStream is) throws IOException {
26+
InputStream is = new ByteArrayInputStream(content);
3027
AppSecData appSecData = ADAPTER.fromJson(Okio.buffer(Okio.source(is)));
28+
if (appSecData == null) {
29+
return null;
30+
}
3131
is.reset();
32-
if (appSecData != null && is.available() > 0) {
33-
appSecData.setRawConfig(MOSHI.adapter(Map.class).fromJson(Okio.buffer(Okio.source(is))));
34-
if (appSecData.getRawConfig().containsKey("rules_data")) {
35-
appSecData.getRawConfig().put("rules", appSecData.getRawConfig().get("rules_data"));
36-
appSecData.getRawConfig().remove("rules_data");
37-
}
38-
if (appSecData.getRawConfig().containsKey("exclusion_data")) {
39-
appSecData
40-
.getRawConfig()
41-
.put("exclusions", appSecData.getRawConfig().get("exclusion_data"));
42-
appSecData.getRawConfig().remove("exclusion_data");
43-
}
32+
appSecData.setRawConfig(MOSHI.adapter(Map.class).fromJson(Okio.buffer(Okio.source(is))));
33+
if (appSecData.getRawConfig().containsKey("rules_data")) {
34+
appSecData.getRawConfig().put("rules", appSecData.getRawConfig().get("rules_data"));
35+
appSecData.getRawConfig().remove("rules_data");
4436
}
37+
if (appSecData.getRawConfig().containsKey("exclusion_data")) {
38+
appSecData.getRawConfig().put("exclusions", appSecData.getRawConfig().get("exclusion_data"));
39+
appSecData.getRawConfig().remove("exclusion_data");
40+
}
41+
4542
return appSecData;
4643
}
4744
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.util.List;
99
import java.util.Optional;
1010

11-
class CollectedUserConfigs extends AbstractList<AppSecUserConfig> {
11+
public class CollectedUserConfigs extends AbstractList<AppSecUserConfig> {
1212
private final List<AppSecUserConfig> userConfigs = new LinkedList<>();
1313

1414
public DirtyStatus addConfig(AppSecUserConfig newUserConfig) {

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

+20-16
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,22 @@ public class CurrentAppSecConfig {
2121
public static final String DEFAULT_KEY = "default_java_config";
2222

2323
private AppSecConfig ddConfig; // assume there's only one of these
24-
CollectedUserConfigs userConfigs = new CollectedUserConfigs();
25-
MergedAsmData mergedAsmData = new MergedAsmData(new HashMap<>());
26-
public final DirtyStatus dirtyStatus = new DirtyStatus();
27-
private String configKey;
24+
private final CollectedUserConfigs userConfigs = new CollectedUserConfigs();
2825

29-
public CurrentAppSecConfig() {
30-
configKey = DEFAULT_KEY;
26+
public MergedAsmData getMergedAsmData() {
27+
return mergedAsmData;
3128
}
3229

33-
public CurrentAppSecConfig(String configKey) {
34-
this.configKey = configKey;
35-
}
30+
MergedAsmData mergedAsmData = new MergedAsmData(new HashMap<>());
31+
public final DirtyStatus dirtyStatus = new DirtyStatus();
3632

3733
@SuppressWarnings("unchecked")
3834
public void setDdConfig(AppSecConfig newConfig) {
3935
this.ddConfig = newConfig;
40-
36+
if (newConfig == null) {
37+
mergedAsmData.removeConfig(MergedAsmData.KEY_BUNDLED_DATA);
38+
return;
39+
}
4140
final Map<String, Object> rawConfig = newConfig.getRawConfig();
4241
List<Map<String, Object>> rules = (List<Map<String, Object>>) rawConfig.get("rules_data");
4342
List<Map<String, Object>> exclusions =
@@ -52,16 +51,16 @@ public void setDdConfig(AppSecConfig newConfig) {
5251
}
5352
}
5453

55-
public String getKey() {
56-
return configKey;
54+
public String getVersion() {
55+
return ddConfig.getVersion();
5756
}
5857

59-
public void setKey(String key) {
60-
this.configKey = key;
58+
public AppSecConfig getDdConfig() {
59+
return ddConfig;
6160
}
6261

63-
public boolean keyIsDefault() {
64-
return DEFAULT_KEY.equals(configKey);
62+
public CollectedUserConfigs getUserConfigs() {
63+
return userConfigs;
6564
}
6665

6766
public static class DirtyStatus {
@@ -103,6 +102,11 @@ public boolean isDirtyForActions() {
103102
}
104103

105104
public AppSecConfig getMergedUpdateConfig() throws IOException {
105+
if (ddConfig == null) {
106+
ddConfig =
107+
AppSecConfig.valueOf(
108+
Collections.singletonMap("version", "2.0")); // placeholder for empty config
109+
}
106110
if (!dirtyStatus.isAnyDirty()) {
107111
throw new IllegalStateException(
108112
"Can't call getMergedUpdateConfig without any dirty property");

0 commit comments

Comments
 (0)