Skip to content

Commit 1e24ff9

Browse files
author
Christian Hergert
committed
cursor: create synthetic failure upon top-level query and $dollar keys
It is possible to submit a query to mongo-c-driver that looks like: {a:1, $orderby: {foo:1}} This is invalid since it mixes the top-level query and the orderby. It should instead be: {$query: {a:1}, {$orderby:{foo:1}}} This fixes CDRIVER-379.
1 parent 96988c8 commit 1e24ff9

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

src/mongoc/mongoc-cursor.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ _mongoc_cursor_new (mongoc_client_t *client,
183183

184184
#define MARK_FAILED(c) \
185185
do { \
186+
bson_init (&(c)->query); \
187+
bson_init (&(c)->fields); \
186188
(c)->failed = true; \
187189
(c)->done = true; \
188190
(c)->end_of_event = true; \
@@ -236,6 +238,33 @@ _mongoc_cursor_new (mongoc_client_t *client,
236238
}
237239
}
238240

241+
/*
242+
* Check if we have a mixed top-level query and dollar keys such
243+
* as $orderby. This is not allowed (you must use {$query:{}}.
244+
*/
245+
if (bson_iter_init (&iter, query)) {
246+
bool found_dollar = false;
247+
bool found_non_dollar = false;
248+
249+
while (bson_iter_next (&iter)) {
250+
if (bson_iter_key (&iter)[0] == '$') {
251+
found_dollar = true;
252+
} else {
253+
found_non_dollar = true;
254+
}
255+
}
256+
257+
if (found_dollar && found_non_dollar) {
258+
bson_set_error (&cursor->error,
259+
MONGOC_ERROR_CURSOR,
260+
MONGOC_ERROR_CURSOR_INVALID_CURSOR,
261+
"Cannot mix top-level query with dollar keys such "
262+
"as $orderby. Use {$query: {},...} instead.");
263+
MARK_FAILED (cursor);
264+
GOTO (finish);
265+
}
266+
}
267+
239268
if (!cursor->is_command && !bson_has_field (query, "$query")) {
240269
bson_init (&cursor->query);
241270
bson_append_document (&cursor->query, "$query", 6, query);
@@ -730,6 +759,14 @@ mongoc_cursor_next (mongoc_cursor_t *cursor,
730759

731760
TRACE ("cursor_id(%"PRId64")", cursor->rpc.reply.cursor_id);
732761

762+
if (bson) {
763+
*bson = NULL;
764+
}
765+
766+
if (cursor->failed) {
767+
return false;
768+
}
769+
733770
if (cursor->iface.next) {
734771
ret = cursor->iface.next(cursor, bson);
735772
} else {

tests/test-mongoc-cursor.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,49 @@ test_clone (void)
108108
}
109109

110110

111+
static void
112+
test_invalid_query (void)
113+
{
114+
mongoc_client_t *client;
115+
mongoc_cursor_t *cursor;
116+
mongoc_uri_t *uri;
117+
bson_error_t error;
118+
const bson_t *doc = NULL;
119+
bson_t *q;
120+
bool r;
121+
char *uristr;
122+
123+
uristr = bson_strdup_printf("mongodb://%s/", MONGOC_TEST_HOST);
124+
uri = mongoc_uri_new(uristr);
125+
bson_free(uristr);
126+
127+
client = mongoc_client_new_from_uri (uri);
128+
assert (client);
129+
130+
q = BCON_NEW ("foo", BCON_INT32 (1), "$orderby", "{", "}");
131+
132+
doc = q;
133+
134+
cursor = _mongoc_cursor_new (client, "test.test", MONGOC_QUERY_NONE, 0, 1, 1,
135+
false, q, NULL, NULL);
136+
r = mongoc_cursor_next (cursor, &doc);
137+
assert (!r);
138+
mongoc_cursor_error (cursor, &error);
139+
assert (strstr (error.message, "$query"));
140+
assert (error.domain == MONGOC_ERROR_CURSOR);
141+
assert (error.code == MONGOC_ERROR_CURSOR_INVALID_CURSOR);
142+
assert (doc == NULL);
143+
144+
mongoc_cursor_destroy (cursor);
145+
mongoc_client_destroy (client);
146+
mongoc_uri_destroy(uri);
147+
}
148+
149+
111150
void
112151
test_cursor_install (TestSuite *suite)
113152
{
114153
TestSuite_Add (suite, "/Cursor/get_host", test_get_host);
115154
TestSuite_Add (suite, "/Cursor/clone", test_clone);
155+
TestSuite_Add (suite, "/Cursor/invalid_query", test_invalid_query);
116156
}

0 commit comments

Comments
 (0)