Author: fdmanana
Date: Tue Nov 23 16:29:30 2010
New Revision: 1038193

URL: http://svn.apache.org/viewvc?rev=1038193&view=rev
Log:
Seal documents before passing them to map functions (JavaScript view server 
only).
This prevents one map function from modifying a document before it's passed to 
another map function.
Has no effect on array fields for some Spidermonkey versions (see 
https://bugzilla.mozilla.org/show_bug.cgi?id=449657).

Closes COUCHDB-925.


Modified:
    couchdb/trunk/share/server/util.js
    couchdb/trunk/share/server/views.js
    couchdb/trunk/share/www/script/test/view_sandboxing.js

Modified: couchdb/trunk/share/server/util.js
URL: 
http://svn.apache.org/viewvc/couchdb/trunk/share/server/util.js?rev=1038193&r1=1038192&r2=1038193&view=diff
==============================================================================
--- couchdb/trunk/share/server/util.js (original)
+++ couchdb/trunk/share/server/util.js Tue Nov 23 16:29:30 2010
@@ -98,7 +98,13 @@ var Couch = {
   },
   recursivelySeal : function(obj) {
     // seal() is broken in current Spidermonkey
-    seal(obj);
+    try {
+      seal(obj);
+    } catch (x) {
+      // Sealing of arrays broken in some SpiderMonkey versions.
+      // https://bugzilla.mozilla.org/show_bug.cgi?id=449657
+      return;
+    }
     for (var propname in obj) {
       if (typeof obj[propname] == "object") {
         arguments.callee(obj[propname]);

Modified: couchdb/trunk/share/server/views.js
URL: 
http://svn.apache.org/viewvc/couchdb/trunk/share/server/views.js?rev=1038193&r1=1038192&r2=1038193&view=diff
==============================================================================
--- couchdb/trunk/share/server/views.js (original)
+++ couchdb/trunk/share/server/views.js Tue Nov 23 16:29:30 2010
@@ -105,19 +105,8 @@ var Views = (function() {
       // ]
       //
 
-      /*
-      Immutable document support temporarily removed.
+      Couch.recursivelySeal(doc);
 
-      Removed because the seal function no longer works on JS 1.8 arrays,
-      instead returning an error. The sealing is meant to prevent map
-      functions from modifying the same document that is passed to other map
-      functions. However, only map functions in the same design document are
-      run together, so we have a reasonable expectation they can trust each
-      other. Any map fun that can't be trusted can be placed in its own
-      design document, and it cannot affect other map functions.
-
-      recursivelySeal(doc); // seal to prevent map functions from changing doc
-      */
       var buf = [];
       for (var i = 0; i < State.funs.length; i++) {
         map_results = [];

Modified: couchdb/trunk/share/www/script/test/view_sandboxing.js
URL: 
http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_sandboxing.js?rev=1038193&r1=1038192&r2=1038193&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/view_sandboxing.js (original)
+++ couchdb/trunk/share/www/script/test/view_sandboxing.js Tue Nov 23 16:29:30 
2010
@@ -49,4 +49,92 @@ couchTests.view_sandboxing = function(de
   // the view server
   var results = db.query(function(doc) { map_results.push(1); emit(null, doc); 
});
   T(results.total_rows == 0);
+
+  // test for COUCHDB-925
+  // altering 'doc' variable in map function affects other map functions
+  var ddoc = {
+    _id: "_design/foobar",
+    language: "javascript",
+    views: {
+      view1: {
+        map:
+          (function(doc) {
+            if (doc.values) {
+              doc.values = [666];
+            }
+            if (doc.tags) {
+              doc.tags.push("qwerty");
+            }
+            if (doc.tokens) {
+              doc.tokens["c"] = 3;
+            }
+          }).toString()
+      },
+      view2: {
+        map:
+          (function(doc) {
+            if (doc.values) {
+              emit(doc._id, doc.values);
+            }
+            if (doc.tags) {
+              emit(doc._id, doc.tags);
+            }
+            if (doc.tokens) {
+              emit(doc._id, doc.tokens);
+            }
+          }).toString()
+      }
+    }
+  };
+  var doc1 = {
+    _id: "doc1",
+    values: [1, 2, 3]
+  };
+  var doc2 = {
+    _id: "doc2",
+    tags: ["foo", "bar"],
+    tokens: {a: 1, b: 2}
+  };
+
+  db.deleteDb();
+  db.createDb();
+  T(db.save(ddoc).ok);
+  T(db.save(doc1).ok);
+  T(db.save(doc2).ok);
+
+  var view1Results = db.view(
+    "foobar/view1", {bypass_cache: Math.round(Math.random() * 1000)});
+  var view2Results = db.view(
+    "foobar/view2", {bypass_cache: Math.round(Math.random() * 1000)});
+
+  TEquals(0, view1Results.rows.length, "view1 has 0 rows");
+  TEquals(3, view2Results.rows.length, "view2 has 3 rows");
+
+  TEquals(doc1._id, view2Results.rows[0].key);
+  TEquals(doc2._id, view2Results.rows[1].key);
+  TEquals(doc2._id, view2Results.rows[2].key);
+
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=449657
+  TEquals(3, view2Results.rows[0].value.length,
+    "Warning: installed SpiderMonkey version doesn't allow sealing of arrays");
+  if (view2Results.rows[0].value.length === 3) {
+    TEquals(1, view2Results.rows[0].value[0]);
+    TEquals(2, view2Results.rows[0].value[1]);
+    TEquals(3, view2Results.rows[0].value[2]);
+  }
+
+  TEquals(1, view2Results.rows[1].value["a"]);
+  TEquals(2, view2Results.rows[1].value["b"]);
+  TEquals('undefined', typeof view2Results.rows[1].value["c"],
+    "doc2.tokens object was not sealed");
+
+  TEquals(2, view2Results.rows[2].value.length,
+    "Warning: installed SpiderMonkey version doesn't allow sealing of arrays");
+  if (view2Results.rows[2].value.length === 2) {
+    TEquals("foo", view2Results.rows[2].value[0]);
+    TEquals("bar", view2Results.rows[2].value[1]);
+  }
+
+  // cleanup
+  db.deleteDb();
 };


Reply via email to