Repository: jsonata-js/jsonata
Pull request #682 (2024-02-29 23:01:41)

Publish version 1.8.7
Publish version 1.8.7 with backported fix


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ba62a4f..2213e9d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+#### 1.8.7 Maintenance Release
+
+- Prevent writing to the object prototype or constructor (PR https://github.com/jsonata-js/jsonata/pull/681)
+
 #### 1.8.6 Maintenance Release
 
 - Fix bug in date/time picture string. Width formatting is not respected without a separator (issue #546)
diff --git a/package.json b/package.json
index 1d32c74..fe1bf0f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "jsonata",
-  "version": "1.8.6",
+  "version": "1.8.7",
   "description": "JSON query and transformation language",
   "module": "jsonata.js",
   "main": "jsonata.js",
diff --git a/src/jsonata.js b/src/jsonata.js
index 19b0940..a22d83b 100644
--- a/src/jsonata.js
+++ b/src/jsonata.js
@@ -1295,6 +1295,13 @@ var jsonata = (function() {
                 }
                 for(var ii = 0; ii < matches.length; ii++) {
                     var match = matches[ii];
+                    if (match && (match.isPrototypeOf(result) || match instanceof Object.constructor)) {
+                        throw {
+                            code: "D1010",
+                            stack: (new Error()).stack,
+                            position: expr.position
+                        };
+                    }
                     // evaluate the update value for each match
                     var update = yield * evaluate(expr.update, match, environment);
                     // update must be an object
@@ -1539,7 +1546,7 @@ var jsonata = (function() {
                 if (typeof err.token == 'undefined' && typeof proc.token !== 'undefined') {
                     err.token = proc.token;
                 }
-                err.position = proc.position;
+                err.position = proc.position || err.position;
             }
             throw err;
         }
@@ -1971,6 +1978,7 @@ var jsonata = (function() {
         "T1007": "Attempted to partially apply a non-function. Did you mean ${{{token}}}?",
         "T1008": "Attempted to partially apply a non-function",
         "D1009": "Multiple key definitions evaluate to same key: {{value}}",
+        "D1010": "Attempted to access the Javascript object prototype", // Javascript specific 
         "T1010": "The matcher function argument passed to function {{token}} does not return the correct object structure",
         "T2001": "The left side of the {{token}} operator must evaluate to a number",
         "T2002": "The right side of the {{token}} operator must evaluate to a number",


Test patch:
diff --git a/test/implementation-tests.js b/test/implementation-tests.js
index 9c58a0d..8ca217b 100644
--- a/test/implementation-tests.js
+++ b/test/implementation-tests.js
@@ -806,6 +806,32 @@ describe("Tests that are specific to a Javascript runtime", () => {
             });
         });
     });
+    describe("Expressions that attempt to pollute the object prototype", function() {
+        it("should throw an error with __proto__", async function() {
+            const expr = jsonata('{} ~> | __proto__ | {"is_admin": true} |');
+            expect(function() {
+                expr.evaluate();
+            })
+                .to.throw()
+                .to.deep.contain({ position: 7, code: "D1010" });
+        });
+        it("should throw an error with __lookupGetter__", async function() {
+            const expr = jsonata('{} ~> | __lookupGetter__("__proto__")() | {"is_admin": true} |');
+            expect(function() {
+                expr.evaluate();
+            })
+                .to.throw()
+                .to.deep.contain({ position: 7, code: "D1010" });
+        });
+        it("should throw an error with constructor", async function() {
+            const expr = jsonata('{} ~> | constructor | {"is_admin": true} |');
+            expect(function() {
+                expr.evaluate();
+            })
+                .to.throw()
+                .to.deep.contain({ position: 7, code: "D1010" });
+        });
+    });
 });
 
 describe("Test that yield platform specific results", () => {