Skip to content

Escape the NUL separators in PolicyEnforcer's log dedup key - #2528

Merged
thjaeckle merged 1 commit into
eclipse-ditto:masterfrom
beyonnex-io:bugfix/policy-enforcer-nul-byte-literals
Aug 27, 2026
Merged

thjaeckle merged 1 commit into
eclipse-ditto:masterfrom
beyonnex-io:bugfix/policy-enforcer-nul-byte-literals

Conversation

@thjaeckle

Copy link
Copy Markdown
Member

What

PolicyEnforcer.java carries two raw NUL (U+0000) bytes on the dedupKey line in
missingReferenceLogger -- written as literal control characters instead of unicode escapes.
This replaces each with the six-character escape \u0000.

-final String dedupKey = policyIdStr + "<raw NUL byte>" + referencingEntry.getLabel() + "<raw NUL byte>" + target;
+final String dedupKey = policyIdStr + "\u0000" + referencingEntry.getLabel() + "\u0000" + target;

Why

NUL is a perfectly good separator here -- it cannot collide with a policy id, entry label or
entry reference. The problem is only that it was written as a raw byte, which makes the whole
file binary to any tool that scans for NUL.

The practical damage is silent search failure:

$ grep -n 'defaultEvaluator' PolicyEnforcer.java
$          # <- nothing, for a 650-line Java file with 8 matches

grep classifies a file containing NUL as binary and suppresses matches. No error, no
diagnostic -- it reads exactly like "no matches". ripgrep is only marginally better,
reporting binary file matches (found "\0" byte around offset 8340) instead of the
matching lines.

I hit this while reviewing an unrelated change to this file and initially mistook it for a
broken tool.

Why it went unnoticed

Git's binary heuristic only inspects the first 8000 bytes of a file. These NULs sit at
roughly offset 8340 -- just past the window. So git diff, git blame and the GitHub web
view all render the file as ordinary text, while grep silently ignores it. Introduced in
3ab05665e8 (2026-04-30).

Behaviour

Unchanged, verified at the artifact level. Unicode escapes are resolved by the Java lexer,
so the string literal still contains exactly one NUL character. Compiling the module before and
after the change produces byte-for-byte identical class files -- checked with cmp for
PolicyEnforcer.class and all five sibling classes in the package.

No new test: there is no behavioural delta to pin, and a test asserting "this source file
contains no NUL bytes" would be testing the toolchain rather than the code.

Scope

I scanned every git-tracked text file in the repository (.java, .conf, .yml, .xml,
.md, .tpl, .sh, .json, .properties, .js, .ts, .html, .css) for NUL bytes --
this was the only affected file.

policies/enforcement 278 tests green, mvn license:check clean.

PolicyEnforcer.java carried two raw NUL (U+0000) bytes on the dedupKey line in
missingReferenceLogger, written as literal control characters rather than as
unicode escapes. NUL is a fine separator choice there - it cannot collide with a
policy id, entry label or entry reference - but as a raw byte it makes the whole
file binary to any tool that scans for NUL.

The practical damage is silent search failure: `grep -n <pattern> PolicyEnforcer.java`
prints *nothing* for a 650-line Java file, because grep classifies a file
containing NUL as binary and suppresses matches. Not an error, no diagnostic -
it reads exactly like "no matches". ripgrep at least says "binary file matches".
I hit this while reviewing an unrelated change and initially mistook it for a
broken tool.

It survived review because git's binary heuristic only inspects the first 8000
bytes, and these NULs sit at roughly offset 8340 - just past the window. So
git diff, git blame and the GitHub web view all rendered the file as normal text
while grep silently ignored it.

Replaces each raw byte with the six-character unicode escape. Unicode escapes are
resolved by the Java lexer, so the string literal still contains exactly one NUL
character and nothing about runtime behaviour changes: recompiling the module
before and after produces byte-for-byte identical class files (verified for
PolicyEnforcer.class and all five sibling classes). No new test - there is no
behavioural delta to pin, and the dedup path is unchanged.

Scanned every git-tracked text file in the repository; this was the only one
affected.

policies/enforcement 278 tests green, license:check clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Jäckle <thomas.jaeckle@beyonnex.io>
@thjaeckle thjaeckle self-assigned this Aug 27, 2026
@thjaeckle thjaeckle added this to the 3.9.7 milestone Aug 27, 2026

@hu-ahmed hu-ahmed left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@thjaeckle
thjaeckle merged commit 13d78a0 into eclipse-ditto:master Aug 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants