Repository: gchq/CyberChef (JavaScript)

Issue:
Bug report: ROT13 does not rotate numbers properly
The ROT13 operation does not seem to correctly rotate numbers.

To reproduce:

1. Enter `1` as input
2. Add the `ROT13` operation to the recipe
3. Set rotation amount to `-1`

Expected result is `0`, but actual result is `6`

Compare with the way [this decoder](https://www.dcode.fr/caesar-cipher) handles number rotation.


Discussion:

Patch:
diff --git a/src/core/operations/ROT13.mjs b/src/core/operations/ROT13.mjs
index 1d0595658d..beec94a456 100644
--- a/src/core/operations/ROT13.mjs
+++ b/src/core/operations/ROT13.mjs
@@ -59,15 +59,16 @@ class ROT13 extends Operation {
             rot13Upperacse = args[1],
             rotNumbers = args[2];
         let amount = args[3],
-            chr;
+            amountNumbers = args[3];
 
         if (amount) {
             if (amount < 0) {
                 amount = 26 - (Math.abs(amount) % 26);
+                amountNumbers = 10 - (Math.abs(amountNumbers) % 10);
             }
 
             for (let i = 0; i < input.length; i++) {
-                chr = input[i];
+                let chr = input[i];
                 if (rot13Upperacse && chr >= 65 && chr <= 90) { // Upper case
                     chr = (chr - 65 + amount) % 26;
                     output[i] = chr + 65;
@@ -75,7 +76,7 @@ class ROT13 extends Operation {
                     chr = (chr - 97 + amount) % 26;
                     output[i] = chr + 97;
                 } else if (rotNumbers && chr >= 48 && chr <= 57) { // Numbers
-                    chr = (chr - 48 + amount) % 10;
+                    chr = (chr - 48 + amountNumbers) % 10;
                     output[i] = chr + 48;
                 }
             }


Test patch:
diff --git a/tests/operations/tests/Rotate.mjs b/tests/operations/tests/Rotate.mjs
index c12fa3776d..a69e20fde6 100644
--- a/tests/operations/tests/Rotate.mjs
+++ b/tests/operations/tests/Rotate.mjs
@@ -135,10 +135,21 @@ TestRegister.addTests([
             },
         ],
     },
+    {
+        name: "ROT13: no shift amount",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        recipeConfig: [
+            {
+                op: "ROT13",
+                args: [true, true, true, 0]
+            },
+        ],
+    },
     {
         name: "ROT13: normal",
-        input: "The Quick Brown Fox Jumped Over The Lazy Dog.",
-        expectedOutput: "Gur Dhvpx Oebja Sbk Whzcrq Bire Gur Ynml Qbt.",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "Gur Dhvpx Oebja Sbk Whzcrq Bire Gur Ynml Qbt. 3456789012",
         recipeConfig: [
             {
                 op: "ROT13",
@@ -146,10 +157,21 @@ TestRegister.addTests([
             },
         ],
     },
+    {
+        name: "ROT13: negative shift amount",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "Gur Dhvpx Oebja Sbk Whzcrq Bire Gur Ynml Qbt. 7890123456",
+        recipeConfig: [
+            {
+                op: "ROT13",
+                args: [true, true, true, -13]
+            },
+        ],
+    },
     {
         name: "ROT13: full loop",
-        input: "The Quick Brown Fox Jumped Over The Lazy Dog.",
-        expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog.",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 6789012345",
         recipeConfig: [
             {
                 op: "ROT13",
@@ -157,10 +179,21 @@ TestRegister.addTests([
             },
         ],
     },
+    {
+        name: "ROT13: full loop (negative shift amount)",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 4567890123",
+        recipeConfig: [
+            {
+                op: "ROT13",
+                args: [true, true, true, -26]
+            },
+        ],
+    },
     {
         name: "ROT13: lowercase only",
-        input: "The Quick Brown Fox Jumped Over The Lazy Dog.",
-        expectedOutput: "Tur Qhvpx Bebja Fbk Jhzcrq Oire Tur Lnml Dbt.",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "Tur Qhvpx Bebja Fbk Jhzcrq Oire Tur Lnml Dbt. 0123456789",
         recipeConfig: [
             {
                 op: "ROT13",
@@ -170,8 +203,8 @@ TestRegister.addTests([
     },
     {
         name: "ROT13: uppercase only",
-        input: "The Quick Brown Fox Jumped Over The Lazy Dog.",
-        expectedOutput: "Ghe Duick Orown Sox Wumped Bver Ghe Yazy Qog.",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "Ghe Duick Orown Sox Wumped Bver Ghe Yazy Qog. 0123456789",
         recipeConfig: [
             {
                 op: "ROT13",
@@ -179,6 +212,50 @@ TestRegister.addTests([
             },
         ],
     },
+    {
+        name: "ROT13: numbers only",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 5678901234",
+        recipeConfig: [
+            {
+                op: "ROT13",
+                args: [false, false, true, 5]
+            },
+        ],
+    },
+    {
+        name: "ROT13: numbers only (negative shift amount)",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 5678901234",
+        recipeConfig: [
+            {
+                op: "ROT13",
+                args: [false, false, true, 5]
+            },
+        ],
+    },
+    {
+        name: "ROT13: numbers only loop",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        recipeConfig: [
+            {
+                op: "ROT13",
+                args: [false, false, true, 10]
+            },
+        ],
+    },
+    {
+        name: "ROT13: numbers only loop (negative shift amount)",
+        input: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        expectedOutput: "The Quick Brown Fox Jumped Over The Lazy Dog. 0123456789",
+        recipeConfig: [
+            {
+                op: "ROT13",
+                args: [false, false, true, -10]
+            },
+        ],
+    },
     {
         name: "ROT47: nothing",
         input: "",