Skip to content

fix(tool-policy-audit): use truncateUtf16Safe for audit field truncation#102464

Merged
steipete merged 2 commits into
openclaw:mainfrom
lsr911:fix/utf16-tool-policy-audit
Jul 9, 2026
Merged

fix(tool-policy-audit): use truncateUtf16Safe for audit field truncation#102464
steipete merged 2 commits into
openclaw:mainfrom
lsr911:fix/utf16-tool-policy-audit

Conversation

@lsr911

@lsr911 lsr911 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

The tool policy audit logger uses naive .slice(0, MAX_AUDIT_FIELD_LENGTH) to truncate field values. This can split surrogate pairs mid-character, producing garbled output in audit logs.

Why This Change Was Made

Replace with truncateUtf16Safe() which preserves surrogate pair boundaries.

Evidence

=== BEFORE: naive .slice(0, 23) ===
Result: "tool-audit-field-with-\ud83d"
Last char: "\ud83d"
Is high surrogate (0xD800-0xDBFF): true  ← DANGLING!

=== AFTER: truncateUtf16Safe(0, 23) ===
Result: "tool-audit-field-with-"
Is clean (no broken pair): true  ← CORRECT

Files Changed

File Change
src/agents/tool-policy-audit.ts +1 import; .slice(0,N)truncateUtf16Safe()

🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

Replace naive .slice(0, MAX) with truncateUtf16Safe() to prevent
surrogate pair splitting in tool policy audit log output.

Co-Authored-By: Claude <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. labels Jul 9, 2026
@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 9, 2026, 2:48 AM ET / 06:48 UTC.

Summary
This PR imports truncateUtf16Safe and uses it when sanitizeAuditField truncates long tool-policy audit fields.

PR surface: Source +1. Total +1 across 1 file.

Reproducibility: yes. Source inspection is enough to reproduce the current-main failure path: a sanitized audit field longer than 160 UTF-16 units with a surrogate pair crossing the cutoff can be raw-sliced into a dangling surrogate, though I did not run a live audit-log repro.

Review metrics: none identified.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted terminal output or audit logs showing a long emoji-containing tool-policy field truncates without a replacement character or dangling surrogate.
  • After updating the PR body with proof, let ClawSweeper rerun automatically; if it does not, ask a maintainer to comment @clawsweeper re-review.

Proof guidance:

  • [P1] Needs real behavior proof before merge: No after-fix live run, terminal output, redacted log, screenshot, recording, or linked artifact is present; the contributor should add redacted proof to the PR body, which should trigger a fresh ClawSweeper review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] The contributor has not provided after-fix real behavior proof showing the audit log output with a truncation-boundary surrogate pair.
  • [P1] The branch is behind current main but GitHub still reports it mergeable, so this is a normal refresh need rather than a supported merge-risk label.

Maintainer options:

  1. Decide the mitigation before merge
    Tool-policy audit fields should keep the existing bounds while using the shared surrogate-safe UTF-16 helper, with merge evidence showing a long emoji-containing field no longer emits a dangling surrogate.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] No ClawSweeper code repair is needed right now; the contributor needs real behavior proof and then normal maintainer review.

Security
Cleared: The diff only changes local audit-log string truncation and adds no dependency, workflow, credential, permission, or execution-path surface.

Review details

Best possible solution:

Tool-policy audit fields should keep the existing bounds while using the shared surrogate-safe UTF-16 helper, with merge evidence showing a long emoji-containing field no longer emits a dangling surrogate.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection is enough to reproduce the current-main failure path: a sanitized audit field longer than 160 UTF-16 units with a surrogate pair crossing the cutoff can be raw-sliced into a dangling surrogate, though I did not run a live audit-log repro.

Is this the best way to solve the issue?

Yes. Replacing the one owner-local truncation boundary with the shared truncateUtf16Safe helper is the narrow maintainable fix; broader UTF-16 sweep work belongs in separate surface-specific PRs.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5f91f6c95840.

Label changes

Label changes:

  • add P3: The PR fixes low-blast-radius audit-log text corruption without changing runtime policy decisions, config, protocol, or user data.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🐚 platinum hermit.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: No after-fix live run, terminal output, redacted log, screenshot, recording, or linked artifact is present; the contributor should add redacted proof to the PR body, which should trigger a fresh ClawSweeper review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P3: The PR fixes low-blast-radius audit-log text corruption without changing runtime policy decisions, config, protocol, or user data.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: No after-fix live run, terminal output, redacted log, screenshot, recording, or linked artifact is present; the contributor should add redacted proof to the PR body, which should trigger a fresh ClawSweeper review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +1. Total +1 across 1 file.

View PR surface stats
Area Files Added Removed Net
Source 1 2 1 +1
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 1 2 1 +1

What I checked:

  • Current main still has the unsafe truncation: sanitizeAuditField trims and escapes audit fields, then uses sanitized.slice(0, MAX_AUDIT_FIELD_LENGTH) before appending ..., so current main can cut a surrogate pair at the truncation boundary. (src/agents/tool-policy-audit.ts:148, 5f91f6c95840)
  • PR applies the expected local replacement: The PR changes only the audit sanitizer path from raw .slice(0, MAX_AUDIT_FIELD_LENGTH) to truncateUtf16Safe(sanitized, MAX_AUDIT_FIELD_LENGTH), keeping the existing length limit and suffix behavior. (src/agents/tool-policy-audit.ts:146, 7b33e2e0a685)
  • Shared helper contract supports the fix: truncateUtf16Safe floors the limit, returns the input when already short, and delegates to sliceUtf16Safe(input, 0, limit) so truncation does not return dangling surrogate halves. (packages/normalization-core/src/utf16-slice.ts:44, 5f91f6c95840)
  • Existing adjacent tests cover audit logging but not this boundary: Tool-policy pipeline tests assert sanitized audit labels/tool names and audit metadata, which confirms the affected runtime path but does not add a surrogate-boundary regression for this PR. (src/agents/tool-policy-pipeline.test.ts:754, 5f91f6c95840)
  • No real behavior proof is present: Live PR metadata shows the PR body contains rationale only, no comments with terminal/log output or artifacts, and the Real behavior proof check failed on the current head. (7b33e2e0a685)
  • Feature history: git blame ties the current sanitizer and raw slice to commit aad99747cd45b3845e027c2297b85835b0bef75f, which also introduced the shared UTF-16 helper re-export in src/utils.ts. (src/agents/tool-policy-audit.ts:148, aad99747cd45)

Likely related people:

  • vincentkoc: Blame on sanitizeAuditField and the truncateUtf16Safe utility/re-export points to the same recent commit that introduced this audit sanitizer and helper surface. (role: introduced behavior; confidence: high; commits: aad99747cd45; files: src/agents/tool-policy-audit.ts, packages/normalization-core/src/utf16-slice.ts, src/utils.ts)
  • drobison00: Merged prior tool-policy pipeline work in the same agent/tool-policy area, including bundled-tool final policy filtering. (role: recent adjacent tool-policy contributor; confidence: medium; commits: 0e7a992d3f31; files: src/agents/tool-policy-pipeline.ts)
  • steipete: Git history shows earlier tool-policy group and refactor commits in the surrounding agent policy pipeline. (role: earlier tool-policy feature contributor; confidence: medium; commits: 2faf7cea93af, abf3dfc375a5; files: src/agents/tool-policy-pipeline.ts, src/agents/tool-policy.ts)
  • lsr911: The author also appears in merged current-main UTF-16-safe truncation changes on adjacent OpenClaw surfaces, so they are relevant to the ongoing truncation sweep beyond only opening this PR. (role: recent adjacent UTF-16 truncation contributor; confidence: medium; commits: 78f9449d73af, 37d613a59654, 42fc9e8a92f9; files: src/channels/thread-bindings-messages.ts, extensions/codex/src/app-server/attempt-notifications.ts, packages/acp-core/src/error-format.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. labels Jul 9, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. label Jul 9, 2026
@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Land-ready maintainer pass complete at exact head a8929df7790a49b69246391f7a37e3246189ff47.

I kept the useful runtime fix, switched it to the direct normalization-core import, and added a production-path regression that verifies both the rendered audit message and structured metadata at a split surrogate boundary.

Proof:

The focused test ran locally because fork-contributor code is not eligible for credential-hydrated Testbox execution before landing review. Exact-head fork CI supplies the broad hosted proof. No remaining proof gap.

@steipete
steipete merged commit 472e516 into openclaw:main Jul 9, 2026
106 of 107 checks passed
@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

@lsr911

lsr911 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper could not start a re-review for this item.

Reason: re-review requires an open issue or PR.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 9, 2026
…ion (openclaw#102464)

* fix(tool-policy-audit): use truncateUtf16Safe for audit field truncation

Replace naive .slice(0, MAX) with truncateUtf16Safe() to prevent
surrogate pair splitting in tool policy audit log output.

Co-Authored-By: Claude <[email protected]>

* test(agents): cover audit UTF-16 boundary

---------

Co-authored-by: Claude <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants