regex-lite on the agent, full regex at ingest, conformance corpus limited to the syntax both accept. The agent had no regex dependency at all and the full crate is a megabyte-plus against a binary whose pitch is that it is small and static. The rejected alternative, regex ingest-side only, would have given up redacting PII before it leaves the host -- the one capability that most needs to be on the agent, and most of the reason on-host processing exists at all. Checked rather than assumed: every pattern the corpus uses compiles and behaves under regex-lite, including named captures and replace_all replacing every occurrence, which is what mask needs and what a redaction stopping at the first hit would get wrong. Both engines also resolve alternation leftmost-first, confirmed by running the same pattern through regex-lite and Go's regexp and getting the same output. A case now pins that, since it is the kind of semantic two independent implementations can differ on silently. No canary gate. An edit reaches every matching agent at once, and this design had suggested that might have to block the feature. It does not, but the risk is accepted rather than waved away: total evaluation is the real mitigation, a fatal rule set crash-loops rather than strands because overrides are never persisted, apply-then-verify makes that self-healing, and applied_override_version already shows the blast radius. What is genuinely given up is the ability to stop a bad rollout partway through -- everything else shortens the outage without preventing the rule reaching every host first. That raises the stakes on apply-then-verify, which is still open. Without a canary, total evaluation stops being a nice property of a well-designed DSL and becomes the only thing between a bad rule and every host at once, so cutting the backstop from v1 is a harder call than it looked when it was written down. Signed-off-by: John Coffey <[email protected]>
38 lines
970 B
JSON
38 lines
970 B
JSON
{
|
|
"name": "regex_alternation_is_leftmost_first",
|
|
"description": "Both engines resolve alternation leftmost-first, so the earlier branch wins even when a later one would match more. Pinned rather than trusted: this is the kind of semantic two independent implementations can differ on silently, and mask depends on which branch won.",
|
|
"rules": [
|
|
{
|
|
"match": [],
|
|
"actions": [
|
|
{
|
|
"action": "mask",
|
|
"field": "message",
|
|
"pattern": "conn(ect|ected)",
|
|
"replacement": "X"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"inputs": [
|
|
{
|
|
"timestamp_unix_nano": 1000,
|
|
"host": "h1",
|
|
"service": "s1",
|
|
"severity": "SEVERITY_INFO",
|
|
"message": "client connected ok",
|
|
"attributes": {}
|
|
}
|
|
],
|
|
"expect": [
|
|
{
|
|
"timestamp_unix_nano": 1000,
|
|
"host": "h1",
|
|
"service": "s1",
|
|
"severity": "SEVERITY_INFO",
|
|
"message": "client Xed ok",
|
|
"attributes": {}
|
|
}
|
|
]
|
|
}
|