Commit b950a22
authored
fix(guard): use UTF-8 safe string truncation in output preview logging (#3713)
## Summary
Fixes #3711 — the WASM guard panics on multi-byte UTF-8 characters in
tool response previews, poisoning the entire session.
## Root cause
`label_response` in `lib.rs` truncates serialized JSON for debug logging
using byte-index slicing (`&output_json[..500]`). When byte 500 falls in
the middle of a multi-byte UTF-8 code point (CJK = 3 bytes, emoji = 4
bytes), Rust panics with "byte index is not a char boundary". Since this
runs inside the WASM guest, the panic becomes a trap that permanently
poisons the guard instance.
## Changes
**New helper** — `safe_preview(s, max_bytes) -> &str`:
- Uses `str::floor_char_boundary()` (stable since Rust 1.80) to find the
nearest valid character boundary at or before the byte limit
- Returns the full string when shorter than the limit
**Three call sites fixed** in `label_response`:
| Location | Before | After |
|----------|--------|-------|
| Path-specific output preview (~L808) | `&output_json[..500]` —
**panics** | `safe_preview(&output_json, 500)` |
| General output preview (~L939) | `&output_json[..500]` — **panics** |
`safe_preview(&output_json, 500)` |
| Input preview (~L752) | `from_utf8(&bytes[..500])` — silent drop |
`safe_preview(full_str, 500)` — always logs |
**8 unit tests** covering:
- ASCII strings (under, at, and over the 500-byte limit)
- CJK characters (3-byte UTF-8) crossing the boundary
- Emoji (4-byte UTF-8) crossing the boundary
- Accented characters (2-byte UTF-8) at exact boundary
- Mixed ASCII + CJK content simulating real JSON payloads
- Empty string edge case
## Evidence
Discovered in `moeru-ai/airi` PR triage workflow ([run
#24311673575](https://github.com/moeru-ai/airi/actions/runs/24311673575))
— a PR with a Chinese body caused the guard to crash, and all subsequent
MCP calls failed with "WASM guard is unavailable after a previous trap".
## Verification
`make agent-finished` passes — all unit + integration tests green.1 file changed
+118
-13
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
29 | 46 | | |
30 | 47 | | |
31 | 48 | | |
| |||
748 | 765 | | |
749 | 766 | | |
750 | 767 | | |
751 | | - | |
752 | | - | |
753 | | - | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
754 | 784 | | |
755 | 785 | | |
756 | 786 | | |
| |||
804 | 834 | | |
805 | 835 | | |
806 | 836 | | |
807 | | - | |
808 | | - | |
809 | | - | |
810 | | - | |
811 | | - | |
| 837 | + | |
812 | 838 | | |
813 | 839 | | |
814 | 840 | | |
| |||
935 | 961 | | |
936 | 962 | | |
937 | 963 | | |
938 | | - | |
939 | | - | |
940 | | - | |
941 | | - | |
942 | | - | |
| 964 | + | |
943 | 965 | | |
944 | 966 | | |
945 | 967 | | |
| |||
1177 | 1199 | | |
1178 | 1200 | | |
1179 | 1201 | | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
| 1206 | + | |
| 1207 | + | |
| 1208 | + | |
| 1209 | + | |
| 1210 | + | |
| 1211 | + | |
| 1212 | + | |
| 1213 | + | |
| 1214 | + | |
| 1215 | + | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
| 1230 | + | |
| 1231 | + | |
| 1232 | + | |
| 1233 | + | |
| 1234 | + | |
| 1235 | + | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
| 1240 | + | |
| 1241 | + | |
| 1242 | + | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
| 1252 | + | |
| 1253 | + | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
1180 | 1285 | | |
0 commit comments