You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2026/06/19 15:30:05 UTC
(couchdb) branch fix-revs-diff-function-claause created (now 9fcf73bd1)
This is an automated email from the ASF dual-hosted git repository.
nickva pushed a change to branch fix-revs-diff-function-claause
in repository https://gitbox.apache.org/repos/asf/couchdb.git
at 9fcf73bd1 Fix invalid revisions function_clause
This branch includes the following new commits:
new 9fcf73bd1 Fix invalid revisions function_clause
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(couchdb) 01/01: Fix invalid revisions function_clause
Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
nickva pushed a commit to branch fix-revs-diff-function-claause
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 9fcf73bd119c60d8176e2f583dc3f39f319a2051
Author: Nick Vatamaniuc <va...@gmail.com>
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) ->