Skip to content

Commit 68817ad

Browse files
authored
Merge pull request #40 from fwosar/one-to-many-field-name-mapping
feat: support one-to-many in field_name_mapping
2 parents efffdb5 + 2a4eeed commit 68817ad

4 files changed

Lines changed: 446 additions & 37 deletions

File tree

crates/rsigma-eval/README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ Each transformation item in a pipeline can have:
258258

259259
| Type | Fields | Description |
260260
|------|--------|-------------|
261-
| `field_name_mapping` | `mapping: {k: v}` | Rename fields via a mapping dict |
261+
| `field_name_mapping` | `mapping: {k: v \| [v1, v2, ...]}` | Rename fields via a mapping dict; list values expand the matched detection item into an OR over the alternatives (one-to-many, pySigma-compatible) |
262262
| `field_name_prefix_mapping` | `mapping: {prefix: replacement}` | Rename fields matching a prefix |
263263
| `field_name_prefix` | `prefix` | Add a prefix to all field names |
264264
| `field_name_suffix` | `suffix` | Add a suffix to all field names |
@@ -437,6 +437,29 @@ let event = JsonEvent::borrow(&json!({"process.command_line": "whoami"}));
437437
let matches = engine.evaluate(&event);
438438
```
439439

440+
`field_name_mapping` also accepts a list of alternatives, matching pySigma's
441+
`FieldMappingTransformation`. The matched detection item is expanded into an
442+
OR over the alternatives — when the surrounding `AllOf` selection has other
443+
items, they're preserved across each branch via a Cartesian expansion so the
444+
`AND` / `OR` semantics stay correct:
445+
446+
```yaml
447+
name: Hashes mapping
448+
transformations:
449+
- type: field_name_mapping
450+
mapping:
451+
Hashes:
452+
- file.hash.md5
453+
- file.hash.sha1
454+
- file.hash.sha256
455+
```
456+
457+
After applying this pipeline, a rule selecting `Hashes: 'abc123'` matches an
458+
event populating *any* of `file.hash.md5`, `file.hash.sha1`, or
459+
`file.hash.sha256`. Correlation rules (`group_by`, `aliases`, threshold
460+
`field`) consume only the first listed alternative since those positions are
461+
inherently scalar.
462+
440463
**With correlations:**
441464

442465
```rust

crates/rsigma-eval/src/pipeline/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,12 @@ fn apply_correlation_transformation(
242242
) -> Result<bool> {
243243
match transformation {
244244
Transformation::FieldNameMapping { mapping } => {
245-
remap_correlation_fields(corr, |name| mapping.get(name).cloned());
245+
// Correlation fields (group_by, aliases mapping values, threshold
246+
// field) are scalar — OR over multiple alternatives isn't
247+
// expressible there, so we use the first listed alternative.
248+
remap_correlation_fields(corr, |name| {
249+
mapping.get(name).and_then(|alts| alts.first().cloned())
250+
});
246251
Ok(true)
247252
}
248253

@@ -470,7 +475,7 @@ fn parse_transformation(obj: &serde_yaml::Mapping) -> Result<Transformation> {
470475

471476
match type_str {
472477
"field_name_mapping" => {
473-
let mapping = parse_string_mapping(obj.get(ykey("mapping")))?;
478+
let mapping = parse_string_or_list_mapping(obj.get(ykey("mapping")))?;
474479
Ok(Transformation::FieldNameMapping { mapping })
475480
}
476481

0 commit comments

Comments
 (0)