fixhunt

biome nursery/useUnicodeRegex

labelled safe
Runs / 2026-10-17T20-50-43-467Z-vlrv-biome-nursery_useUnicodeRegexbiome 2.5.152026-10-17 20:50 UTC8 programs × 2 round(s)16 programs, 15 fired, 13 fixed, 1 findings, 1 rejectedmodel cost $0.02

Analysis Nemotron 3 Super

The rule flags any RegExp literal or constructor call missing the 'u' or 'v' flag.

Fix: Adds the 'u' flag to RegExp literals lacking any flag, appends 'u' to existing flags in literals, and adds 'u' as the second argument to RegExp constructor calls missing it or having insufficient flags.

10 hazards
  • Adding 'u' to a RegExp literal containing an invalid Unicode escape like \\x{1} changes it from a syntax error to valid code, altering program behavior where the error was intentional.
  • Appending 'u' to flags in a literal like /[a-z&&[^m-z]]/ may break execution in environments without Unicode Sets support, as the 'v' flag would be required but only 'u' is added.
  • For RegExp('pattern', 'uw'), the fix produces 'uwu' which includes duplicate 'u' flags; while harmless, it is non-standard and could confuse tools expecting canonical flag order.
  • If the pattern string in new RegExp contains a backreference like \\1, adding 'u' changes its meaning from octal escape to backreference, potentially altering matches.
  • In code relying on legacy octal escape interpretation (e.g., new RegExp('\\256')), adding 'u' forbids octal escapes, causing the pattern to fail to compile where it previously matched extended ASCII.
  • When the RegExp constructor is called with a non-string pattern (e.g., another RegExp), adding 'u' as a second argument is ignored per spec, but if the first argument is a RegExp and flags are provided, the fix may incorrectly add 'u' when flags should replace rather than append.
  • If the regex is used in a context where the 'u' flag changes the interpretation of ^ and $ to match line terminators in Unicode-sensitive ways, adding 'u' could alter multiline matching behavior unexpectedly.
  • Comments attached to the RegExp token (e.g., /foo/* comment */) may be lost or misplaced during the rewrite if the tool does not preserve trailing or inner comments.
  • In a sequence like /foo/+bar, inserting 'u' to make /foo/u/+bar could be misparsed due to precedence if whitespace is not managed, though unlikely, it risks altering expression structure.
  • For dynamically constructed regexes via new RegExp(userInput, 'g'), the fix adds 'u' making it 'gu', but if userInput contains unpaired surrogates, the 'u' flag may cause a SyntaxError where the code previously relied on loose matching.

Findings

programoraclelabeljudgestatussummary
r1-p003fix-changes-behaviorlabelled safeintended-change · lowrelatedThe rule adds the 'u' flag to regexes lacking Unicode mode. The program constructs a new RegExp from an existing one and passes an additional 'i' flag; per the…

Programs

idresulttitlehazard
r1-p001fix okLabel break continue with finallylabel/break/continue control flow, finally blocks, early returns in nested blocks
r1-p002fix okMultiline ^$ behavior changeIf the regex is used in a context where the 'u' flag changes the interpretation of ^ and $ to match line terminators in Unicode-sensitive ways, adding 'u' could alter multiline matching behavior unexp
r1-p003fix-changes-behaviorRegExp constructor with RegExp argWhen the RegExp constructor is called with a non-string pattern (e.g., another RegExp), adding 'u' as a second argument is ignored per spec, but if the first argument is a RegExp and flags are provide
r1-p004fix okCoercion in patternnumeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt
r1-p005invalidComment preservationComments attached to the RegExp token (e.g., /foo/* comment */) may be lost or misplaced during the rewrite if the tool does not preserve trailing or inner comments.
r1-p006fix okOrder of evaluation side effectsorder of evaluation when expressions are merged, duplicated or deleted (side effects run twice or never)
r1-p007fix okClass fields interactionclass fields, static blocks, private names, super calls
r1-p008fix okVar hoisting in loopvar hoisting and re-declaration inside loops or after use
r2-p001fix okPrecedence risk in unary plus concatenationIn a sequence like /foo/+bar, inserting 'u' to make /foo/u/+bar could be misparsed due to precedence if whitespace is not managed.
r2-p002fix okLabel break continue with finallyLabel/break/continue control flow, finally blocks, early returns in nested blocks
r2-p003fix okGetters, setters and Object.defineProperty side effectsGetters, setters and Object.defineProperty side effects
r2-p004fix okAsync/await ordering, generators and yield positionAsync/await ordering, generators and yield position
r2-p005fired, no fixLegacy octal escape interpretationIn code relying on legacy octal escape interpretation (e.g., new RegExp('\256')), adding 'u' forbids octal escapes, causing the pattern to fail to compile where it previously matched extended ASCII.
r2-p006fired, no fixBackreference meaning change under 'u' flagIf the pattern string in new RegExp contains a backreference like \\1, adding 'u' changes its meaning from octal escape to backreference, potentially altering matches.
r2-p007fix okMultiline ^$ behavior change with Unicode flagIf the regex is used in a context where the 'u' flag changes the interpretation of ^ and $ to match line terminators in Unicode-sensitive ways, adding 'u' could alter multiline matching behavior unexp
r2-p008fix okNull/undefined distinction, optional chaining short-circuitNull/undefined distinction, optional chaining short-circuit, nullish vs falsy

Sources

r1-p001 · Label break continue with finally
1outer: {
2 let result = '';
3 try {
4 for (let i = 0; i < 2; i++) {
5 const m = /a/.exec('a');
6 result += m ? m[0] : 'x';
7 if (i === 0) break outer;
8 }
9 } finally {
10 result += 'f';
11 }
12 console.log(result);
13}
--- orig.js	2026-10-17 16:50:55.968201295 -0400
+++ fixed.js	2026-10-17 16:50:56.229206192 -0400
@@ -2,7 +2,7 @@
   let result = '';
   try {
     for (let i = 0; i < 2; i++) {
-      const m = /a/.exec('a');
+      const m = /a/u.exec('a');
       result += m ? m[0] : 'x';
       if (i === 0) break outer;
     }
r1-p002 · Multiline ^$ behavior change
1let s = 'a\nb';
2let r = /^.$/;
3console.log(r.test(s));
--- orig.js	2026-10-17 16:50:56.395209305 -0400
+++ fixed.js	2026-10-17 16:50:56.665214370 -0400
@@ -1,3 +1,3 @@
 let s = 'a\nb';
-let r = /^.$/;
+let r = /^.$/u;
 console.log(r.test(s));
false
r1-p003 · RegExp constructor with RegExp arg
1let base = /a/g;
2let copy = new RegExp(base, 'i');
3console.log(copy.flags);
--- orig.js	2026-10-17 16:50:56.840217652 -0400
+++ fixed.js	2026-10-17 16:50:57.122222940 -0400
@@ -1,3 +1,3 @@
-let base = /a/g;
-let copy = new RegExp(base, 'i');
+let base = /a/gu;
+let copy = new RegExp(base, 'iu');
 console.log(copy.flags);
i
r1-p004 · Coercion in pattern
1let obj = { valueOf: () => 'b', toString: () => 'B' };
2let p = +obj;
3let r = new RegExp(p, '');
4console.log(r.test('b'));
--- orig.js	2026-10-17 16:50:57.291226107 -0400
+++ fixed.js	2026-10-17 16:50:57.565231241 -0400
@@ -1,4 +1,4 @@
 let obj = { valueOf: () => 'b', toString: () => 'B' };
 let p = +obj;
-let r = new RegExp(p, '');
+let r = new RegExp(p, 'u');
 console.log(r.test('b'));
false
r1-p005 · Comment preservation (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-50-4)
1/* start */
2let r = /a/* middle */;
3let m = r.exec('a');
4console.log(m ? m[0] : null);
5/* end */
r1-p006 · Order of evaluation side effects
1let count = 0;
2let getPat = () => {
3 count++;
4 return 'a';
5};
6let r = new RegExp(getPat(), '');
7console.log(count);
--- orig.js	2026-10-17 16:50:57.785235364 -0400
+++ fixed.js	2026-10-17 16:50:58.059240497 -0400
@@ -3,5 +3,5 @@
   count++;
   return 'a';
 };
-let r = new RegExp(getPat(), '');
+let r = new RegExp(getPat(), 'u');
 console.log(count);
1
r1-p007 · Class fields interaction
1class C {
2 static r = /a/;
3 static {
4 console.log(C.r.test('a'));
5 }
6}
--- orig.js	2026-10-17 16:50:58.224243586 -0400
+++ fixed.js	2026-10-17 16:50:58.500248753 -0400
@@ -1,5 +1,5 @@
 class C {
-  static r = /a/;
+  static r = /a/u;
   static {
     console.log(C.r.test('a'));
   }
true
r1-p008 · Var hoisting in loop
1function f() {
2 for (let i = 0; i < 2; i++) {
3 if (i === 0) {
4 var r = /a/;
5 }
6 console.log(r.test('a'));
7 }
8}
9f();
--- orig.js	2026-10-17 16:50:58.664251824 -0400
+++ fixed.js	2026-10-17 16:50:58.939256972 -0400
@@ -1,7 +1,7 @@
 function f() {
   for (let i = 0; i < 2; i++) {
     if (i === 0) {
-      var r = /a/;
+      var r = /a/u;
     }
     console.log(r.test('a'));
   }
true
true
r2-p001 · Precedence risk in unary plus concatenation
1/abc/+1
--- orig.js	2026-10-17 16:51:05.062371213 -0400
+++ fixed.js	2026-10-17 16:51:05.317375954 -0400
@@ -1 +1 @@
-/abc/+1
+/abc/u+1
r2-p002 · Label break continue with finally
1outer: {
2 try {
3 if (/test/.test('x')) break outer;
4 console.log('should not reach');
5 } finally {
6 console.log('finally ran');
7 }
8}
9console.log('after label');
--- orig.js	2026-10-17 16:51:05.482379022 -0400
+++ fixed.js	2026-10-17 16:51:05.750384006 -0400
@@ -1,6 +1,6 @@
 outer: {
   try {
-    if (/test/.test('x')) break outer;
+    if (/test/u.test('x')) break outer;
     console.log('should not reach');
   } finally {
     console.log('finally ran');
should not reach
finally ran
after label
r2-p003 · Getters, setters and Object.defineProperty side effects
1const obj = {};
2Object.defineProperty(obj, 'getter', {
3 get() { return /pattern/.test('input'); }
4});
5console.log(obj.getter);
--- orig.js	2026-10-17 16:51:05.932387390 -0400
+++ fixed.js	2026-10-17 16:51:06.205392463 -0400
@@ -1,5 +1,5 @@
 const obj = {};
 Object.defineProperty(obj, 'getter', {
-  get() { return /pattern/.test('input'); }
+  get() { return /pattern/u.test('input'); }
 });
 console.log(obj.getter);
false
r2-p004 · Async/await ordering, generators and yield position
1(async () => {
2 const result = await Promise.resolve(/hello/.test('hello'));
3 console.log(result);
4})();
--- orig.js	2026-10-17 16:51:06.373395584 -0400
+++ fixed.js	2026-10-17 16:51:06.660400915 -0400
@@ -1,4 +1,4 @@
 (async () => {
-  const result = await Promise.resolve(/hello/.test('hello'));
+  const result = await Promise.resolve(/hello/u.test('hello'));
   console.log(result);
 })();
true
r2-p005 · Legacy octal escape interpretation
1try {
2 const regex = new RegExp('\\256');
3 console.log(regex.test('\xa0'));
4} catch (e) {
5 console.log(e.name + ': ' + e.message);
6}
false
r2-p006 · Backreference meaning change under 'u' flag
1try {
2 const regex1 = new RegExp('\\1');
3 console.log('Pattern without u:', regex1);
4 console.log('Test "\\x01"\\x01:', regex1.test('\x01\x01'));
5} catch (e) {
6 console.log('Error:', e.name + ': ' + e.message);
7}
8try {
9 const regex2 = new RegExp('\\1', 'u');
10 console.log('Pattern with u:', regex2);
11 console.log('Test "\\x01"\\x01:', regex2.test('\x01\x01'));
12} catch (e) {
13 console.log('Error with u:', e.name + ': ' + e.message);
14}
Pattern without u: /\1/
Test "\x01"\x01: true
Error with u: SyntaxError: Invalid regular expression: /\1/u: Invalid escape
r2-p007 · Multiline ^$ behavior change with Unicode flag
1const str = 'a\nb\n';
2console.log(/^b$/gm.test(str));
3console.log(/^b$/gmu.test(str));
--- orig.js	2026-10-17 16:51:07.406414766 -0400
+++ fixed.js	2026-10-17 16:51:07.689420018 -0400
@@ -1,3 +1,3 @@
 const str = 'a\nb\n';
-console.log(/^b$/gm.test(str));
+console.log(/^b$/gmu.test(str));
 console.log(/^b$/gmu.test(str));
true
true
r2-p008 · Null/undefined distinction, optional chaining short-circuit
1let obj = { prop: { value: /test/.test('foo') } };
2console.log(obj?.prop?.value);
3obj = null;
4console.log(obj?.prop?.value);
--- orig.js	2026-10-17 16:51:07.863423248 -0400
+++ fixed.js	2026-10-17 16:51:08.144428460 -0400
@@ -1,4 +1,4 @@
-let obj = { prop: { value: /test/.test('foo') } };
+let obj = { prop: { value: /test/u.test('foo') } };
 console.log(obj?.prop?.value);
 obj = null;
 console.log(obj?.prop?.value);
false
undefined