Skip to content

[akamai] Fix ingest pipeline failures and malformed geo codes in siem - #20969

Open
smnschndr wants to merge 2 commits into
elastic:mainfrom
smnschndr:akamai-fix-siem-pipeline-null-safety
Open

[akamai] Fix ingest pipeline failures and malformed geo codes in siem#20969
smnschndr wants to merge 2 commits into
elastic:mainfrom
smnschndr:akamai-fix-siem-pipeline-null-safety

Conversation

@smnschndr

@smnschndr smnschndr commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Proposed commit message

Three defects in the siem ingest pipeline caused Akamai security events to be indexed as event.kind: pipeline_error with all parsed fields lost, or to carry malformed values. All three are reproducible with POST _ingest/pipeline/_simulate, and all three are invisible to the current test fixture, whose two documents happen to avoid every affected path.

1. Painless NullPointerException in script_base64_decode_attackData_rule_573db939 — two triggers:

  • ctx.akamai.siem.rules = rules_array assumed akamai.siem already existed. That object is created only by the KV processors for httpMessage.requestHeaders / responseHeaders. Header logging is optional in the Akamai security configuration, and an empty string is stripped earlier by remove_http_message_request_headers_non_kv, so an event carrying attack rules but no headers failed with cannot access method/field [siem] from a null def reference.
  • ctx.json.attackData[key].length assumed all seven rule* fields were present. Akamai only sends the ones that apply to the event, so a missing key (e.g. ruleSelectors) failed with cannot access method/field [length] from a null def reference.

The script runs before every attackData, botData, clientData and userRiskData rename and before event.category / event.kind, so the entire tail of the pipeline was skipped. The document was not rejected — it was silently indexed as pipeline_error with an empty akamai.siem, no config_id, no policy_id, no rules and no related.ip, which means these attacks are invisible to detection rules and dashboards.

The same defensive pattern already exists in script_userRiskData_general_ade061bc in this file; the two scripts were simply inconsistent.

2. Malformed source.geo.region_iso_codeset_source_geo_region_iso_code_3a81fa3d builds its value from {{{json.geo.country}}}-{{{json.geo.regionCode}}} but ran after rename_json_geo_country_to_source_geo_country_iso_code_66520e30, which removes json.geo.country precisely when GeoIP did not resolve the address. Two wrong outcomes:

  • GeoIP miss plus an Akamai geo block → region_iso_code: "-NY" instead of "US-NY".
  • No geo data at all → the value renders as "-", which is not empty, so ignore_empty_value does not skip it. Every such event gained a synthetic source.geo object whose only field was region_iso_code: "-". The trailing null-strip script removes '', not "-".

The processor now runs before the rename and requires both parts to be present.

3. Unguarded client copyset_client_10dffeb7 copies client from source, but events without attackData.clientIP have no source object and copy_from fails on a missing path (field [source] not present as part of path [source]). This was masked in production by defect 2, which incidentally created a source object; fixing 2 on its own would have converted it into a live failure. The guard is added in the same change.

Testing

Added four pipeline test documents, one per affected path: attack rules without headers, a missing rule* key, an unresolvable client address with an Akamai geo block, and an event with no client IP. The two pre-existing expected documents are byte-identical after regeneration, so the change is a pure addition to the expected output.

Verified against a local stack:

elastic-package format / lint / check   → clean (only the SVR00009 exclusion already in validation.yml)
elastic-package test pipeline           → PASS
_ingest/simulate over the affected shapes → 0 documents with event.kind: pipeline_error
                                            source.geo.region_iso_code: "US-NY"

Note: RFC 5737 documentation addresses all resolve in the Elasticsearch test GeoIP databases, so the GeoIP-miss fixture uses an RFC 1918 address — that is the only way to exercise the fallback deterministically.

The whitespace-only changes in fields.yml and the two siem_otel policy test files are elastic-package format output — the package did not format cleanly before this change, and format --fail-fast now passes.

Out of scope, found during the same review and not addressed here: event.start is set from the collection timestamp on events without httpMessage.start; attackData.appliedAction is dropped and there is no event.type / event.outcome / event.action; the CEL-era terminate processor is dead code now that the CEL input is gone.

Checklist

  • I have reviewed tips for building integrations and this pull request is aligned with them.
  • I have verified that all data streams collect metrics or logs.
  • I have added an entry to my package's changelog.yml file.
  • I have verified that Kibana version constraints are current according to guidelines.
  • I have verified that any added dashboard complies with Kibana's Dashboard good practices — n/a, no dashboard changes.

How to test this PR locally

cd packages/akamai
elastic-package test pipeline --data-streams siem

To see the failures this fixes, check out the parent commit and run the same command against the new fixture documents.

🤖 Generated with Claude Code

https://claude.ai/code/session_014AUSnTxmZK2NCk1JuyyoiH

Three defects in the `siem` ingest pipeline caused Akamai security events
to be indexed as `event.kind: pipeline_error` with all parsed fields lost,
or to carry malformed values. All three are reproducible with
`POST _ingest/pipeline/_simulate`.

1. `script_base64_decode_attackData_rule_573db939` threw a NullPointerException
   in two cases:
   - `ctx.akamai.siem.rules = rules_array` assumed `akamai.siem` existed, but
     that object is only created by the KV processors for `requestHeaders` /
     `responseHeaders`. Events with attack rules but no HTTP headers (header
     logging is optional in the Akamai security configuration, and an empty
     string is stripped earlier by `remove_http_message_request_headers_non_kv`)
     failed with "cannot access method/field [siem] from a null def reference".
   - `ctx.json.attackData[key].length` assumed all seven `rule*` fields were
     present. Akamai only sends the ones that apply, so a missing key failed
     with "cannot access method/field [length] from a null def reference".

   Because the script runs before every `attackData`, `botData`, `clientData`
   and `userRiskData` rename and before `event.category` / `event.kind`, the
   whole tail of the pipeline was skipped for these events.

2. `set_source_geo_region_iso_code_3a81fa3d` ran after the rename that removes
   `json.geo.country`, so whenever GeoIP could not resolve the client address
   the field rendered as `-NY` instead of `US-NY`. With no geo data at all the
   value rendered as `-`, which is not empty and therefore not caught by
   `ignore_empty_value` — every such event gained a synthetic `source.geo`
   object holding only `region_iso_code: "-"`. The processor now runs before
   the rename and requires both parts.

3. `set_client_10dffeb7` copied `client` from `source` without a guard.
   Events without `attackData.clientIP` have no `source` object, and
   `copy_from` fails on a missing path. This was masked by defect 2, which
   incidentally created a `source` object; fixing 2 alone would have turned it
   into a live failure.

Adds four pipeline test documents covering each path: attack rules without
headers, a missing `rule*` key, an unresolvable client address with an Akamai
geo block, and an event with no client IP. The two pre-existing expected
documents are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014AUSnTxmZK2NCk1JuyyoiH
@smnschndr
smnschndr requested review from a team as code owners August 31, 2026 06:17
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown
Contributor

Reviewers

Buildkite won't run for external contributors automatically; you need to add a comment:

  • /test : will kick off a build in Buildkite.

NOTE: https://github.com/elastic/integrations/blob/main/.buildkite/pull-requests.json contains all those details.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014AUSnTxmZK2NCk1JuyyoiH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant