# Tenzir Node v5.12.0

This release adds support for OCSF 1.6.0 and introduces the `replace` operator.

## 🚀 Features

### Support for OCSF 1.6.0

Aug 4, 2025 · [@jachris](https://github.com/jachris) · [#5407](https://github.com/tenzir/tenzir/pull/5407)

The `ocsf::` family of operators now supports OCSF 1.6.0.

Furthermore, the version `1.7.0-dev` is also supported now.

### `role` and `external_id` options for \`load\_s3

Aug 4, 2025 · [@IyeOnline](https://github.com/IyeOnline) · [#5406](https://github.com/tenzir/tenzir/pull/5406)

We added `role` and `external_id` options to the `load_s3` operator, bringing it in line with `save_s3`, which already features these options.

### Replacing values

Aug 4, 2025 · [@raxyte](https://github.com/raxyte) · [#5372](https://github.com/tenzir/tenzir/pull/5372)

The new `replace` operator allows you to find and replace all occurrences of a specific value across all fields (but not in lists) in your data with another value. This is particularly useful for data sanitization, redacting sensitive information, or normalizing values across datasets.

The operator scans every field in each input event and replaces any value that equals the `what` parameter with the value specified by `with`.

Replace all occurrences of the string `"-"` with null:

```tql
from {
  status: "-",
  data: {result: "-", count: 42},
  items: ["-", "valid", "-"]
}
replace what="-", with=null
```

```tql
{
  status: null,
  data: {result: null, count: 42},
  items: ["-", "valid", "-"]
}
```

Redact a specific IP address across all fields:

```tql
from {
  src_ip: 192.168.1.1,
  dst_ip: 10.0.0.1,
  metadata: {source: 192.168.1.1}
}
replace what=192.168.1.1, with="REDACTED"
```

```tql
{
  src_ip: "REDACTED",
  dst_ip: 10.0.0.1,
  metadata: {source: "REDACTED"}
}
```

### Check if your data is truly empty

Aug 4, 2025 · [@mavam](https://github.com/mavam) · [#5403](https://github.com/tenzir/tenzir/pull/5403)

Ever stared at your security logs wondering if that suspicious-looking field is actually empty or just pretending? We’ve all been there. That’s why we added `is_empty()` - a universal emptiness detector that works on strings, lists, and records.

```tql
from {
  id: "evt-12345",
  tags: ["malware", "c2"],
  metadata: {},
  description: "",
  iocs: []
}
has_metadata = not metadata.is_empty()
has_description = not description.is_empty()
has_iocs = not iocs.is_empty()
```

```tql
{
  id: "evt-12345",
  tags: [
    "malware",
    "c2",
  ],
  metadata: {},
  description: "",
  iocs: [],
  has_metadata: false,
  has_description: false,
  has_iocs: false,
}
```

No more checking `length() == 0` or wondering if that field exists but is empty. Just ask `is_empty()` and move on with your threat hunting!

## 🔧 Changes

### Handling of type conflicts when reading data

Aug 4, 2025 · [@jachris](https://github.com/jachris) · [#5405](https://github.com/tenzir/tenzir/pull/5405)

Tenzir requires all items of a list to have the same type. As a result, items in lists that contain different types (such as `[1, "test"]`) are cast to the common type `string`. Previously, all items were stored with their JSON representation, leading to the result `["1", "\"test\""]`. Now, only lists and record are stored as JSON, and strings are preserved without extra quotes. Thus, the new output is `["1", "test"]`.

### Automatic integer casting in `ocsf::apply`

Aug 3, 2025 · [@jachris](https://github.com/jachris) · [#5401](https://github.com/tenzir/tenzir/pull/5401)

The `ocsf::apply` operator now automatically casts `uint64` values to `int64` when the OCSF schema expects an integer field. This is important because the exact integer type is mostly considered an implementation detail. Unsigned integers are mainly produced when reading events for which a schema has been defined. This change makes sure that OCSF mappings that use the resulting events can successfully pass through `ocsf::apply`.

**Example:**

```tql
from {
  class_uid: 4001,
  metadata: {
    version: "1.5.0"
  },
  severity_id: uint(3)
}
ocsf::apply
```

Previously, this would result in a type mismatch warning and the `severity_id` field would be set to null. Now the `uint64` value 3 is automatically cast to `int64`, preserving the data. Values that exceed the maximum `int64` value will still generate a warning and be set to null.

## 🐞 Bug Fixes

### Metadata handling of `ocsf::derive` and `ocsf::trim`

Aug 3, 2025 · [@jachris](https://github.com/jachris) · [#5402](https://github.com/tenzir/tenzir/pull/5402)

The `ocsf::derive` and `ocsf::trim` operators now correctly preserve the metadata (such as `@name`) of the incoming event instead of overwriting it with the internal metadata used to encode OCSF schemas.

### Optimization of the `delay` operator

Aug 2, 2025 · [@jachris](https://github.com/jachris) · [#5399](https://github.com/tenzir/tenzir/pull/5399)

The `delay` operator optimization routine incorrectly declared that the behavior of the operator does not depend on the order of its input. As a result, chains such as `sort -> delay -> publish` did not correctly delay events.

[ Download on GitHub ](https://github.com/tenzir/tenzir/releases/tag/v5.12.0)

[Get the release artifacts and source code.](https://github.com/tenzir/tenzir/releases/tag/v5.12.0)