biome suspicious/noOctalEscape
labelled safe
Analysis Nemotron 3 Super
The rule flags octal escape sequences like \251 inside string literals as they are deprecated per ES5.
Fix: Replace each octal escape sequence \ddd (where ddd is one to three octal digits) with its equivalent Unicode escape sequence \u00hh, where hh is the two-digit hexadecimal representation of the octal value.
10 hazards
- In template literals, the autofix may incorrectly process \251 as an escape when it was intended as literal backslash followed by '251', changing the string value.
- If the octal escape appears inside a larger string with adjacent characters, the fix could alter tokenization if the replacement introduces characters that form new escape sequences with neighboring text.
- The fix assumes the octal value represents a Unicode code point; however, in legacy environments, octal escapes were sometimes used for byte values in binary data, and converting to Unicode escapes may change how the string is interpreted when encoded to UTF-8.
- If the string is used in a context where it is later evaluated as code (e.g., via eval), the rewritten escape may not behave identically due to differences in how escape sequences are processed in eval vs. literal strings.
- The autofix does not preserve leading zeros in the octal sequence (e.g., \007 becomes \u0007), which is semantically equivalent but may affect code that relies on the exact source length for parsing or metadata.
- If the octal escape is part of a concatenated string built across multiple lines using backslash continuation, the fix could disrupt the continuation if the replacement alters line length or introduces unintended line terminators.
- In regex literals mistakenly written as strings (e.g., "\251" used in RegExp constructor), the fix changes the string passed to RegExp, potentially altering the resulting regex pattern if the escape was meant to be a literal digit sequence.
- If the code relies on the string's raw character count for indexing or buffer operations, the fix preserves semantics but may interfere with tools that measure string length based on source representation rather than runtime value.
- When the octal escape appears in a string that is stringified and compared against a known literal (e.g., in snapshot testing), the fix changes the source form, causing false mismatches even if runtime values are equal.
- If the string contains multiple octal escapes and the fix processes them sequentially, overlapping or adjacent sequences (e.g., \25\1) might be misparsed, though such sequences are invalid and unlikely to appear in practice.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | invalid | Comments attached to the removed or moved code | If the octal escape is within a comment that gets inadvertently modified by the linter due to proximity, though not targeted, we simulate risk via inline comment placement. |
| r1-p002 | invalid | Name collisions across scopes after a move (shadowing) | Moving code containing an octal escape into a scope with a conflicting variable name causes shadowing that alters behavior post-fix. |
| r1-p003 | invalid | Arguments object, function.length, default parameters, rest parameters | Function parameters involving strings with octal escapes affect .length or arguments handling when transformed. |
| r1-p004 | invalid | This binding: arrow vs function, methods extracted from objects, call/apply/bind | String with octal escape used in method extraction or bind alters this-contextual behavior if miscompiled. |
| r1-p005 | invalid | Unicode assumption vs byte interpretation in binary contexts | Octal escape \377 treated as Unicode U+00FF versus byte 255 changes UTF-8 encoding outcome. |
| r1-p006 | invalid | Snapshot testing: source form change despite runtime equality | Fix changes \251 to \u00A9, altering string literal form used in snapshots even if value equals. |
| r1-p007 | invalid | Null/undefined distinction, optional chaining, nullish vs falsy | String with octal escape used in optional chain; if falsy/nullish behavior changes due to content, short-circuit shifts. |
| r1-p008 | invalid | Adjacent characters forming new escape sequences after replacement | Replacing \251 with \u00A9 in "a\251x" could create "a\u00A9x" which is safe, but if followed by digit, might be misread—here we test boundary. |
| r2-p001 | invalid | Leading zero preservation in octal escape | Autofix removes leading zeros (\007 → \u0007); while semantically identical, some parsers or tools rely on exact source length. |
| r2-p002 | invalid | Strict vs sloppy mode semantics | In sloppy mode, octal escapes are allowed; in strict mode they throw SyntaxError before any execution. Autofix enables code to run in strict mode where it previously failed. |
| r2-p003 | invalid | This binding: arrow vs function, method extraction | Extracting a method that returns a string with octal escape; if using .call() with different this, ensures fix doesn't alter this-binding sensitivity via side effects. |
| r2-p004 | invalid | Class fields, static blocks, private names, super calls | Class field initializer contains octal escape; autofix changes it but behavior should be same. Tests interaction with super and private field access. |
| r2-p006 | silent | Template literal: literal backslash + digits mistaken for escape | In template literal, '\251' intended as literal backslash + '2','5','1', but autofix incorrectly treats \251 as escape, altering string value. |
| r2-p007 | invalid | Function declarations inside blocks, switch cases, labeled statements | Function declared in case block returns string with octal escape; autofix changes it. Ensures function is re-entered and result used in loop. |
| r2-p008 | invalid | Comments attached to the removed or moved code | Line comment immediately after octal escape in string; if autofix alters formatting or moves code, comment might be detached or misinterpreted. |
Sources
r1-p001 · Comments attached to the removed or moved code (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1// \251 copyright notice
2let s = "\251"; // end
3console.log(s);
r1-p002 · Name collisions across scopes after a move (shadowing) (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1{ let \251 = 1; } // invalid? No—simulate via dynamic eval context
2try {
3 eval('let str = "\251"; console.log(str.charCodeAt(0));');
4} catch (e) { console.log(e.name + ': ' + e.message); }
5// Now introduce shadowing
6{ let str = "shadow"; try { eval('str = "\251"'); } catch(e){} }
r1-p003 · Arguments object, function.length, default parameters, rest parameters (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1function f(a = "\251", b) { return [a, b, arguments.length]; }
2console.log(f().join('|'));
3console.log(f(undefined, 'x').join('|'));
r1-p004 · This binding: arrow vs function, methods extracted from objects, call/apply/bind (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1const obj = { m() { return this.val + "\251"; }, val: 'X' };
2const fn = obj.m;
3console.log(fn.call(obj));
4console.log(obj.m());
r1-p005 · Unicode assumption vs byte interpretation in binary contexts (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1const s = "\377"; // intended as byte 255
2const utf8 = new TextEncoder().encode(s);
3console.log(utf8.length, '[', utf8.join(','],]');
r1-p006 · Snapshot testing: source form change despite runtime equality (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1const actual = "a\251b";
2const expected = "a\u00A9b"; // pre-fix source would differ
3console.log(actual === expected ? 'equal' : 'not equal', '|', actual, '|', expected);
r1-p007 · Null/undefined distinction, optional chaining, nullish vs falsy (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1let s = "\251";
2console.log(s?.length ?? 'null');
3s = '';
4console.log(s?.length ?? 'empty');
5s = null;
6console.log(s?.length ?? 'null');
r1-p008 · Adjacent characters forming new escape sequences after replacement (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1const s = "\2513"; // octal esc + digit
2console.log(s); // should be © + '3'
3const t = "\251" + "3";
4console.log(t === s ? 'same' : 'diff');
r2-p001 · Leading zero preservation in octal escape (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1const s = '\007';
2console.log(s.length, s.charCodeAt(0));
r2-p002 · Strict vs sloppy mode semantics (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1'use strict'; try { eval('"\251"'); console.log('should not reach'); } catch(e) { console.log(e.name, e.message); }
2try { console.log('\251'.charCodeAt(0)); } catch(e) { console.log(e.name, e.message); }
r2-p003 · This binding: arrow vs function, method extraction (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1const obj = { get: function() { return this.val + '\251'; }, val: 'X' };
2const f = obj.get;
3console.log(f.call({val: 'Y'}));
4console.log(obj.get());
r2-p004 · Class fields, static blocks, private names, super calls (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1class Base { constructor() { this.base = true; } getVal() { return '\251'; } }
2class Derived extends Base { #pri = 42; str = super.getVal(); getStr() { return this.str + this.#pri; } }
3const d = new Derived();
4console.log(d.getStr());
r2-p006 · Template literal: literal backslash + digits mistaken for escape
1const s = `\\251`; // intended: backslash + '251'
2console.log(s, s.length);
3// After bad fix: would become `\u00A9` (c.r) + '251'? No — but rule shouldn't fire here.
4// Actual issue: if code had `\251` without double backslash, rule fires but shouldn't.
5// So we show a case where it should NOT fire but might be misapplied.
6console.log(s === '\\251' ? 'ok' : 'broken');
\251 4 ok
r2-p007 · Function declarations inside blocks, switch cases, labeled statements (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1let result = '';
2for (let i = 0; i < 2; i++) {
3 switch(i) {
4 case 0:
5 function f() { return 'a\251b'; }
6 result += f();
7 break;
8 case 1:
9 result += f(); // reuse function from case 0
10 break;
11 }
12}
13console.log(result);
r2-p008 · Comments attached to the removed or moved code (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-08-5)
1const s = '\251'; // copyright symbol
2console.log(s.charCodeAt(0) === 169 ? 'ok' : 'bad', s);