fix(cisco-switch): map real message addresses and stop MAC rule circuit breakers - #2689
Merged
osmontero merged 8 commits intoSep 25, 2026
Merged
Conversation
Names, impact, category, technique, adversary side, references,
thresholds and windows are unchanged. The VLAN hopping rule is
unchanged.
- mac_address_spoofing (v1.0.1): require origin.mac, leave out MAC flap
notifications (SW_MATM-4-MACFLAP_NOTIF), read log.msg instead of
log.message, and deduplicate by adversary.mac instead of grouping.
The filter never wrote origin.mac, so the {{.origin.mac}} history
placeholder failed on every flap, and after five failures the CEL
plugin disabled the rule with a 'Circuit Breaker' alert; the rule has
never produced a detection. The filter change that follows maps
origin.mac on every flap. Nothing shows that a flap means an address
was copied, and without this change every flap would run a history
search. The description now says that flaps are not used.
- arp_poisoning_detection (v1.0.1): require origin.ip before the rule
can match, and read log.msg instead of log.message. No step writes
origin.ip for SW_DAI, IP DUPADDR/SOURCEGUARD or the text branches, so
any match would fail its {{.origin.ip}} history search the same way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Filter 3.1.0. Every change rests on real switch records, the filter's
own patterns and the EventProcessor and go-sdk behaviour. Cisco's
system message guide could not be read, so nothing that depends on
what a message means is changed.
- Line 206: write the 'medium' severity condition as
equals("log.severity", "4") instead of log.severity=="4". A line
without a %FACILITY-SEVERITY-MNEMONIC header has no log object or no
log.severity, so the raw comparison failed and the engine stored the
error on the event. The helper returns false instead; severity is
unchanged on every tested line.
- SW_MATM-4-MACFLAP_NOTIF: write the flapping address to origin.mac, the
VLAN to log.vlan, and the two interfaces, in the order the message
gives them, to log.firstPort and log.secondPort. Interface names stay
under log.*; origin.port holds numbers only.
- SISF-4-EXCESS_ARP_ACTIVITY: the client address to origin.mac.
- SSH-4-SSH2_UNEXPECTED_MSG and SSH-5-SSH_CLOSE: the client address to
origin.ip.
- DHCPD-4-PING_CONFLICT: the pinged address to target.ip.
- SYS-3-LOGGINGHOST_FAIL and SYS-6-LOGGINGHOST_STARTSTOP: the logging
host to target.ip and its port to target.port as a number.
Each new step runs only for its facility and mnemonic and writes nothing
unless the whole text shape matches. The actionResult steps and the
severity words are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cisco_switch_filter_test.go checks, with go-sdk v1.1.33: that no where clause compares log.* directly, and that every clause evaluates without an error on a draft without a log object, one without a severity and a parsed one; that the severity steps keep their result on every level from 0 to 7; that a model of the engine's step plugins reproduces the playground result for every stored field of 44 fabricated lines, with a positive and a near-miss line for each new mapping; that interface names never reach origin.port or target.port; the unchanged names, metadata, impact and history searches of the three rules, the MAC rule's deduplication and the unchanged VLAN condition; 28 synthetic rule cases; that the MAC and ARP rules match none of the fabricated lines and the VLAN rule exactly the six SW_VLAN/DTP lines; and that a rule with a history search never matches an event that lacks its placeholder fields. All eight tests fail against the original filter and rules. testdata/cisco-switch/replay.py runs the same 44 lines through the public EventProcessor playground with the filter, the three rules and the shared grok definitions, 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: MAC addresses in the locally administered 02:00:00:xx:xx:xx range in Cisco's dotted form, RFC 5737 and RFC 3849 addresses, and example names. None is taken from Cisco's documentation, which could not be read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Evidence basis (real switch records on two instances, given as counts and header shapes only; the production circuit-breaker alerts of the MAC rule; Cisco's documentation unavailable), the basis and proof of each change, the MAC rule decision with the estimated volumes of each option as an owner decision, the playground, SDK and Go test results, the deferred items with what would unblock each, the customer-side routing note and the known limits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the playground run in which two contrived lines gave the committed MAC and ARP rules a true condition: both rules reached their history search with the value resolved, and both searches failed because no OpenSearch was listening. State that no history search completed. 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>
…d engine 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 25 names the filter writes contains an underscore and every text-search call reads a string field. Full plugins/alerts suite: 51 pass, 11 skip, 0 fail. replay.py: 44 events, 6 VLAN alerts, no Circuit Breaker, no history search. The 398 private inputs and the 2,823 distinct texts give event-for-event the same output as the original review; rule runs 14 of 14; the original rules still trip the Circuit Breaker. v1.1.36 replay: committed MAC and ARP rules match none of 229,017 real records; 15 of 15 synthetic checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Real Cisco switch logs show that the MAC spoofing rule keeps disabling itself in production. The filter never writes the MAC address that the rule's history search needs. This draft maps the addresses that real switch messages carry. It also keeps MAC flap notifications out of the spoofing rule, guards the ARP rule's history search, and fixes a raw CEL condition that errors on every non-switch line.
Predecessor: #2599 (closed, historical context only).
Evidence (read-only, described without identifying data)
%SW_MATM-4-MACFLAP_NOTIF;Circuit Breaker: MAC Address Spoofing Detectionalerts (2026-08-19 to 09-21) and no real MAC spoofing alert.expression value cannot be nil after placeholder resolution.497bf53; the latest8a3ade7differs only in SDK versions and the CEL plugin's OpenSearch settings) and go-sdk behavior (unchanged for these points in v1.1.36).Filter (
filters/cisco/cs_switch.yml, 3.0.2 → 3.1.0)where: log.severity=="4"→equals("log.severity", "4"). The raw form fails to compile on events without alogobject ("undeclared reference to 'log'"). It stored that error on about 1.12M non-switch records. Severity results are unchanged.origin.mac,log.vlan,log.firstPortandlog.secondPort. Interface names stay underlog.*becauseorigin.portis numeric. The names do not claim which port is the previous one.origin.mac;origin.ip;target.ip;target.ipand a numerictarget.port.Rules
mac_address_spoofing.yml:origin.mac;log.msg, which the filter writes, instead oflog.message, which nothing writes;deduplicateBy: [adversary.mac].arp_poisoning_detection.yml: requiresorigin.ipbefore its history search and readslog.msg.vlan_hopping_attempts.ymlis unchanged; see the deferred items.Why flaps are excluded (owner decision the reviewer can revisit)
Nothing establishes that a flap means an address was copied. In the real data, flaps look like network paths:
Estimated 30-day alert volumes from real timestamps (not observed alerts):
Validation
Re-checked on 2026-09-24 on the latest versions: official
v11d2479c1a(merged into this branch, no conflicts;plugins/alertsnow pins go-sdk v1.1.36) and EventProcessormain8a3ade7(playground and every plugin on go-sdk v1.1.36).regexMatchmatch strings only. Neither changes this draft: none of the 25 names the filter writes contains an underscore (SW_MATMand similar are values, not names), and everyregexMatch/containscall reads a text field. No file needed a change.plugins/alerts/testdata/cisco-switch/replay.py: 44 events, 0 errors, every field as inexpected.json, 6 VLAN alerts, no circuit breaker, no history search.origin.mac; with this filter's new mapping it would instead run a history search on every flap, which is why flaps are excluded. 15/15 synthetic checks pass.plugins/alertssuite 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 Switch tests all fail against the original files.Deferred
These need Cisco's documentation or an owner decision:
For the owner, not a filter change
On one instance, Firepower and Firepower Management Center devices send their logs to the Cisco Switch input. Those events never reach the Firepower filter or rules. The fix is to point those devices at the Firepower input.
Limits
8a3ade7) and this module both use go-sdk v1.1.36; neither is claimed to match a customer deployment.See
filters/audits/cisco-switch.md.🤖 Generated with Claude Code