summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuanjo Rodriguez <juanjo@apache.org>2020-07-10 08:30:56 +0200
committerJuanjo Rodriguez <jjrodrig@gmail.com>2020-07-22 14:42:05 +0200
commit909357e993816bd4a441a701ed97a23acaaffa2f (patch)
tree6284355ad005a1171d6d7ab5271c7dda9d2f14ca
parentfc6dbeebb430db3d46cd3b3b7082ec167d46b679 (diff)
downloadcouchdb-909357e993816bd4a441a701ed97a23acaaffa2f.tar.gz
port view_sandboxing.js into elixir
-rw-r--r--test/elixir/README.md2
-rw-r--r--test/elixir/test/view_sandboxing_test.exs191
-rw-r--r--test/javascript/tests/view_sandboxing.js1
3 files changed, 193 insertions, 1 deletions
diff --git a/test/elixir/README.md b/test/elixir/README.md
index cf529438d..7f11d87cf 100644
--- a/test/elixir/README.md
+++ b/test/elixir/README.md
@@ -109,7 +109,7 @@ X means done, - means partially
- [ ] Port view_multi_key_temp.js
- [X] Port view_offsets.js
- [X] Port view_pagination.js
- - [ ] Port view_sandboxing.js
+ - [X] Port view_sandboxing.js
- [X] Port view_update_seq.js
# Using ExUnit to write unit tests
diff --git a/test/elixir/test/view_sandboxing_test.exs b/test/elixir/test/view_sandboxing_test.exs
new file mode 100644
index 000000000..af0928efa
--- /dev/null
+++ b/test/elixir/test/view_sandboxing_test.exs
@@ -0,0 +1,191 @@
+defmodule ViewSandboxingTest do
+ use CouchTestCase
+
+ @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+ @tag :with_db
+ test "attempting to change the document has no effect", context do
+ db_name = context[:db_name]
+
+ {:ok, _} = create_doc(db_name, @document)
+
+ map_fun = """
+ function(doc) {
+ doc.integer = 2;
+ emit(null, doc);
+ }
+ """
+
+ resp = query(db_name, map_fun, nil, %{include_docs: true})
+ rows = resp["rows"]
+ # either we have an error or our doc is unchanged
+ assert resp["total_rows"] == 0 or Enum.at(rows, 0)["doc"]["integer"] == 1
+
+ map_fun = """
+ function(doc) {
+ doc.array[0] = 0;
+ emit(null, doc);
+ }
+ """
+
+ resp = query(db_name, map_fun, nil, %{include_docs: true})
+ row = Enum.at(resp["rows"], 0)
+ # either we have an error or our doc is unchanged
+ assert resp["total_rows"] == 0 or Enum.at(row["doc"]["array"], 0) == 1
+ end
+
+ @tag :with_db
+ test "view cannot invoke interpreter internals", context do
+ db_name = context[:db_name]
+ {:ok, _} = create_doc(db_name, @document)
+
+ map_fun = """
+ function(doc) {
+ gc();
+ emit(null, doc);
+ }
+ """
+
+ # make sure that a view cannot invoke interpreter internals such as the
+ # garbage collector
+ resp = query(db_name, map_fun)
+ assert resp["total_rows"] == 0
+ end
+
+ @tag :with_db
+ test "view cannot access the map_funs and map_results array", context do
+ db_name = context[:db_name]
+ {:ok, _} = create_doc(db_name, @document)
+
+ map_fun = """
+ function(doc) {
+ map_funs.push(1);
+ emit(null, doc);
+ }
+ """
+
+ resp = query(db_name, map_fun)
+ assert resp["total_rows"] == 0
+
+ map_fun = """
+ function(doc) {
+ map_results.push(1);
+ emit(null, doc);
+ }
+ """
+
+ resp = query(db_name, map_fun)
+ assert resp["total_rows"] == 0
+ end
+
+ @tag :with_db
+ test "COUCHDB-925 - altering 'doc' variable in map function affects other map functions",
+ context do
+ db_name = context[:db_name]
+
+ 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;
+ }
+ }
+ """
+ },
+ 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);
+ }
+ }
+ """
+ }
+ }
+ }
+
+ doc1 = %{
+ _id: "doc1",
+ values: [1, 2, 3]
+ }
+
+ doc2 = %{
+ _id: "doc2",
+ tags: ["foo", "bar"],
+ tokens: %{a: 1, b: 2}
+ }
+
+ {:ok, _} = create_doc(db_name, ddoc)
+ {:ok, _} = create_doc(db_name, doc1)
+ {:ok, _} = create_doc(db_name, doc2)
+
+ resp1 = view(db_name, "foobar/view1")
+ resp2 = view(db_name, "foobar/view2")
+
+ assert Enum.empty?(resp1.body["rows"])
+ assert length(resp2.body["rows"]) == 3
+
+ assert doc1[:_id] == Enum.at(resp2.body["rows"], 0)["key"]
+ assert doc2[:_id] == Enum.at(resp2.body["rows"], 1)["key"]
+ assert doc2[:_id] == Enum.at(resp2.body["rows"], 2)["key"]
+
+ assert length(Enum.at(resp2.body["rows"], 0)["value"]) == 3
+
+ row0_values = Enum.at(resp2.body["rows"], 0)["value"]
+
+ assert Enum.at(row0_values, 0) == 1
+ assert Enum.at(row0_values, 1) == 2
+ assert Enum.at(row0_values, 2) == 3
+
+ row1_values = Enum.at(resp2.body["rows"], 1)["value"]
+ row2_values = Enum.at(resp2.body["rows"], 2)["value"]
+
+ # we can't be 100% sure about the order for the same key
+ assert (is_map(row1_values) and row1_values["a"] == 1) or
+ (is_list(row1_values) and Enum.at(row1_values, 0) == "foo")
+
+ assert (is_map(row1_values) and row1_values["b"] == 2) or
+ (is_list(row1_values) and Enum.at(row1_values, 1) == "bar")
+
+ assert (is_map(row2_values) and row2_values["a"] == 1) or
+ (is_list(row2_values) and Enum.at(row2_values, 0) == "foo")
+
+ assert (is_map(row2_values) and row2_values["b"] == 2) or
+ (is_list(row2_values) and Enum.at(row2_values, 1) == "bar")
+
+ assert is_list(row1_values) or !Map.has_key?(row1_values, "c")
+ assert is_list(row2_values) or !Map.has_key?(row2_values, "c")
+ end
+
+ @tag :with_db
+ test "runtime code evaluation can be prevented", context do
+ db_name = context[:db_name]
+ {:ok, _} = create_doc(db_name, @document)
+
+ map_fun = """
+ function(doc) {
+ var glob = emit.constructor('return this')();
+ emit(doc._id, null);
+ }
+ """
+
+ resp = query(db_name, map_fun)
+ assert resp["total_rows"] == 0
+ end
+end
diff --git a/test/javascript/tests/view_sandboxing.js b/test/javascript/tests/view_sandboxing.js
index 1cdd815de..0e5f308a9 100644
--- a/test/javascript/tests/view_sandboxing.js
+++ b/test/javascript/tests/view_sandboxing.js
@@ -10,6 +10,7 @@
// License for the specific language governing permissions and limitations under
// the License.
+couchTests.elixir = true
couchTests.view_sandboxing = function(debug) {
var db_name = get_random_db_name();
var db = new CouchDB(db_name, {"X-Couch-Full-Commit":"false"});