guard SGR 38/48 lookahead; parse colon sub-parameters (T-369)
printf '\e[38m' was a RangeError inside Terminal.write — the extended-color branches indexed params[i+1..i+4] unguarded. An emulator must never throw on hostile bytes. Both branches now share a bounds-checked helper that ignores truncated sequences. Colons were silently dropped mid-CSI, fusing 38:2:255:0:0 into one bogus parameter; the consumer now records ECMA-48 sub-parameter links, so ITU T.416 colon-form truecolor/256-color (with or without the colorspace slot) parses identically to the semicolon form, and a malformed colon group is dropped whole instead of bleeding into neighbouring SGR codes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -676,6 +676,56 @@ void main() {
|
||||
f.parser.write('\x1b[123m');
|
||||
expect(f.h.named('unsupportedStyle').first.args, [123]);
|
||||
});
|
||||
|
||||
// T-369: an emulator must never throw on hostile bytes. The old code did
|
||||
// unguarded params[i+1] lookahead in 38/48 — `\x1b[38m` was a RangeError
|
||||
// inside Terminal.write.
|
||||
test('truncated 38/48 sequences are ignored, never throw', () {
|
||||
final f = _newParser();
|
||||
for (final s in ['\x1b[38m', '\x1b[48m', '\x1b[38;2m', '\x1b[38;2;255m', '\x1b[38;2;255;10m', '\x1b[38;5m', '\x1b[48;5m', '\x1b[38:2m', '\x1b[38:5m', '\x1b[48:2:255m']) {
|
||||
f.parser.write(s);
|
||||
}
|
||||
expect(f.h.named('setForegroundColorRgb'), isEmpty);
|
||||
expect(f.h.named('setBackgroundColorRgb'), isEmpty);
|
||||
expect(f.h.named('setForegroundColor256'), isEmpty);
|
||||
expect(f.h.named('setBackgroundColor256'), isEmpty);
|
||||
});
|
||||
|
||||
test('colon-form truecolor matches semicolon form (ITU T.416)', () {
|
||||
final f = _newParser();
|
||||
f.parser.write('\x1b[38:2:10:20:30m\x1b[48:2:100:150:200m');
|
||||
expect(f.h.named('setForegroundColorRgb').first.args, [10, 20, 30]);
|
||||
expect(f.h.named('setBackgroundColorRgb').first.args, [100, 150, 200]);
|
||||
});
|
||||
|
||||
test('colon-form with empty colorspace slot — 38:2::r:g:b', () {
|
||||
final f = _newParser();
|
||||
f.parser.write('\x1b[38:2::10:20:30m');
|
||||
expect(f.h.named('setForegroundColorRgb').first.args, [10, 20, 30]);
|
||||
});
|
||||
|
||||
test('colon-form 256-colour — 38:5:n', () {
|
||||
final f = _newParser();
|
||||
f.parser.write('\x1b[38:5:200m\x1b[48:5:42m');
|
||||
expect(f.h.named('setForegroundColor256').first.args, [200]);
|
||||
expect(f.h.named('setBackgroundColor256').first.args, [42]);
|
||||
});
|
||||
|
||||
test('a malformed colon group is dropped whole, neighbours still apply', () {
|
||||
final f = _newParser();
|
||||
// The bogus 38:2:255 group must not bleed into the following bold.
|
||||
f.parser.write('\x1b[38:2:255;1m');
|
||||
expect(f.h.named('setForegroundColorRgb'), isEmpty);
|
||||
expect(f.h.named('setCursorBold').length, 1);
|
||||
});
|
||||
|
||||
test('extended color followed by more SGR params keeps positions', () {
|
||||
final f = _newParser();
|
||||
f.parser.write('\x1b[1;38;2;10;20;30;4m');
|
||||
expect(f.h.named('setCursorBold').length, 1);
|
||||
expect(f.h.named('setForegroundColorRgb').first.args, [10, 20, 30]);
|
||||
expect(f.h.named('setCursorUnderline').length, 1);
|
||||
});
|
||||
});
|
||||
|
||||
group('EscapeParser — OSC sequences', () {
|
||||
|
||||
Reference in New Issue
Block a user