Commit 511dd04
authored
feat(secl): add a capture field to the set action (#54165)
### What does this PR do?
Extends the SECL `set` action with a new `capture` field: a regular expression with a
single capture group, applied to the value of `field`, whose first group becomes the
variable value.
```yaml
- id: ssm_command_tracking
expression: open.file.path =~ "/var/lib/amazon/ssm/*/document/orchestration/**"
silent: true
actions:
- set:
name: ssm_command_id
scope: process
inherited: true
field: open.file.path
capture: "/orchestration/([^/]+)/"
```
- `eval.CaptureStringMatcher` joins the existing `StringMatcher` family in
`secl/compiler/eval/strings.go`. `Compile` rejects patterns that don't compile and
patterns with no capture group; `Capture` uses `FindStringSubmatchIndex` to avoid
allocating a slice of strings for every group.
- The compiled pattern lives on the `*Action` (next to `ScopeFieldEvaluator`), compiled
once at policy load by `CompileCaptureMatcher`. It deliberately does **not** live in
`RuleSet.fieldEvaluators`, which is keyed by field name and shared across rules — two
rules capturing different patterns out of the same field would otherwise collide.
- Validation is all at load time: `capture` requires `field` (which transitively makes it
exclusive with `value` and `expression`), and is rejected on non-string and array
fields. At runtime the only branch is match / no match.
- A value that doesn't match is a silent no-op leaving the variable at its previous
value. Capture rules can be attached to high frequency events, so a miss must not log
or clear anything.
- `policy.schema.json` is regenerated. Because it declares `additionalProperties: false`,
skipping this would make every policy using `capture` fail schema validation.
Only fields holding a single string are supported. Array fields would need per-element
extraction, which is out of scope here.
### Motivation
Workload Protection events are rich at the kernel layer but disconnected from
cloud-layer activity. The identifiers needed to join them frequently already exist
inside WP event fields — an SSM `CommandId` inside a filesystem path, an IAM role inside
an IMDS url — but a rule could match those fields without being able to decompose them.
Storing the whole field value doesn't help: CloudTrail has the bare `a1b2…`, so the
strings aren't equal and there is nothing to join on. Capturing the id and attaching it
to the process with `inherited: true` puts the join key on every descendant event, so the
backend join becomes an exact equality instead of command-line parsing inside a time
window.
Implements the approved RFC "Capturing Correlation Artifact: Structured Join Keys from
SECL Set Actions".
### Describe how you validated your changes
Unit tests (`secl/compiler/eval`, `secl/rules`) cover extraction, the first-group-only
rule, non-participating optional groups, load-time rejection of malformed patterns and
of non-string/array fields, and the two-rules-same-field case that guards the per-action
matcher.
Functional test `TestActionCaptureInherited` on a real kernel covers the end-to-end
scenario: a silent rule captures a UUID-shaped SSM command id out of an orchestration
path, and a **grandchild** shell two levels down fires a rule asserting
`${process.ssm_command_id}` equals the bare id. It also asserts the artifact reaches the
serialized event intact at `$.process.variables.ssm_command_id` — variable values pass
through `scrubber.ScrubString`, so this confirms the scrubber doesn't mangle the join key
— and that no event is sent for the extraction rule itself.
Benchmark added for the set action, since the review raised the cost of evaluating the
pattern on frequently matched rules (linux/arm64):
| Case | ns/op | B/op | allocs/op |
|---|---|---|---|
| set without capture | 106 | 48 | 3 |
| set with capture, match | 335 | 104 | 6 |
| set with capture, no match | 122 | 48 | 3 |
Matching costs ~+230ns over a plain set action; a miss costs ~+16ns and no extra
allocations. Go's `regexp` is RE2, so a pattern is linear in the input length and cannot
backtrack catastrophically regardless of how it is written.
### Additional Notes
Captured values are cloned rather than sliced out of the field value: a Go substring
shares its backing array, so a 36-byte id would otherwise keep the whole path alive for
as long as the variable lived — and these variables are inherited across process trees
and can carry a TTL. That is the `+1 alloc` in the table above.
Note for reviewers: `secl/rules/policy_test.go` and `ruleset_test.go` are `//go:build
linux`, so the package reports 52 passing tests on darwin against 190 on linux. Changes
here need a linux run to be meaningfully verified.
Follow-ups, both out of scope:
- Capture on array fields, which is what env var artifacts (`GITHUB_RUN_ID`,
`ECS_TASK_ARN`) would need; `exec.envp` currently fails at load.
- The ECS task id example from the RFC, which depends on `process.cgroup.id` exposing the
relative cgroup path rather than just the leaf container id on the EC2 launch type.
That still needs to be checked on a real ECS host.
Co-authored-by: lorenzo.susini <lorenzo.susini@datadoghq.com>1 parent 64d2167 commit 511dd04
15 files changed
Lines changed: 862 additions & 0 deletions
File tree
- pkg/security
- rules/monitor
- secl
- compiler/eval
- rules
- schemas
- tests
- releasenotes/notes
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
231 | 231 | | |
232 | 232 | | |
233 | 233 | | |
| 234 | + | |
234 | 235 | | |
235 | 236 | | |
236 | 237 | | |
| |||
347 | 348 | | |
348 | 349 | | |
349 | 350 | | |
| 351 | + | |
350 | 352 | | |
351 | 353 | | |
352 | 354 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
82 | 139 | | |
83 | 140 | | |
84 | 141 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
268 | 268 | | |
269 | 269 | | |
270 | 270 | | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
271 | 321 | | |
272 | 322 | | |
273 | 323 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
259 | 259 | | |
260 | 260 | | |
261 | 261 | | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
262 | 374 | | |
263 | 375 | | |
264 | 376 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
57 | 58 | | |
58 | 59 | | |
59 | 60 | | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
60 | 76 | | |
61 | 77 | | |
62 | 78 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
187 | 201 | | |
188 | 202 | | |
189 | 203 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
226 | 226 | | |
227 | 227 | | |
228 | 228 | | |
| 229 | + | |
229 | 230 | | |
230 | 231 | | |
231 | 232 | | |
| |||
259 | 260 | | |
260 | 261 | | |
261 | 262 | | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
262 | 270 | | |
263 | 271 | | |
264 | 272 | | |
| |||
0 commit comments