This is an automated email from the ASF dual-hosted git repository.
nickva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/couchdb.git
The following commit(s) were added to refs/heads/main by this push:
new 164aabae9 Fix invalid revisions function_clause
164aabae9 is described below
commit 164aabae9daa936b0d9e130180a01f6d3e51792f
Author: Nick Vatamaniuc <[email protected]>
AuthorDate: Fri Jun 19 11:19:24 2026 -0400
Fix invalid revisions function_clause
Previously, invalid revisions for `_missing_revs`, `_revs_diff` and likely
`_purge` could trigger a function clause [1]. Instead since we're letting
user
provided values in there we should throw a proper "bad request" exception.
[1]
```
POST /db_foo/_missing_revs
{
"test_doc_001": ["1-ad7c8ee69b8215bd0a0062732e9b2f59"],
"_deleted": true,
"new_edits": true
}
HTTP/2 500 Internal Server Error
{"error":"unknown_error","reason":"function_clause","ref":4294547805}
```
---
src/chttpd/test/eunit/chttpd_revs_diff_tests.erl | 30 +++++++++++++++++++++++-
src/fabric/src/fabric.erl | 8 +++++--
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/src/chttpd/test/eunit/chttpd_revs_diff_tests.erl
b/src/chttpd/test/eunit/chttpd_revs_diff_tests.erl
index 910c541cc..6998228aa 100644
--- a/src/chttpd/test/eunit/chttpd_revs_diff_tests.erl
+++ b/src/chttpd/test/eunit/chttpd_revs_diff_tests.erl
@@ -72,11 +72,15 @@ chttpd_revs_diff_test_() ->
?TDEF_FE(t_revs_diff_non_existent_doc),
?TDEF_FE(t_revs_diff_all_revs),
?TDEF_FE(t_revs_diff_some_missing_some_not),
+ ?TDEF_FE(t_revs_diff_invalid_non_list_revs),
+ ?TDEF_FE(t_revs_diff_invalid_rev_in_list),
?TDEF_FE(t_empty_missing_revs),
?TDEF_FE(t_missing_revs_no_revs),
?TDEF_FE(t_missing_revs_non_existent_doc),
?TDEF_FE(t_missing_revs_all_revs),
- ?TDEF_FE(t_missing_revs_some_missing_some_not)
+ ?TDEF_FE(t_missing_revs_some_missing_some_not),
+ ?TDEF_FE(t_missing_revs_invalid_non_list_revs),
+ ?TDEF_FE(t_missing_revs_invalid_rev_in_list)
]
}
}
@@ -140,6 +144,18 @@ t_revs_diff_some_missing_some_not({Top, Db}) ->
#{<<"possible_ancestors">> := PAs1} = Doc1,
?assertEqual([<<"2-revb">>, <<"2-revc">>], lists:sort(PAs1)).
+t_revs_diff_invalid_non_list_revs({Top, Db}) ->
+ Body = #{?DOC1 => true},
+ {Code, Res} = req(post, Top ++ Db ++ "/_revs_diff", Body),
+ ?assertEqual(400, Code),
+ ?assertMatch(#{<<"error">> := <<"bad_request">>}, Res).
+
+t_revs_diff_invalid_rev_in_list({Top, Db}) ->
+ Body = #{?DOC1 => [true]},
+ {Code, Res} = req(post, Top ++ Db ++ "/_revs_diff", Body),
+ ?assertEqual(400, Code),
+ ?assertMatch(#{<<"error">> := <<"bad_request">>}, Res).
+
t_empty_missing_revs({Top, Db}) ->
{Code, Res} = req(post, Top ++ Db ++ "/_missing_revs", #{}),
?assertEqual(200, Code),
@@ -190,6 +206,18 @@ t_missing_revs_some_missing_some_not({Top, Db}) ->
Res
).
+t_missing_revs_invalid_non_list_revs({Top, Db}) ->
+ Body = #{?DOC1 => true},
+ {Code, Res} = req(post, Top ++ Db ++ "/_missing_revs", Body),
+ ?assertEqual(400, Code),
+ ?assertMatch(#{<<"error">> := <<"bad_request">>}, Res).
+
+t_missing_revs_invalid_rev_in_list({Top, Db}) ->
+ Body = #{?DOC1 => [true]},
+ {Code, Res} = req(post, Top ++ Db ++ "/_missing_revs", Body),
+ ?assertEqual(400, Code),
+ ?assertMatch(#{<<"error">> := <<"bad_request">>}, Res).
+
create_db(Top, Db) ->
case req(put, Top ++ Db) of
{201, #{}} ->
diff --git a/src/fabric/src/fabric.erl b/src/fabric/src/fabric.erl
index 47895374f..ab575f135 100644
--- a/src/fabric/src/fabric.erl
+++ b/src/fabric/src/fabric.erl
@@ -722,12 +722,16 @@ design_doc(GroupName) ->
<<"_design/", GroupName/binary>>.
idrevs({Id, Revs}) when is_list(Revs) ->
- {docid(Id), [rev(R) || R <- Revs]}.
+ {docid(Id), [rev(R) || R <- Revs]};
+idrevs({_Id, _Revs}) ->
+ throw({bad_request, <<"Invalid revisions, must be a list">>}).
rev(Rev) when is_list(Rev); is_binary(Rev) ->
couch_doc:parse_rev(Rev);
rev({Seq, Hash} = Rev) when is_integer(Seq), is_binary(Hash) ->
- Rev.
+ Rev;
+rev(_BadRev) ->
+ throw({bad_request, <<"Invalid rev format">>}).
%% @doc convenience method, useful when testing or calling fabric from the
shell
opts(Options) ->