fix(imessage): strip length-prefixed UTF-8 from imsg rpc text#64000
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 01abbf1074
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Greptile SummaryThis PR adds a new Confidence Score: 5/5Safe to merge — logic is correct and conservative; the only finding is a misleading test description with missing two-byte varint coverage. All findings are P2. The implementation correctly handles the stated goal, the exact-length guard prevents false positives, and Unicode edge cases are safe. The test labeled "multi-byte varint" actually uses a single-byte varint (0x7F), leaving the two-byte decoding path unverified, but the production code logic is sound. extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.test.ts — misleading test name and missing two-byte varint case. Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.test.ts
Line: 10-15
Comment:
**Misleading test name — single-byte varint, not multi-byte**
`0x7F` has bit 7 clear, so it is the terminal byte of a **one-byte** varint with value 127, not a multi-byte varint. A genuinely two-byte varint (e.g. `0x80 0x01` for value 128) is not exercised here, leaving the multi-byte decoding path untested. Renaming the case and adding a companion case with a real two-byte prefix would lock in that path.
```suggestion
it("removes a 127-byte payload behind a single-byte varint length", () => {
const inner = "a".repeat(127);
const buf = Buffer.allocUnsafe(1 + inner.length);
buf.writeUInt8(0x7f, 0);
buf.write(inner, 1, "utf8");
expect(stripImessageLengthPrefixedUtf8Text(buf.toString("utf8"))).toBe(inner);
});
it("removes a 128-byte payload behind a two-byte varint length", () => {
const inner = "a".repeat(128);
// Varint for 128: 0x80 0x01
const buf = Buffer.allocUnsafe(2 + inner.length);
buf.writeUInt8(0x80, 0);
buf.writeUInt8(0x01, 1);
buf.write(inner, 2, "utf8");
expect(stripImessageLengthPrefixedUtf8Text(buf.toString("utf8"))).toBe(inner);
});
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(imessage): strip length-prefixed UTF..." | Re-trigger Greptile |
Review feedback addressed (latest:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1eac513d25
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
1eac513 to
6331065
Compare
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 Unbounded UTF-8 Buffer conversion/decoding of inbound iMessage text can cause memory/CPU DoS
Description
Vulnerable code: const stripped = tryStripImessageLengthPrefixedUtf8Buffer(Buffer.from(text, "utf8"));
...
const inner = utf8Decoder.decode(stripped);RecommendationAdd an explicit size/byte limit (and ideally a try/catch) before converting/decoding untrusted text. Example: const MAX_STRIP_BYTES = 64 * 1024; // choose an appropriate bound
export function stripImessageLengthPrefixedUtf8Text(text: string): string {
if (!text) return text;
// Prevent large allocations/CPU on attacker-controlled input
if (Buffer.byteLength(text, "utf8") > MAX_STRIP_BYTES) {
return text;
}
try {
const buf = Buffer.from(text, "utf8");
const stripped = tryStripImessageLengthPrefixedUtf8Buffer(buf);
if (!stripped) return text;
const inner = utf8Decoder.decode(stripped);
return inner.length > 0 ? inner : text;
} catch {
return text; // fail open; preserve original text
}
}If feasible, further reduce work by only examining a small prefix to decide whether stripping is needed before allocating/decoding the full payload. Analyzed PR: #64000 at commit Last updated on: 2026-04-10T08:36:08Z |
6331065 to
4ba523d
Compare
obviyus
left a comment
There was a problem hiding this comment.
Verified the imessage inbound text corruption path and confirmed the fix now strips only the expected length-delimited wrapper shape before parseIMessageNotification forwards text and reply_to_text.
Maintainer follow-up: rebased onto latest main, replaced the printable-length heuristic with an explicit field-wrapper discriminator, and added regressions for ASCII-printable lengths plus non-wrapper plain text.
Local gate: pnpm test extensions/imessage/src/monitor.gating.test.ts extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.test.ts extensions/imessage/src/monitor/parse-notification.test.ts and pnpm tsgo.
|
Landed on main. Thanks @neeravmakwana. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ba523dc60
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (key.value !== 0x0a) { | ||
| return null; | ||
| } | ||
|
|
||
| const length = readVarint(buf, key.nextOffset); |
There was a problem hiding this comment.
Guard newline-prefixed plain text from wrapper stripping
Fresh evidence on this commit: "\na" + "x".repeat(97) is treated as a wrapped payload because 0x0a is both the protobuf field-1 tag and a literal newline byte, so the function drops the first two characters and returns only the tail. Since parseIMessageNotification now applies this helper to every inbound text and reply_to_text, legitimate user messages that start with a newline can be silently corrupted when the next byte happens to equal the remaining UTF-8 length; add a stronger discriminator before stripping.
Useful? React with 👍 / 👎.
* fix(imessage): strip length-prefixed UTF-8 from imsg rpc text * fix: strip wrapped imsg rpc text fields (#64000) (thanks @neeravmakwana) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…ravmakwana) * fix(imessage): strip length-prefixed UTF-8 from imsg rpc text * fix: strip wrapped imsg rpc text fields (openclaw#64000) (thanks @neeravmakwana) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…ravmakwana) * fix(imessage): strip length-prefixed UTF-8 from imsg rpc text * fix: strip wrapped imsg rpc text fields (openclaw#64000) (thanks @neeravmakwana) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…ravmakwana) * fix(imessage): strip length-prefixed UTF-8 from imsg rpc text * fix: strip wrapped imsg rpc text fields (openclaw#64000) (thanks @neeravmakwana) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…ravmakwana) * fix(imessage): strip length-prefixed UTF-8 from imsg rpc text * fix: strip wrapped imsg rpc text fields (openclaw#64000) (thanks @neeravmakwana) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…ravmakwana) * fix(imessage): strip length-prefixed UTF-8 from imsg rpc text * fix: strip wrapped imsg rpc text fields (openclaw#64000) (thanks @neeravmakwana) --------- Co-authored-by: Ayaan Zaidi <[email protected]>
Summary
imsg rpcsometimes include a protobuf-style length-delimited UTF-8 blob inside the JSONtext(andreply_to_text) string. Those bytes were passed through to the agent as literal prefix garbage.textandreply_to_text.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Root Cause (if applicable)
imsgmay embed a length-delimited UTF-8 payload inside the JSON string; OpenClaw previously forwarded that field with onlytrim().text/reply_to_textafter parse.Regression Test Plan (if applicable)
extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.test.ts,extensions/imessage/src/monitor/parse-notification.test.tsUser-visible / Behavior Changes
Inbound iMessage text and quoted reply text may no longer show a short binary or mojibake prefix when the
imsgbridge included a length-delimited UTF-8 wrapper inside the JSON field.Diagram (if applicable)
N/A
Security Impact (required)
No)No)No)