This is an automated email from the ASF dual-hosted git repository.

vatamane pushed a commit to branch fix-json-order-false-positive-in-scanner
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 6e09ff49419197d93cc5f807afc58496746f556d
Author: Nick Vatamaniuc <[email protected]>
AuthorDate: Thu Aug 22 19:25:20 2024 -0400

    Fix json order false positives in quickjs scanner plugin
    
    After scanning some production clusters, noticed that QuickJS and SM 91 
seems
    to be using a different json order than SM 1.8.5. A KV proplist ejson prompt
    result could show up as a compatibility issue when the JSON key order is
    different.
    
    To fix it, use maps instead of ejson KVs. Could have just used a
    jiffy:encode/decode pair, but it seemed wasteful to serialize everything 
when
    we just want to transform the proplists so created a small utility function.
---
 src/couch/src/couch_util.erl                                | 10 ++++++++++
 src/couch/test/eunit/couch_util_tests.erl                   | 13 +++++++++++++
 src/couch_quickjs/src/couch_quickjs_scanner_plugin.erl      |  5 ++++-
 .../test/couch_quickjs_scanner_plugin_tests.erl             |  9 ++++++++-
 4 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/src/couch/src/couch_util.erl b/src/couch/src/couch_util.erl
index 18faa9695..ee3c3fc4c 100644
--- a/src/couch/src/couch_util.erl
+++ b/src/couch/src/couch_util.erl
@@ -46,6 +46,7 @@
 -export([verify_hash_names/2]).
 -export([get_config_hash_algorithms/0]).
 -export([remove_sensitive_data/1]).
+-export([ejson_to_map/1]).
 
 -include_lib("couch/include/couch_db.hrl").
 
@@ -852,3 +853,12 @@ remove_sensitive_data(KVList) ->
     KVList1 = lists:keyreplace(<<"password">>, 1, KVList, {<<"password">>, 
<<"****">>}),
     % some KVList entries are atoms, so test fo this too
     lists:keyreplace(password, 1, KVList1, {password, <<"****">>}).
+
+ejson_to_map(#{} = Val) ->
+    maps:map(fun(_, V) -> ejson_to_map(V) end, Val);
+ejson_to_map(Val) when is_list(Val) ->
+    lists:map(fun(V) -> ejson_to_map(V) end, Val);
+ejson_to_map({Val}) when is_list(Val) ->
+    maps:from_list(lists:map(fun({K, V}) -> {K, ejson_to_map(V)} end, Val));
+ejson_to_map(Val) ->
+    Val.
diff --git a/src/couch/test/eunit/couch_util_tests.erl 
b/src/couch/test/eunit/couch_util_tests.erl
index 62ec20775..5f8f1ce43 100644
--- a/src/couch/test/eunit/couch_util_tests.erl
+++ b/src/couch/test/eunit/couch_util_tests.erl
@@ -193,3 +193,16 @@ json_decode_test_() ->
         ?_assertEqual({[]}, couch_util:json_decode(<<"{}">>, [])),
         ?_assertEqual(#{}, couch_util:json_decode(<<"{}">>, [return_maps]))
     ].
+
+ejson_to_map_test() ->
+    ?assertEqual(0, couch_util:ejson_to_map(0)),
+    ?assertEqual(true, couch_util:ejson_to_map(true)),
+    ?assertEqual(0.1, couch_util:ejson_to_map(0.1)),
+    ?assertEqual(#{}, couch_util:ejson_to_map(#{})),
+    ?assertEqual(#{a => b}, couch_util:ejson_to_map(#{a => b})),
+    ?assertEqual([], couch_util:ejson_to_map([])),
+    ?assertEqual([1], couch_util:ejson_to_map([1])),
+    ?assertEqual(#{}, couch_util:ejson_to_map({[]})),
+    ?assertEqual(#{a => 1, b => 2}, couch_util:ejson_to_map({[{b, 2}, {a, 
1}]})),
+    ?assertEqual(#{<<"a">> => [1, #{}]}, couch_util:ejson_to_map({[{<<"a">>, 
[1, {[]}]}]})),
+    ?assertEqual([#{true => 1}], couch_util:ejson_to_map([{[{true, 1}]}])).
diff --git a/src/couch_quickjs/src/couch_quickjs_scanner_plugin.erl 
b/src/couch_quickjs/src/couch_quickjs_scanner_plugin.erl
index ee8a76bcc..8d80388e8 100644
--- a/src/couch_quickjs/src/couch_quickjs_scanner_plugin.erl
+++ b/src/couch_quickjs/src/couch_quickjs_scanner_plugin.erl
@@ -579,8 +579,11 @@ proc(Pid) ->
     }.
 
 prompt(#proc{} = Proc, Prompt) ->
+    % Make sure to transform ejson with key values {[{K, V}, ...]} to
+    % one containing maps to avoid false positives resulting from
+    % differences in json key encoding order
     try
-        couch_query_servers:proc_prompt(Proc, Prompt)
+        couch_util:ejson_to_map(couch_query_servers:proc_prompt(Proc, Prompt))
     catch
         Tag:Err ->
             {error, {Tag, Err}}
diff --git a/src/couch_quickjs/test/couch_quickjs_scanner_plugin_tests.erl 
b/src/couch_quickjs/test/couch_quickjs_scanner_plugin_tests.erl
index e91081f96..ed7d16410 100644
--- a/src/couch_quickjs/test/couch_quickjs_scanner_plugin_tests.erl
+++ b/src/couch_quickjs/test/couch_quickjs_scanner_plugin_tests.erl
@@ -392,6 +392,13 @@ ddoc_view(Doc) ->
             v_expr_fun1 => #{map => <<"(function(doc) {emit(1,2)})\n">>},
             v_expr_fun2 => #{map => <<"(function(doc) {emit(3,4)});">>},
             v_expr_fun3 => #{map => <<"y=9;\n(function(doc) {emit(5,y)})">>},
-            v_expr_fun4 => #{map => <<"x=10; function(doc) {emit(x,6)}\n">>}
+            v_expr_fun4 => #{map => <<"x=10; function(doc) {emit(x,6)}\n">>},
+            v_emit_map1 => #{
+                map => <<
+                    "function(doc){ \n",
+                    "  emit(42, {NaN:1, \"2\": 1}); \n"
+                    "};\n"
+                >>
+            }
         }
     }.

Reply via email to