Skip to content

Commit 0623c3c

Browse files
committed
helper-rust: avoid unnecessary waf updates; remove old cfd before adding
1 parent 7fb4314 commit 0623c3c

File tree

5 files changed

+195
-76
lines changed

5 files changed

+195
-76
lines changed

appsec/helper-rust/src/rc.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,26 @@ impl<'a> Iterator for ConfigIter<'a> {
240240
}
241241
}
242242

243+
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
244+
pub struct RcPath(String);
245+
impl RcPath {
246+
pub fn as_str(&self) -> &str {
247+
&self.0
248+
}
249+
}
250+
impl AsRef<str> for RcPath {
251+
fn as_ref(&self) -> &str {
252+
self.as_str()
253+
}
254+
}
255+
243256
#[derive(Debug, PartialEq, Eq)]
244257
pub struct Config<'a> {
245258
shm_path: &'a Path,
246-
rc_path: String,
259+
rc_path: RcPath,
247260
}
248261
impl<'a> Config<'a> {
249-
pub fn rc_path(&self) -> &str {
262+
pub fn rc_path(&self) -> &RcPath {
250263
&self.rc_path
251264
}
252265

@@ -281,7 +294,7 @@ impl<'a> Config<'a> {
281294

282295
Ok(Config {
283296
shm_path: Path::new(OsStr::from_bytes(shm_path)),
284-
rc_path,
297+
rc_path: RcPath(rc_path),
285298
})
286299
}
287300

@@ -333,9 +346,9 @@ pub struct ParsedConfigKey {
333346
}
334347

335348
impl ParsedConfigKey {
336-
pub fn from_rc_path(rc_path: &str) -> Option<Self> {
349+
pub fn from_rc_path(rc_path: &RcPath) -> Option<Self> {
337350
// Format: (datadog/<org_id> | employee)/<PRODUCT>/<config_id>/<name>
338-
let parts: Vec<&str> = rc_path.split('/').collect();
351+
let parts: Vec<&str> = rc_path.as_str().split('/').collect();
339352

340353
if parts.len() >= 4 && parts[0] == "datadog" {
341354
// datadog/<org_id>/<PRODUCT>/<config_id>/...

appsec/helper-rust/src/service.rs

Lines changed: 69 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -348,64 +348,47 @@ impl Service {
348348
) -> anyhow::Result<()> {
349349
debug!("Applying config for runtime id {}", cfg_dir.runtime_id()?);
350350

351+
let new_configs = cfg_dir
352+
.iter()
353+
.map_err(|e| {
354+
let any_error = e.context("Failed to start iteration over configs");
355+
self.log_general_rc_error(&any_error);
356+
any_error
357+
})?
358+
.map(|res_cfg| {
359+
res_cfg
360+
.map_err(|e| {
361+
let any_error = e.context("Failure during config iteration");
362+
self.log_general_rc_error(&any_error);
363+
any_error
364+
})
365+
.map(|cfg| cfg.rc_path().clone())
366+
})
367+
.collect::<Result<HashSet<_>, anyhow::Error>>()?;
368+
351369
let mut new_snapshot = (**self.config_snapshot.load()).clone();
352-
let mut new_configs = HashSet::new();
353370
let mut waf_changed = false;
354371
let mut all_diagnostics = Vec::new();
355372
let mut rules_version: Option<String> = None;
356373

357-
let maybe_cfg_iter = cfg_dir.iter();
358-
match maybe_cfg_iter {
359-
Ok(cfg_iter) => {
360-
for maybe_cfg in cfg_iter {
361-
// Handle each new/updated config
362-
let cfg = maybe_cfg?;
363-
let rc_path = cfg.rc_path();
364-
new_configs.insert(rc_path.to_string());
365-
366-
let result = self.apply_config_file(
367-
&cfg,
368-
rc_path,
369-
state,
370-
&mut all_diagnostics,
371-
&mut rules_version,
372-
&mut new_snapshot,
373-
&mut waf_changed,
374-
);
375-
if let Err(e) = result {
376-
self.log_product_rc_error(rc_path, &self.logs_collector, e);
377-
}
378-
}
379-
}
380-
Err(e) => {
381-
self.log_general_rc_error(e.context("Failed to iterate over configs"));
382-
}
383-
}
384-
385-
for maybe_cfg in cfg_dir.iter()? {
386-
let cfg = maybe_cfg?;
387-
let rc_path = cfg.rc_path();
388-
new_configs.insert(rc_path.to_string());
389-
}
390-
391374
// Handle removal of configs
392375
for old_path in state.last_configs.difference(&new_configs) {
393-
if old_path.contains("/ASM_FEATURES/") {
376+
if old_path.as_str().contains("/ASM_FEATURES/") {
394377
state.asm_feature_config_manager.remove(old_path);
395378
} else {
396-
let res = self.waf.remove_config(old_path);
379+
let res = self.waf.remove_config(old_path.as_str());
397380
match res {
398381
Ok(true) => {
399-
debug!("Removed WAF config: {}", old_path);
382+
debug!("Removed WAF config: {:?}", old_path);
400383
waf_changed = true;
401384
}
402385
Ok(false) => {
403-
warning!("No WAF config found to remove: {}", old_path);
386+
warning!("No WAF config found to remove: {:?}", old_path);
404387
}
405388
Err(e) => {
406-
self.log_general_rc_error(e.context(format!(
389+
self.log_general_rc_error(&e.context(format!(
407390
concat!(
408-
"Failed to remove WAF config {}; ",
391+
"Failed to remove WAF config {:?}; ",
409392
"this should happen only when reading the default config"
410393
),
411394
old_path
@@ -414,6 +397,28 @@ impl Service {
414397
}
415398
}
416399
}
400+
401+
// cfg_dir iteration worked above, so it will presumably work here too
402+
let cfg_iter = cfg_dir.iter()?;
403+
for maybe_cfg in cfg_iter {
404+
// Handle each config in the new config directory
405+
let cfg = maybe_cfg?;
406+
let rc_path = cfg.rc_path();
407+
408+
let result = self.apply_config_file(
409+
&cfg,
410+
rc_path,
411+
state,
412+
&mut all_diagnostics,
413+
&mut rules_version,
414+
&mut new_snapshot,
415+
&mut waf_changed,
416+
);
417+
if let Err(e) = result {
418+
self.log_product_rc_error(rc_path, &self.logs_collector, e);
419+
}
420+
}
421+
417422
state.last_configs = new_configs;
418423

419424
// Telemetry waf.config_errors metrics and telemetry logs - always process
@@ -445,7 +450,7 @@ impl Service {
445450
}
446451
Err(e) => {
447452
self.log_general_rc_error(
448-
e.context("Failed to rebuild WAF after config update"),
453+
&e.context("Failed to rebuild WAF after config update"),
449454
);
450455
false
451456
}
@@ -496,19 +501,24 @@ impl Service {
496501
fn apply_config_file(
497502
&self,
498503
cfg: &rc::Config,
499-
rc_path: &str,
504+
rc_path: &rc::RcPath,
500505
state: &mut RcUpdateState,
501506
all_diagnostics: &mut Vec<(
502-
String,
507+
rc::RcPath,
503508
libddwaf::object::WafOwnedDefaultAllocator<libddwaf::object::WafMap>,
504509
)>,
505510
rules_version: &mut Option<String>,
506511
new_snapshot: &mut ConfigSnapshot,
507512
waf_changed: &mut bool,
508513
) -> anyhow::Result<()> {
509514
let product = cfg.product();
515+
if state.last_configs.contains(rc_path) {
516+
debug!("Config already applied on previous update: {:?}", rc_path);
517+
return Ok(());
518+
}
519+
510520
debug!(
511-
"Processing config: rc_path={}, product={}",
521+
"Processing config: rc_path={:?}, product={}",
512522
rc_path,
513523
product.name()
514524
);
@@ -518,33 +528,35 @@ impl Service {
518528
let data = unsafe { shmem.as_slice() };
519529
state
520530
.asm_feature_config_manager
521-
.add(rc_path.to_string(), data)?;
531+
.add(rc_path.as_str().to_string(), data)?;
522532
Ok(())
523533
}
524534
"ASM_DD" | "ASM" | "ASM_DATA" => {
525535
let shmem = cfg.read()?;
526536
let data = unsafe { shmem.as_slice() };
527537

528538
let ruleset = waf_ruleset::WafRuleset::from_slice(data)
529-
.with_context(|| format!("Failed to parse WAF config for {}", rc_path))?;
539+
.with_context(|| format!("Failed to parse WAF config for {:?}", rc_path))?;
530540

531541
let waf_obj: libddwaf::object::WafObject = ruleset.into();
532542
let mut diagnostics = Default::default();
533543

534-
let upd_result =
535-
self.waf
536-
.add_or_update_config(rc_path, &waf_obj, Some(&mut diagnostics));
544+
let upd_result = self.waf.add_or_update_config(
545+
rc_path.as_str(),
546+
&waf_obj,
547+
Some(&mut diagnostics),
548+
);
537549

538550
if upd_result.is_ok() {
539-
debug!("Added/updated WAF config: {}", rc_path);
551+
debug!("Added/updated WAF config: {:?}", rc_path);
540552
if product.name() == "ASM_DD" {
541553
*rules_version = waf_diag::extract_ruleset_version(&diagnostics);
542554
*new_snapshot = new_snapshot.with_new_rules_version(rules_version.clone());
543555
}
544556
*waf_changed = true;
545557
}
546558

547-
all_diagnostics.push((rc_path.to_string(), diagnostics));
559+
all_diagnostics.push((rc_path.clone(), diagnostics));
548560
upd_result
549561
}
550562
_ => {
@@ -556,12 +568,12 @@ impl Service {
556568

557569
fn log_product_rc_error(
558570
&self,
559-
rc_path: &str,
571+
rc_path: &rc::RcPath,
560572
logs_submitter: &TelemetryLogsCollector,
561573
error: anyhow::Error,
562574
) {
563575
let message = format!(
564-
"Failed to apply config {} in service with config {:?}: {:#}",
576+
"Failed to apply config {:?} in service with config {:?}: {:#}",
565577
rc_path, self.fixed_config, error
566578
);
567579

@@ -589,13 +601,13 @@ impl Service {
589601
});
590602
} else {
591603
error!(
592-
"Can't parse RC path: {}; no submission of telemetry log",
604+
"Can't parse RC path: {:?}; no submission of telemetry log",
593605
rc_path
594606
);
595607
}
596608
}
597609

598-
fn log_general_rc_error(&self, error: anyhow::Error) {
610+
fn log_general_rc_error(&self, error: &anyhow::Error) {
599611
let message = format!(
600612
"Failed to apply config for service with config {:?}: {:#}",
601613
self.fixed_config, error
@@ -755,7 +767,7 @@ impl ServiceManagerInner {
755767

756768
struct RcUpdateState {
757769
poller: Option<rc::ConfigPoller>,
758-
last_configs: HashSet<String>,
770+
last_configs: HashSet<rc::RcPath>,
759771
asm_feature_config_manager: AsmFeatureConfigManager,
760772
pending_telemetry_metrics: TelemetryMetricsCollector,
761773
pending_init_diagnostics_legacy: Option<InitDiagnosticsLegacy>,

appsec/helper-rust/src/service/waf_diag.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
client::log::{debug, warning},
3-
rc::ParsedConfigKey,
3+
rc,
44
telemetry::{
55
LogLevel, TelemetryLog, TelemetryLogsCollector, TelemetryMetricSubmitter, TelemetryTags,
66
WAF_CONFIG_ERRORS,
@@ -22,26 +22,26 @@ const DIAGNOSTIC_KEYS: &[&str] = &[
2222
];
2323

2424
pub fn report_diagnostics_errors(
25-
rc_path: &str,
25+
rc_path: &rc::RcPath,
2626
diagnostics: &libddwaf::object::WafOwnedDefaultAllocator<libddwaf::object::WafMap>,
2727
rules_version: &str,
2828
metric_submitter: &mut impl TelemetryMetricSubmitter,
2929
log_submitter: &TelemetryLogsCollector,
3030
) {
3131
use libddwaf::object::WafObjectType;
3232

33-
let maybe_parsed_key = ParsedConfigKey::from_rc_path(rc_path);
33+
let maybe_parsed_key = rc::ParsedConfigKey::from_rc_path(rc_path);
3434
let parsed_key = match maybe_parsed_key {
3535
Some(parsed_key) => {
3636
debug!(
37-
"Processing diagnostics for {}: {} keys",
38-
rc_path,
37+
"Processing diagnostics for {:?}: {} keys",
38+
rc_path.as_str(),
3939
diagnostics.len()
4040
);
4141
parsed_key
4242
}
4343
None => {
44-
warning!("Failed to parse config key for {}", rc_path,);
44+
warning!("Failed to parse config key for {:?}", rc_path);
4545
return;
4646
}
4747
};
@@ -53,7 +53,7 @@ pub fn report_diagnostics_errors(
5353
let value = keyed.value();
5454
if value.object_type() != WafObjectType::Map {
5555
warning!(
56-
"Diagnostic key {} for {} is not a map, skipping",
56+
"Diagnostic key {} for {:?} is not a map, skipping",
5757
config_key,
5858
rc_path
5959
);
@@ -67,7 +67,7 @@ pub fn report_diagnostics_errors(
6767
}
6868

6969
debug!(
70-
"Diagnostic {} for {} has {} entries",
70+
"Diagnostic {} for {:?} has {} entries",
7171
config_key,
7272
rc_path,
7373
map.len()
@@ -245,7 +245,7 @@ pub fn extract_init_diagnostics_legacy(
245245

246246
fn submit_diagnostic_log(
247247
log_submitter: &TelemetryLogsCollector,
248-
parsed_key: &ParsedConfigKey,
248+
parsed_key: &rc::ParsedConfigKey,
249249
config_key: &str,
250250
suffix: &str,
251251
level: LogLevel,

appsec/tests/integration/build.gradle

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,10 @@ def runMainTask = { String phpVersion, String variant ->
671671
includeEngines('junit-jupiter')
672672
excludeEngines('junit-vintage')
673673

674+
if (!project.hasProperty('slowTests')) {
675+
excludeTags('slow')
676+
}
677+
674678
if (isMusl) {
675679
includeTags('musl')
676680
}
@@ -770,6 +774,10 @@ if (project.hasProperty('testClass')) {
770774
it.useJUnitPlatform {
771775
includeEngines('junit-jupiter')
772776
excludeEngines('junit-vintage')
777+
778+
if (!project.hasProperty('slowTests')) {
779+
excludeTags('slow')
780+
}
773781
}
774782

775783
it.testClassesDirs = sourceSets.test.output.classesDirs

0 commit comments

Comments
 (0)