Summary
internal/pattern/engine.go (parseVariableWithVars) does strings.Split(variableStr, "|") to separate the variable name from its filter chain. The split is quote-unaware, so any | inside a quoted re("...") argument is treated as a filter separator.
{{ direction | re("UP|DOWN") }} becomes filters re("UP and DOWN"). The malformed re( arg falls through the built-in-pattern lookup and the variable silently degrades to the default \S+ — it then matches any token.
Tested variants that all exhibit the bug: re("UP|DOWN"), re("(?:UP|DOWN)"), re("(UP|DOWN)"), re("UP\|DOWN"), re("(?i:up|down)"). re("(?:UP)|(?:DOWN)") crashes the parse.
Repro
Input line: 1 DOWN tengigabitethernet 1/1/1Enable 7
and sibling table rows 300 -- 1s 7 ... / 1 2 Discovery Up Up ... in the same buffer.
<group name="m*">
{{ id | DIGIT | to_int }} {{ direction | re("(?:UP|DOWN)") }} {{ rest | ROW }}
</group>
Result: 4 rows with direction = --, DOWN, 2, None (expected 1 row).
With re("[UD][PO]W?N?") (no pipe) → 1 row, correct.
Impact
Silent over-matching: sibling tables bleed into the wrong list; any column that looks constrained is actually unconstrained. Found while building Telco X-Series CFM templates (DH360-Device-Discovery field-mappings/templates/show_ethernet_cfm_full-detail.ttp).
Suggested fix
Split the filter chain with a quote-aware tokenizer (track "/' state; also (/) depth so re("a|b") and lookup("x", add_field="y") stay intact), or parse re(...) args before splitting.
Related
A second, smaller quirk: a {{ x | set("v") }} placed after {{ r | ROW }} on the same line truncates ROW to \S+. Moving set() before the ROW capture avoids it.
Summary
internal/pattern/engine.go(parseVariableWithVars) doesstrings.Split(variableStr, "|")to separate the variable name from its filter chain. The split is quote-unaware, so any|inside a quotedre("...")argument is treated as a filter separator.{{ direction | re("UP|DOWN") }}becomes filtersre("UPandDOWN"). The malformedre(arg falls through the built-in-pattern lookup and the variable silently degrades to the default\S+— it then matches any token.Tested variants that all exhibit the bug:
re("UP|DOWN"),re("(?:UP|DOWN)"),re("(UP|DOWN)"),re("UP\|DOWN"),re("(?i:up|down)").re("(?:UP)|(?:DOWN)")crashes the parse.Repro
Input line:
1 DOWN tengigabitethernet 1/1/1Enable 7and sibling table rows
300 -- 1s 7 .../1 2 Discovery Up Up ...in the same buffer.Result: 4 rows with
direction=--,DOWN,2,None(expected 1 row).With
re("[UD][PO]W?N?")(no pipe) → 1 row, correct.Impact
Silent over-matching: sibling tables bleed into the wrong list; any column that looks constrained is actually unconstrained. Found while building Telco X-Series CFM templates (DH360-Device-Discovery
field-mappings/templates/show_ethernet_cfm_full-detail.ttp).Suggested fix
Split the filter chain with a quote-aware tokenizer (track
"/'state; also(/)depth sore("a|b")andlookup("x", add_field="y")stay intact), or parsere(...)args before splitting.Related
A second, smaller quirk: a
{{ x | set("v") }}placed after{{ r | ROW }}on the same line truncates ROW to\S+. Movingset()before the ROW capture avoids it.