Forráskód Böngészése

Fix RegExp.prototype.source double-escape (#1455)

Marko Lahma 2 éve
szülő
commit
ec623b2aa4

+ 83 - 76
Jint.Tests/Runtime/RegExpTests.cs

@@ -2,98 +2,105 @@
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Array;
 using Jint.Native.Array;
 
 
-namespace Jint.Tests.Runtime
+namespace Jint.Tests.Runtime;
+
+public class RegExpTests
 {
 {
-    public class RegExpTests
+    private const string TestRegex = "^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w\\.-]*)*\\/?$";
+    private const string TestedValue = "https://archiverbx.blob.core.windows.net/static/C:/Users/USR/Documents/Projects/PROJ/static/images/full/1234567890.jpg";
+
+    [Fact]
+    public void CanNotBreakEngineWithLongRunningMatch()
     {
     {
-        private readonly string testRegex = "^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w\\.-]*)*\\/?$";
-        private readonly string testedValue = "https://archiverbx.blob.core.windows.net/static/C:/Users/USR/Documents/Projects/PROJ/static/images/full/1234567890.jpg";
+        var engine = new Engine(e => e.RegexTimeoutInterval(TimeSpan.FromSeconds(1)));
 
 
-        [Fact]
-        public void CanNotBreakEngineWithLongRunningMatch()
+        Assert.Throws<RegexMatchTimeoutException>(() =>
         {
         {
-            var engine = new Engine(e => e.RegexTimeoutInterval(TimeSpan.FromSeconds(1)));
+            engine.Execute($"'{TestedValue}'.match(/{TestRegex}/)");
+        });
+    }
 
 
-            Assert.Throws<RegexMatchTimeoutException>(() =>
-            {
-                engine.Execute($"'{testedValue}'.match(/{testRegex}/)");
-            });
-        }
+    [Fact]
+    public void CanNotBreakEngineWithLongRunningRegExp()
+    {
+        var engine = new Engine(e => e.RegexTimeoutInterval(TimeSpan.FromSeconds(1)));
 
 
-        [Fact]
-        public void CanNotBreakEngineWithLongRunningRegExp()
+        Assert.Throws<RegexMatchTimeoutException>(() =>
         {
         {
-            var engine = new Engine(e => e.RegexTimeoutInterval(TimeSpan.FromSeconds(1)));
+            engine.Execute($"'{TestedValue}'.match(new RegExp(/{TestRegex}/))");
+        });
+    }
 
 
-            Assert.Throws<RegexMatchTimeoutException>(() =>
-            {
-               engine.Execute($"'{testedValue}'.match(new RegExp(/{testRegex}/))");
-            });
-        }
+    [Fact]
+    public void PreventsInfiniteLoop()
+    {
+        var engine = new Engine();
+        var result = (ArrayInstance)engine.Evaluate("'x'.match(/|/g);");
+        Assert.Equal((uint) 2, result.Length);
+        Assert.Equal("", result[0]);
+        Assert.Equal("", result[1]);
+    }
 
 
-        [Fact]
-        public void PreventsInfiniteLoop()
-        {
-            var engine = new Engine();
-            var result = (ArrayInstance)engine.Evaluate("'x'.match(/|/g);");
-            Assert.Equal((uint) 2, result.Length);
-            Assert.Equal("", result[0]);
-            Assert.Equal("", result[1]);
-        }
-
-        [Fact]
-        public void ToStringWithNonRegExpInstanceAndMissingProperties()
-        {
-            var engine = new Engine();
-            var result = engine.Evaluate("/./['toString'].call({})").AsString();
+    [Fact]
+    public void ToStringWithNonRegExpInstanceAndMissingProperties()
+    {
+        var engine = new Engine();
+        var result = engine.Evaluate("/./['toString'].call({})").AsString();
 
 
-            Assert.Equal("/undefined/undefined", result);
-        }
+        Assert.Equal("/undefined/undefined", result);
+    }
 
 
-        [Fact]
-        public void ToStringWithNonRegExpInstanceAndValidProperties()
-        {
-            var engine = new Engine();
-            var result = engine.Evaluate("/./['toString'].call({ source: 'a', flags: 'b' })").AsString();
+    [Fact]
+    public void ToStringWithNonRegExpInstanceAndValidProperties()
+    {
+        var engine = new Engine();
+        var result = engine.Evaluate("/./['toString'].call({ source: 'a', flags: 'b' })").AsString();
 
 
-            Assert.Equal("/a/b", result);
-        }
+        Assert.Equal("/a/b", result);
+    }
 
 
-        [Fact]
-        public void MatchAllIteratorReturnsCorrectNumberOfElements()
-        {
-            var engine = new Engine();
-            var result = engine.Evaluate("[...'one two three'.matchAll(/t/g)].length").AsInteger();
-            
-            Assert.Equal(2, result);
-        }
-
-        [Fact]
-        public void ToStringWithRealRegExpInstance()
-        {
-            var engine = new Engine();
-            var result = engine.Evaluate("/./['toString'].call(/test/g)").AsString();
+    [Fact]
+    public void MatchAllIteratorReturnsCorrectNumberOfElements()
+    {
+        var engine = new Engine();
+        var result = engine.Evaluate("[...'one two three'.matchAll(/t/g)].length").AsInteger();
 
 
-            Assert.Equal("/test/g", result);
-        }
+        Assert.Equal(2, result);
+    }
 
 
-        [Fact]
-        public void ShouldNotThrowErrorOnIncompatibleRegex()
-        {
-            var engine = new Engine();
-            Assert.NotNull(engine.Evaluate(@"/[^]*?(:[rp][el]a[\w-]+)[^]*/"));
-            Assert.NotNull(engine.Evaluate("/[^]a/"));
-            Assert.NotNull(engine.Evaluate("new RegExp('[^]a')"));
+    [Fact]
+    public void ToStringWithRealRegExpInstance()
+    {
+        var engine = new Engine();
+        var result = engine.Evaluate("/./['toString'].call(/test/g)").AsString();
 
 
-            Assert.NotNull(engine.Evaluate("/[]/"));
-            Assert.NotNull(engine.Evaluate("new RegExp('[]')"));
-        }
+        Assert.Equal("/test/g", result);
+    }
 
 
-        [Fact]
-        public void ShouldNotThrowErrorOnRegExNumericNegation()
-        {
-            var engine = new Engine();
-            Assert.True(ReferenceEquals(JsNumber.DoubleNaN, engine.Evaluate("-/[]/")));
-        }
+    [Fact]
+    public void ShouldNotThrowErrorOnIncompatibleRegex()
+    {
+        var engine = new Engine();
+        Assert.NotNull(engine.Evaluate(@"/[^]*?(:[rp][el]a[\w-]+)[^]*/"));
+        Assert.NotNull(engine.Evaluate("/[^]a/"));
+        Assert.NotNull(engine.Evaluate("new RegExp('[^]a')"));
+
+        Assert.NotNull(engine.Evaluate("/[]/"));
+        Assert.NotNull(engine.Evaluate("new RegExp('[]')"));
+    }
+
+    [Fact]
+    public void ShouldNotThrowErrorOnRegExNumericNegation()
+    {
+        var engine = new Engine();
+        Assert.True(ReferenceEquals(JsNumber.DoubleNaN, engine.Evaluate("-/[]/")));
+    }
+
+    [Fact]
+    public void ShouldProduceCorrectSourceForSlashEscapes()
+    {
+        var engine = new Engine();
+        var source = engine.Evaluate(@"/\/\//.source");
+        Assert.Equal("\\/\\/", source);
     }
     }
 }
 }

+ 7 - 3
Jint/Native/RegExp/RegExpPrototype.cs

@@ -111,12 +111,16 @@ namespace Jint.Native.RegExp
                 ExceptionHelper.ThrowTypeError(_realm);
                 ExceptionHelper.ThrowTypeError(_realm);
             }
             }
 
 
-            if (r.Source is null)
+            if (string.IsNullOrEmpty(r.Source))
             {
             {
-                return JsString.Empty;
+                return RegExpInstance.regExpForMatchingAllCharacters;
             }
             }
 
 
-            return r.Source.Replace("/", "\\/").Replace("\n", "\\n");
+
+            return r.Source
+                .Replace("\\/", "/") // ensure forward-slashes
+                .Replace("/", "\\/") // then escape again
+                .Replace("\n", "\\n");
         }
         }
 
 
         /// <summary>
         /// <summary>