Skip to content

fix(cisco-asa): repair condition syntax, geolocation keys and rule consumers - #2682

Draft
kryonsx wants to merge 9 commits into
utmstack:v11from
kryonsx:codex/v11-cisco-asa-review-20260923
Draft

kryonsx wants to merge 9 commits into
utmstack:v11from
kryonsx:codex/v11-cisco-asa-review-20260923

Conversation

@kryonsx

@kryonsx kryonsx commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The Cisco ASA filter and its three rules have defects that the filter's own patterns, the event engine and go-sdk establish on their own. This draft fixes those and defers everything that depends on Cisco's message wording.

Predecessor: #2598 (closed, historical context only).

Evidence basis

  • No Cisco ASA records were found on any reachable UTMStack v11 instance: 29 instances searched, 2 not reachable. The deployed filter and rules match this repository.
  • Cisco's syslog guide on www.cisco.com refused automated access (HTTP 403).
  • Every change below is therefore established by the filter itself, the EventProcessor source (commit 497bf53; the latest 8a3ade7 differs only in SDK versions and the CEL plugin's OpenSearch settings) and go-sdk v1.1.26/v1.1.33 behavior (unchanged for these points in v1.1.36), and validated with fabricated raw lines. Nothing here claims real-device validation.

Filter (filters/cisco/asa.yml, 3.0.3 → 3.1.1)

  • Conditions: 519 conditions such as log.messageId==106001 become equals("log.messageId", 106001), with ranges using greaterOrEqual/lessOrEqual and log.severity=="4" using equals.
    • go-sdk declares CEL variables only for top-level keys that already exist. When neither header grok matches, each raw condition fails to compile ("undeclared reference to 'log'"). The step is then skipped and an error is added: 519 errors, about 145 KB per event.
    • The same failure pattern appears on live records of another Cisco filter that uses this style.
    • Truth tables are identical on parsed events (1,987 comparisons).
  • Geolocation: the 16 geolocation steps now write to log.<field>Geolocation instead of log.<field>.geolocation. Writing under the address turned a string field into an object and lost the address. In a local OpenSearch test, one of the two documents was then rejected.
  • 106102/106103: "permitted" hits were stored as denied because two add steps ran in the wrong order.
  • Five parsing fixes:
    • 302013 direction is now set;
    • 302304 protocol grok matches Teardown;
    • 305011/305012 no longer cut action;
    • 302017 sets log.firewallUserTo and trims target.user;
    • the 113009/113011 second variant no longer overwrites the user with = name.
  • Three steps that could never write on the engine (3.1.1). The EventProcessor grok plugin counts an empty match as no match, and then the whole step writes nothing.
    • 302003/302004: the optional to was its own pattern, (to\s)?, which matched empty text on every 302003 line. A 302003 line whose local side is a host name was parsed by no step. The to is now read with the address and trimmed off.
    • 302022/302024/302026: the two steps for a mapped address without a port used {{.data}}, which on its own always matches empty text, so they never wrote. They now read [^/()]+, which only fits when no port follows.

Rules

  • ips_signature_matches.yml: the condition now requires origin.ip before its history lookup. Message 108003 is not parsed, so the lookup failed and the rule engine disabled the rule after five errors. A branch reading log.action, which nothing writes, is removed.
  • multiple_failed_vpn_attempts.yml: reads log.msg, which the filter writes, instead of log.message.
  • botnet_traffic_detection.yml is unchanged; see the deferred items below.

Validation

Re-checked on 2026-09-24 on the latest versions: official v11 d2479c1a (merged into this branch, no conflicts; plugins/alerts now pins go-sdk v1.1.36) and EventProcessor main 8a3ade7 (playground and every plugin on go-sdk v1.1.36; the geolocation plugin is built from v11, which pins v1.1.34).

  • SDK change: go-sdk v1.1.35+ keeps underscores in field names the parser plugins write, and v1.1.36 makes regexMatch match strings only. Neither changes this draft: none of the 129 names the filter writes contains an underscore, and every regexMatch/contains call reads a text field. No file needed a change.
  • Isolated EventProcessor playground (latest), 80 fabricated lines:
    • Unchanged filter: 6,228 errors (12 lines with 519 each).
    • Changed filter: 0 errors. All 84 assertions pass, with no unintended differences. Every line's stored fields equal the results recorded on the older build.
    • Rule run: 2 botnet alerts, 0 IPS alerts, 0 circuit-breaker alerts and 0 compile errors (24/24 checks). With the original filter and rules, the IPS rule still fails 6 history lookups and trips the circuit breaker.
    • Committed plugins/alerts/testdata/cisco-asa/replay.py: 46 events (41 plus the five below), 0 errors, every field as in expected.json, 2 botnet alerts, no circuit breaker, no history search.
  • Empty-match correction (3.1.1), on engine 8a3ade7: five new fabricated lines show both defects with version 3.1.0 (the 302003 host-name line kept only its result; the 302024 line had no mapped address) and the fix with 3.1.1; the other three lines are identical. The 80 private fabricated lines give identical events with both versions. go-sdk v1.1.36 rule replay over the 46 committed events: 13/13 checks; the earlier predicate checks, repeated with the new events: 90/90 and 65/65.
  • go-sdk v1.1.36 predicate replay: 90/90 and 65/65. History queries (threshold, window, placeholders) passed 36/36 against a local OpenSearch with go-sdk v1.1.33 earlier; those blocks are unchanged, and the SDK's history code (plugins/rules.go) is identical in v1.1.36.
  • Go tests: the full plugins/alerts suite passes on go-sdk v1.1.36: 51 pass, 11 skip (other technologies' private-evidence tests, same as the base), 0 fail. The eight new Cisco ASA tests all fail against the original files. The newest one tries every grok pattern alone on texts that start with each printable character and fails when one matches empty text at the start; it and the model test (now with the five new lines) also fail on version 3.1.0.

Deferred

These need Cisco's documentation or real device records.

  • Put the 113015/113017 user IP on origin.ip. This would make the failed-VPN rule work.
  • Parse 108003, 338001, 338002, 113021, 109034 and 611102, and decide which address is malicious in the botnet messages.
  • Header variants: no timestamp, no device-id, RFC 5424.
  • Origin/target direction per message, byte counters and device time.
  • Outcome values, owned by a separate outcome review.

The audit file lists each deferred item with the evidence that would unblock it.

Limits

  • Fixtures are fabricated.
  • The playground (EventProcessor 8a3ade7) and this module both use go-sdk v1.1.36; neither is claimed to match a customer deployment.
  • The index-rejection result comes from a local OpenSearch 2.13.0 test.
  • Production indexing, grouping, deduplication and notifications were not tested.

See filters/audits/cisco-asa.md.

🤖 Generated with Claude Code

kryonsx and others added 6 commits September 23, 2026 23:24
…ixes

Filter 3.1.0. Every change rests on the filter's own patterns and on the
EventProcessor and go-sdk behaviour. Cisco's syslog guide could not be
read, so nothing that depends on Cisco wording, address sides or result
meanings is changed.

- Rewrite the 519 raw log.messageId and log.severity comparisons in where
  clauses with equals, greaterOrEqual and lessOrEqual. A line that no
  header pattern accepts has no log object; each raw clause then failed
  to compile, its step was skipped and an error was stored (519 errors,
  about 145 KB, per event). The helpers return false instead. Parsed
  events are unchanged.
- Write the 16 log.* geolocation results to log.<field>Geolocation. The
  plugin wrote them under the address itself, which replaced the address
  with an object for every non-private address, so one field was a
  string in some events and an object in others.
- 106102/106103: run the denied add before the accepted add, so a
  permitted hit is no longer overwritten with denied.
- 302013: drop the direction grok's trailing pattern, which always met
  empty input, so log.direction is set.
- 302304: the protocol grok reads 'Teardown', the only prefix its source
  can have, so protocol is set.
- 305011/305012: the protocol grok no longer cuts action to its first
  words.
- 302017: log.firewallUserTo is set and target.user loses its leading
  parenthesis.
- 113009/113011: the second grok variant runs only when the first set no
  user, so origin.user is 'alice' rather than '= alice'.

The actionResult values and filters/cisco/firepower.yml are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Names, thresholds, windows, impact, adversary and MITRE labels are
unchanged. The botnet rule is unchanged.

- ips_signature_matches (v1.0.1): require origin.ip before the rule can
  match. 108003 events carry no origin.ip, so the {{.origin.ip}} history
  placeholder failed on each one and the CEL plugin disabled the rule
  after five events with a 'Circuit Breaker' alert. Drop the log.action
  branch: no ASA step writes log.action. The log.message text branches
  are left as they are.
- multiple_failed_vpn_attempts (v1.0.1): read log.msg, which the filter
  writes, instead of log.message, which nothing writes. This has no
  effect until the 113015 source address reaches origin.ip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cisco_asa_filter_test.go checks, with go-sdk v1.1.33, that no where
clause compares log.* directly and that the 524 helper clauses keep the
old results on 2,510 events with a log object and are false without
one; that no geolocation destination lies under a field that holds a
value; that a model of the engine's step plugins reproduces the
playground result for every non-geolocation field of 41 fabricated
lines, with a positive and a near-miss line for each step change; the
reordered and guarded where clauses in filter order; the unchanged rule
names, metadata, impact, grouping and history searches; 14 synthetic
rule cases; and that a rule with a history search never matches an
event that lacks its placeholder fields. All seven tests fail against
the original filter and rules.

testdata/cisco-asa/replay.py runs the same 41 lines through the public
EventProcessor playground with the filter, the three rules, the shared
grok definitions and invented geolocation data, and checks every field
and alert. The rules' OpenSearch address is a closed local port, so a
history search would fail and be reported. All inputs are invented, use
RFC 5737 and RFC 3849 addresses and example names, and are not taken
from Cisco's documentation, which could not be read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Evidence basis (no Cisco ASA record on any searchable instance, Cisco's
documentation unavailable), the basis and proof of each change, the
playground, SDK and Go test results, the deferred items with what would
unblock each, and the known limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in go-sdk v1.1.36 for plugins/alerts (field names keep underscores; regexMatch matches strings only) and the coordinated vendor re-baseline. No file overlaps this draft.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ngine

Official v11 d2479c1 (go-sdk v1.1.36 in plugins/alerts) and EventProcessor main 8a3ade7 (every playground plugin on v1.1.36). Field names now keep underscores and regexMatch matches strings only; neither changes this draft, because none of the 129 names the filter writes contains an underscore and every text-search call reads a string field. Full plugins/alerts suite: 50 pass, 11 skip, 0 fail. replay.py: 41 events, 2 botnet alerts, no Circuit Breaker, no history search. The 80 private lines give the same per-line results as the original review; the rule run passes 24 of 24 checks; the original rules still trip the Circuit Breaker. v1.1.36 predicate checks: 90 of 90 and 65 of 65.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kryonsx and others added 3 commits September 24, 2026 17:07
…n build

The newest published engine image, eventprocessor:v11.2.14 (built 2026-09-24 19:13 UTC), carries EventProcessor revision 8a3ade7 with go-sdk v1.1.36, built with go1.26.8 for linux/amd64. The local build used for the re-validation is the same source compiled natively for darwin/arm64 with go1.25.7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The EventProcessor 8a3ade7 grok plugin trims the remaining text before
each pattern and counts an empty match as no match, and then the whole
step writes nothing. Three patterns in filters/cisco/asa.yml match empty
text at the start:

- 302003/302004, first variant: '(to\s)?' is empty on every 302003 line,
  so a 302003 line whose local side is a host name was parsed by neither
  variant. The optional "to" is now read with the local address, and a
  trim step removes it.
- 302022/302024/302026: the two steps for a mapped address without a
  port read it with '{{.data}}', which on its own always matches empty
  text, so they never wrote. They now read '[^/()]+', which only fits
  when no port follows.

Filter version 3.1.1. Five fabricated lines are added to
testdata/cisco-asa (302003 with an address and with a host name, 302004
with "to", 302022 with a mapped port, 302024 without one). Their expected
fields were recorded from the 8a3ade7 playground after the declared checks
(F-C9, F-C10). TestCiscoASAGrokPatternsNeverMatchEmpty tries every
expanded pattern alone on texts that start with each printable character.
The model test and the new test both fail on 3.1.0 and pass on 3.1.1.
Two test comments now name the pinned go-sdk v1.1.36.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the version 3.1.1 section: what the grok plugin does with an empty
match, the three steps and their fix, and the checks on EventProcessor
8a3ade7. replay.py gives 46 events and the same two botnet alerts, the
80 private fabricated lines are unchanged, the go-sdk v1.1.36 rule replay
passes 13 of 13 checks and the earlier predicate checks 90 of 90 and 65
of 65, and the full suite has 51 passing and 11 skipped tests. The
fixture and test counts elsewhere in the audit are updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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