diff options
author | Juanjo Rodriguez <juanjo@apache.org> | 2020-07-10 08:30:56 +0200 |
---|---|---|
committer | Juanjo Rodriguez <jjrodrig@gmail.com> | 2020-07-22 14:42:05 +0200 |
commit | 909357e993816bd4a441a701ed97a23acaaffa2f (patch) | |
tree | 6284355ad005a1171d6d7ab5271c7dda9d2f14ca | |
parent | fc6dbeebb430db3d46cd3b3b7082ec167d46b679 (diff) | |
download | couchdb-909357e993816bd4a441a701ed97a23acaaffa2f.tar.gz |
port view_sandboxing.js into elixir
-rw-r--r-- | test/elixir/README.md | 2 | ||||
-rw-r--r-- | test/elixir/test/view_sandboxing_test.exs | 191 | ||||
-rw-r--r-- | test/javascript/tests/view_sandboxing.js | 1 |
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"}); |