Skip to content

Add active WAF rules version to crash telemetry payload#11954

Draft
jandro996 wants to merge 3 commits into
masterfrom
update-crash-telemetry-payload
Draft

Add active WAF rules version to crash telemetry payload#11954
jandro996 wants to merge 3 commits into
masterfrom
update-crash-telemetry-payload

Conversation

@jandro996

@jandro996 jandro996 commented Jul 15, 2026

Copy link
Copy Markdown
Member

What Does This Do

  • ConfigManager gains updateCrashConfigEntry(String key, String value): patches a single entry of the already-written .cfg file in place — reads all existing key=value lines (including ones readConfig doesn't parse, like agent/hs_err/javacore_path), replaces/inserts the target key, and writes atomically via a temp file + renameTo. Unlike writeConfigToFile, it does not take a fresh Config snapshot and does not register a new shutdown hook, since it can be called repeatedly (once per Remote Config update).
  • ConfigManager caches the File written by writeConfigToPath in a volatile static field so updateCrashConfigEntry can locate it later — the .cfg path can't be recomputed elsewhere (HotSpot default temp dir vs J9 -Xdump:tool custom path differ). writeConfigToPath and updateCrashConfigEntry are both synchronized on the class monitor to avoid the initial write and a near-immediate Remote Config update racing on the same file.
  • StoredConfig (nested class) and its Builder gain a nullable wafRulesVersion field; ConfigManager.readConfig's switch adds a case "waf_rules_version".
  • WAFStatsReporter.setRulesVersion(String) now also calls ConfigManager.updateCrashConfigEntry("waf_rules_version", rulesetVersion) — this is the single mutation point for the WAF ruleset version, covering both the initial bundled-ruleset load and every subsequent Remote Config push.
  • CrashUploader emits waf_rules_version in the crash telemetry payload and error-tracking tags when storedConfig.wafRulesVersion is present.
  • dd-java-agent/appsec/build.gradle adds a dependency on dd-java-agent:agent-crashtracking (needed for WAFStatsReporter to call into ConfigManager).
  • Migrates WAFStatsReporterSpecification.groovy to JUnit 5 WAFStatsReporterTest.java.

Motivation

Correlate JVM crashes in production with the WAF ruleset version that was active at the time of the crash. Spin-off of APPSEC-62784 (SIGSEGV in ddwaf_run).

Additional Notes

None.

Contributor Checklist

Jira ticket: APPSEC-68788

Note: Once your PR is ready to merge, add it to the merge queue by commenting /merge. /merge -c cancels the queue request. /merge -f --reason "reason" skips all merge queue checks; please use this judiciously, as some checks do not run at the PR-level. For more information, see this doc.

- ConfigManager: patch a single .cfg entry in place (updateCrashConfigEntry)
  without a fresh Config snapshot or a new shutdown hook
- WAFStatsReporter.setRulesVersion: funnel the WAF ruleset version into the
  crash config on every load/RC update
- CrashUploader: emit waf_rules_version in the telemetry payload and
  error-tracking ddtags when present
- Migrate WAFStatsReporterSpecification.groovy to JUnit5 WAFStatsReporterTest.java
- ConfigManager: synchronize writeConfigToPath to match updateCrashConfigEntry,
  closing a race window on the cached cfg file
- ConfigManager: delete the orphaned temp file when writing to it fails,
  not just when the atomic rename fails
- ConfigManagerTest: add coverage for the no-op paths (no cached cfg,
  null key/value) and reset the cached static field in @AfterEach
- WAFStatsReporterTest: reset the cached static field in @AfterEach for
  test isolation
@jandro996 jandro996 added tag: ai generated Largely based on code generated by an AI or LLM comp: crash tracking Crash Tracking comp: asm waf Application Security Management (WAF) type: feature Enhancements and improvements labels Jul 15, 2026
@jandro996

Copy link
Copy Markdown
Member Author

@codex review

@jandro996 jandro996 added tag: ai generated Largely based on code generated by an AI or LLM and removed tag: ai generated Largely based on code generated by an AI or LLM labels Jul 15, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f280128e09

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

public static synchronized void writeConfigToPath(File scriptFile, String... additionalEntries) {
String cfgFileName = getBaseName(scriptFile) + PID_PREFIX + PidHelper.getPid() + ".cfg";
File cfgFile = new File(scriptFile.getParentFile(), cfgFileName);
ConfigManager.cfgFile = cfgFile;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the crash uploader cfg path from being overwritten

writeConfigToPath is used for both the crash uploader and the OOME notifier; in the normal initializer path initializeCrashUploader(...) runs before initializeOOMENotifier(...), whose OOMENotifierScriptInitializer.initialize calls writeConfigToPath again. Caching a single cfgFile here means the later OOME config (dd_oome_notifier_pid*.cfg) replaces the crash uploader config path, so WAFStatsReporter.setRulesVersion patches the OOME cfg and the upload_crash script still reads a crash cfg without waf_rules_version.

Useful? React with 👍 / 👎.

Comment on lines +227 to +229
File cfgFile = ConfigManager.cfgFile;
if (cfgFile == null || key == null || value == null) {
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve WAF version until crash config exists

On Java 9+ the crashtracking initializer is scheduled asynchronously before AppSec starts, so AppSec can call setRulesVersion while this cached file is still null. Returning here permanently drops the bundled ruleset version; when writeConfigToPath later creates the crash uploader cfg it has no way to add it, and unless another remote-config update arrives crash telemetry omits the active WAF version.

Useful? React with 👍 / 👎.

Comment on lines +244 to +246
if (!tmpFile.renameTo(cfgFile)) {
LOGGER.debug("Failed to atomically replace config file: {}", cfgFile);
tmpFile.delete(); // best-effort cleanup; failure is acceptable here

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Replace the existing cfg file on Windows

When crash tracking runs on Windows, this path attempts to rename a temp file over an already-existing .cfg. File.renameTo does not reliably replace an existing target there, so rules-version updates can leave the original cfg unchanged and delete the patched temp file, causing Windows crash uploads to miss waf_rules_version.

Useful? React with 👍 / 👎.

@datadog-datadog-us1-prod

datadog-datadog-us1-prod Bot commented Jul 15, 2026

Copy link
Copy Markdown

🎯 Code Coverage (details)
Patch Coverage: 78.26%
Overall Coverage: 57.18% (-0.03%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 53143f8 | Docs | Datadog PR Page | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 13.96 s 14.02 s [-1.3%; +0.5%] (no difference)
startup:insecure-bank:tracing:Agent 13.05 s 13.04 s [-0.6%; +0.8%] (no difference)
startup:petclinic:appsec:Agent 16.92 s 16.09 s [+0.7%; +9.7%] (maybe worse)
startup:petclinic:iast:Agent 15.68 s 16.96 s [-13.2%; -1.9%] (unstable)
startup:petclinic:profiling:Agent 16.63 s 16.80 s [-2.2%; +0.1%] (no difference)
startup:petclinic:sca:Agent 16.86 s 16.80 s [-0.7%; +1.3%] (no difference)
startup:petclinic:tracing:Agent 15.63 s 16.19 s [-7.5%; +0.6%] (no difference)

Commit: 53143f80 · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

- Stop the OOME notifier's .cfg write from clobbering the crash-uploader's
  cached path so waf_rules_version reaches the right file
- Buffer updateCrashConfigEntry calls until the .cfg file is cached, so the
  initial bundled WAF ruleset version isn't lost if it arrives before the
  deferred Java 9+ crash-tracking init finishes
- Fall back to delete+rename when replacing the .cfg file on Windows, where
  File.renameTo does not reliably replace an existing target
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: asm waf Application Security Management (WAF) comp: crash tracking Crash Tracking tag: ai generated Largely based on code generated by an AI or LLM type: feature Enhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant