Skip to content

fix(repo): escape raw NUL bytes that hid six source files from grep#3127

Merged
os-zhuang merged 1 commit into
mainfrom
claude/elated-dewdney-cc929c
Jul 17, 2026
Merged

fix(repo): escape raw NUL bytes that hid six source files from grep#3127
os-zhuang merged 1 commit into
mainfrom
claude/elated-dewdney-cc929c

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What

Six source files embedded a literal U+0000 byte as a composite-key separator instead of the \u0000 escape. grep/ripgrep scan the whole buffer, classify such a file as binary, and silently return no matches:

$ grep -n saveMetaItem packages/metadata-protocol/src/protocol.ts
$ echo $?
1

...despite 16 real hits. A core protocol file was invisible to code search and to any grep-based lint tooling.

File Role of the NUL
metadata-protocol/src/protocol.ts:3054 ETag locale separator
cli/src/utils/lint-view-refs.ts:148 dedupe key
lint/src/build-access-matrix.ts:90 access-matrix key
plugins/plugin-security/src/suggested-audience-bindings.ts:108 suggestion key (x2)
services/service-storage/src/attachment-access-hooks.ts:179 cache key
services/service-analytics/src/__tests__/objectql-strategy-boolean-filter.test.ts:63 join separator

Why it survived review

git only scans the first 8000 bytes for a NUL. protocol.ts carried its NUL at offset 147230 — past that window — so git kept diffing it as ordinary text while grep had already given up on it. The defect was invisible from both directions.

Fix

Replace each raw NUL with \u0000, matching the existing convention at rest-server.ts:1065 ('\u0000default'), which uses the escape for exactly this kind of key sentinel. Chosen over \0, which degrades into a legacy-octal escape error if ever followed by a digit.

Verification

The changed line in protocol.ts lives in getMetaItemCached, which no test executes — it is only ever mocked. So a green suite would not have proven anything here. Verified directly instead, by driving the real method and comparing against an independent oracle that reproduces the pre-fix raw-NUL expression:

locale pre-fix ETag post-fix ETag match
en-US 68f978ed 68f978ed yes
zh-CN 50289f3b 50289f3b yes
(none) 75dad1ef 75dad1ef yes

Byte-identical. Locale-varying ETags still hold (issue #1319): same-locale match returns 304, cross-locale correctly does not.

Also: zero raw NULs repo-wide; grep -c saveMetaItem protocol.ts now returns 16; emitted dist/ contains zero NULs; 1383 tests green across all six affected packages (metadata-protocol 34, lint 225, service-analytics 164, service-storage 99, plugin-security 481, cli 380); builds green.

No changeset — pure fix, no user-facing behaviour change (AGENTS.md post-task checklist #3).

Follow-up

Nothing prevents this from recurring; six files accumulated it silently. A CI guard rejecting raw NULs in source files would close it — happy to add in a separate PR.

Generated with Claude Code

Six source files embedded a literal U+0000 byte as a composite-key
separator instead of the `\u0000` escape. grep/ripgrep scan the whole
buffer, classify such a file as binary, and silently return no matches:
`grep -n saveMetaItem protocol.ts` reported nothing despite 16 hits,
making a core protocol file invisible to code search and to any
grep-based lint tooling.

This survived review because git only scans the first 8000 bytes for a
NUL, and protocol.ts carried its NUL at offset 147230 - so the file kept
diffing as ordinary text while grep had already given up on it.

Replace each raw NUL with `\u0000`, matching the existing convention in
rest-server.ts. That form is chosen over `\0`, which becomes a legacy
octal escape error if it is ever followed by a digit.

The resulting strings are byte-identical, so behaviour is unchanged:
getMetaItemCached still emits the same per-locale ETags as before
(en-US 68f978ed / zh-CN 50289f3b / no-locale 75dad1ef), still returns
304 on a same-locale match, and still declines 304 across locales
(issue #1319). No changeset: pure fix, no user-facing change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Building Building Preview, Comment Jul 17, 2026 12:08pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/cli, @objectstack/lint, @objectstack/metadata-protocol, @objectstack/plugin-security, packages/services.

30 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via packages/cli)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli)
  • content/docs/automation/hook-bodies.mdx (via packages/cli)
  • content/docs/automation/webhooks.mdx (via packages/services)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-protocol)
  • content/docs/deployment/backup-restore.mdx (via @objectstack/cli)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/cli)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli, @objectstack/plugin-security)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/cli)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli, packages/services)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/permissions/access-recipes.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint, @objectstack/plugin-security)
  • content/docs/permissions/explain.mdx (via @objectstack/plugin-security)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/plugin-security)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-security)
  • content/docs/plugins/packages.mdx (via @objectstack/cli, @objectstack/plugin-security, packages/services)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/services)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli, @objectstack/plugin-security)
  • content/docs/ui/audience-based-interfaces.mdx (via packages/plugins/plugin-security)
  • content/docs/ui/dashboards.mdx (via @objectstack/plugin-security)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang merged commit 4cba5a5 into main Jul 17, 2026
14 of 16 checks passed
@os-zhuang
os-zhuang deleted the claude/elated-dewdney-cc929c branch July 17, 2026 12:17
os-zhuang added a commit that referenced this pull request Jul 17, 2026
A single literal U+0000 byte makes grep/ripgrep classify the whole file as
binary and silently return zero matches: `grep -n saveMetaItem
packages/metadata-protocol/src/protocol.ts` reported nothing despite 16 real
hits, hiding a core protocol file from code search and from every grep-based
lint -- with no error to say so.

Nothing caught this. git decides binary-ness from the first 8000 bytes only,
and protocol.ts carried its NUL at offset 147230, so it kept diffing as
ordinary text through review. That blind spot is how six separate files
accumulated the same defect before #3127 fixed them.

Add scripts/check-nul-bytes.mjs, wired into the lint workflow beside the other
guards. It scans tracked .ts/.tsx/.js/.jsx/.mjs/.cjs/.cts/.mts sources
(generated and vendored output excluded), reports each offender as
file:line:column plus the byte offset -- the location grep cannot give you --
and points the author at the \u0000 escape form and the existing convention at
packages/rest/src/rest-server.ts:1065.

Verified by replaying the real defect rather than only a synthetic one:
restoring protocol.ts to its pre-#3127 state turns the guard red at 3054:72
(offset 147230, confirmed against an independent oracle) while git still diffs
that file as ordinary text. The fixed tree is green across 2261 files in ~0.2s.
A tracked file under an excluded dir stays ignored even when force-added, so
the exclusion is doing real work rather than riding on .gitignore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant