mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-07 17:09:13 -10:00
fix(authority): stop the PID-proof scan from slicing inside a multi-byte character
Issue #7048: a Cyrillic session panics and loses the whole TUI, ten times in two weeks, always "start byte index N is not a char boundary". The reported line moved between versions, so this was found from the signature rather than the line number. `pid_var_proofs` walks the command by byte to find `$` sigils, then asked whether the two bytes before the sigil are a `++` or `--` prefix: let prefix_mutated = i >= 2 && matches!(&stmt[i - 2..i], "++" | "--"); `i` is always a character boundary, because it indexes an ASCII `$`. `i - 2` is not: a two-byte Cyrillic character followed by one ASCII byte and then `$` puts `i - 2` inside that character, and slicing a `&str` there panics. Latin text never hits it, which is why this survived to a release and why the reporter saw it only in Russian. The comparison now reads the same two bytes as `&[u8]`, which has no boundary requirement: let prefix_mutated = i >= 2 && matches!(&bytes[i - 2..i], [b'+', b'+'] | [b'-', b'-']); `bytes` is already `stmt.as_bytes()` from the loop above, so nothing new is allocated. Verification: a standalone harness ran both forms over five inputs. The old form panics on `яX$p`, `тоY$p` and `имяZ$p`; the new form returns false on all three and still returns true for `++$p`, so the proof semantics are unchanged. That harness compiles the exact expression used here, so it also checks the slice pattern's types. `rustfmt --check` on the file exits 0. Not verified in-crate: `cargo check -p codewhale-tui --lib` could not run - the shared build lock was held by another session's test for the whole attempt and the command timed out waiting. CI is the verifier for the crate build. The reporter also describes a second site where evidence preparation slices command output by byte offset. I did not find it: `AutoReviewContext::from_tool_call_inner` and everything it calls in this file contain no byte-index text slice, and the service-level output truncation in tools/shell_output.rs already walks `char_indices`. That path stays open and is worth its own report. Refs #7048 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
ba0db02e67
commit
26155e9bd0
1 file changed
+6
-1
@@ -1523,7 +1523,12 @@ fn pid_var_proofs(command: &str) -> HashMap<String, PidVarSource> {
|
||||
// Prefix `++`/`--`, destructuring (`$a, $b = ...` proves nothing
|
||||
// about `$b`), postfix `++`/`--`, and compound assignment all
|
||||
// mutate without proving: mark touched, keep no proof.
|
||||
let prefix_mutated = i >= 2 && matches!(&stmt[i - 2..i], "++" | "--");
|
||||
//
|
||||
// Compare bytes, not a `&str` slice: `i` is a boundary because it
|
||||
// points at an ASCII `$`, but `i - 2` can land inside a multi-byte
|
||||
// character. Slicing the string there panicked for any Cyrillic or
|
||||
// CJK input (#7048).
|
||||
let prefix_mutated = i >= 2 && matches!(&bytes[i - 2..i], [b'+', b'+'] | [b'-', b'-']);
|
||||
let destructured = stmt[..i].trim_end().ends_with(',');
|
||||
while j < bytes.len() && (bytes[j] == b' ' || bytes[j] == b'\t') {
|
||||
j += 1;
|
||||
|
||||
Reference in new issue
Block a user