Skip to content

Commit 77e931d

Browse files
Jake ChampionJakeChampion
authored andcommitted
refactor objectstore key validation logic into a single function that put and lookup both make use of
1 parent c197c6f commit 77e931d

File tree

1 file changed

+61
-122
lines changed

1 file changed

+61
-122
lines changed

c-dependencies/js-compute-runtime/builtins/object-store.cpp

Lines changed: 61 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
#include <algorithm>
2+
#include <cstring>
3+
#include <iostream>
4+
#include <optional>
5+
#include <string>
6+
17
// TODO: remove these once the warnings are fixed
28
#pragma clang diagnostic push
39
#pragma clang diagnostic ignored "-Winvalid-offsetof"
@@ -13,6 +19,23 @@
1319
#include "host_call.h"
1420
#include "js-compute-builtins.h"
1521

22+
std::string_view bad_chars{"#?*[]\n\r"};
23+
24+
std::optional<char> find_invalid_character_for_object_store_key(const char *str) {
25+
std::optional<char> res;
26+
27+
std::string_view view{str, strlen(str)};
28+
29+
auto it = std::find_if(view.begin(), view.end(),
30+
[](auto c) { return bad_chars.find(c) != std::string_view::npos; });
31+
32+
if (it != view.end()) {
33+
res = *it;
34+
}
35+
36+
return res;
37+
}
38+
1639
namespace ObjectStoreEntry {
1740
namespace Slots {
1841
enum {
@@ -91,88 +114,66 @@ ObjectStoreHandle object_store_handle(JSObject *obj) {
91114

92115
const unsigned ctor_length = 1;
93116

94-
bool check_receiver(JSContext *cx, JS::HandleValue receiver, const char *method_name);
95-
96-
bool lookup(JSContext *cx, unsigned argc, JS::Value *vp) {
97-
METHOD_HEADER(1)
98-
99-
JS::RootedObject result_promise(cx, JS::NewPromiseObject(cx, nullptr));
100-
if (!result_promise) {
101-
return ReturnPromiseRejectedWithPendingError(cx, args);
102-
}
103-
104-
size_t key_len;
117+
std::optional<char *> parse_and_validate_key(JSContext *cx, JS::HandleValue val, size_t *key_len) {
105118
// Convert the key argument into a String following https://tc39.es/ecma262/#sec-tostring
106-
JS::UniqueChars key = encode(cx, args.get(0), &key_len);
119+
JS::UniqueChars key = encode(cx, val, key_len);
107120
if (!key) {
108-
return ReturnPromiseRejectedWithPendingError(cx, args);
121+
return std::nullopt;
109122
}
110123

111124
// If the converted string has a length of 0 then we throw an Error
112125
// because ObjectStore Keys have to be at-least 1 character.
113-
if (key_len == 0) {
126+
if (*key_len == 0) {
114127
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_EMPTY);
115-
return ReturnPromiseRejectedWithPendingError(cx, args);
128+
return std::nullopt;
116129
}
117130

118131
// If the converted string has a length of more than 1024 then we throw an Error
119132
// because ObjectStore Keys have to be less than 1025 characters.
120-
if (key_len > 1024) {
133+
if (*key_len > 1024) {
121134
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_TOO_LONG);
122-
return ReturnPromiseRejectedWithPendingError(cx, args);
135+
return std::nullopt;
123136
}
124137

125138
char *key_chars = key.get();
126139

127-
if (strchr(key_chars, '#') != NULL) {
128-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
129-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "#");
130-
return ReturnPromiseRejectedWithPendingError(cx, args);
131-
}
132-
if (strchr(key_chars, '?') != NULL) {
133-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
134-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "?");
135-
return ReturnPromiseRejectedWithPendingError(cx, args);
136-
}
137-
if (strchr(key_chars, '*') != NULL) {
138-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
139-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "*");
140-
return ReturnPromiseRejectedWithPendingError(cx, args);
141-
}
142-
if (strchr(key_chars, '[') != NULL) {
143-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
144-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "[");
145-
return ReturnPromiseRejectedWithPendingError(cx, args);
146-
}
147-
if (strchr(key_chars, ']') != NULL) {
140+
if (auto res = find_invalid_character_for_object_store_key(key_chars)) {
148141
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
149-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "]");
150-
return ReturnPromiseRejectedWithPendingError(cx, args);
151-
}
152-
if (strchr(key_chars, '\n') != NULL) {
153-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
154-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "newline");
155-
return ReturnPromiseRejectedWithPendingError(cx, args);
156-
}
157-
if (strchr(key_chars, '\r') != NULL) {
158-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
159-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "carriage return");
160-
return ReturnPromiseRejectedWithPendingError(cx, args);
142+
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, *res);
143+
return std::nullopt;
161144
}
162145
auto acme_challenge = ".well-known/acme-challenge/";
163146
if (strncmp(key_chars, acme_challenge, strlen(acme_challenge)) == 0) {
164147
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_ACME);
165-
return ReturnPromiseRejectedWithPendingError(cx, args);
148+
return std::nullopt;
166149
}
167150

168151
if (strcmp(key_chars, ".") == 0 || strcmp(key_chars, "..") == 0) {
169152
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_RELATIVE);
153+
return std::nullopt;
154+
}
155+
156+
return key_chars;
157+
}
158+
159+
bool check_receiver(JSContext *cx, JS::HandleValue receiver, const char *method_name);
160+
161+
bool lookup(JSContext *cx, unsigned argc, JS::Value *vp) {
162+
METHOD_HEADER(1)
163+
164+
JS::RootedObject result_promise(cx, JS::NewPromiseObject(cx, nullptr));
165+
if (!result_promise) {
170166
return ReturnPromiseRejectedWithPendingError(cx, args);
171167
}
172168

169+
size_t key_len;
170+
std::optional<char *> key_chars = parse_and_validate_key(cx, args.get(0), &key_len);
171+
if (!key_chars) {
172+
return ReturnPromiseRejectedWithPendingError(cx, args);
173+
}
173174
BodyHandle body_handle = {INVALID_HANDLE};
174-
int status =
175-
fastly_object_store_lookup(object_store_handle(self), key_chars, key_len, &body_handle);
175+
int status = fastly_object_store_lookup(object_store_handle(self), key_chars.value(), key_len,
176+
&body_handle);
176177
if (!HANDLE_RESULT(cx, status)) {
177178
return false;
178179
}
@@ -206,74 +207,11 @@ bool put(JSContext *cx, unsigned argc, JS::Value *vp) {
206207
}
207208

208209
size_t key_len;
209-
// Convert the key argument into a String following https://tc39.es/ecma262/#sec-tostring
210-
JS::UniqueChars key = encode(cx, args.get(0), &key_len);
211-
if (!key) {
212-
return ReturnPromiseRejectedWithPendingError(cx, args);
213-
}
214-
215-
// If the converted string has a length of 0 then we throw an Error
216-
// because ObjectStore Keys have to be at-least 1 character.
217-
if (key_len == 0) {
218-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_EMPTY);
219-
return ReturnPromiseRejectedWithPendingError(cx, args);
220-
}
221-
222-
// If the converted string has a length of more than 1024 then we throw an Error
223-
// because ObjectStore Keys have to be less than 1025 characters.
224-
if (key_len > 1024) {
225-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_TOO_LONG);
226-
return ReturnPromiseRejectedWithPendingError(cx, args);
227-
}
228-
229-
char *key_chars = key.get();
230-
231-
if (strchr(key_chars, '#') != NULL) {
232-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
233-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "#");
234-
return ReturnPromiseRejectedWithPendingError(cx, args);
235-
}
236-
if (strchr(key_chars, '?') != NULL) {
237-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
238-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "?");
239-
return ReturnPromiseRejectedWithPendingError(cx, args);
240-
}
241-
if (strchr(key_chars, '*') != NULL) {
242-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
243-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "*");
244-
return ReturnPromiseRejectedWithPendingError(cx, args);
245-
}
246-
if (strchr(key_chars, '[') != NULL) {
247-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
248-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "[");
249-
return ReturnPromiseRejectedWithPendingError(cx, args);
250-
}
251-
if (strchr(key_chars, ']') != NULL) {
252-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
253-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "]");
254-
return ReturnPromiseRejectedWithPendingError(cx, args);
255-
}
256-
if (strchr(key_chars, '\n') != NULL) {
257-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
258-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "newline");
259-
return ReturnPromiseRejectedWithPendingError(cx, args);
260-
}
261-
if (strchr(key_chars, '\r') != NULL) {
262-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
263-
JSMSG_OBJECT_STORE_KEY_INVALID_CHARACTER, "carriage return");
264-
return ReturnPromiseRejectedWithPendingError(cx, args);
265-
}
266-
auto acme_challenge = ".well-known/acme-challenge/";
267-
if (strncmp(key_chars, acme_challenge, strlen(acme_challenge)) == 0) {
268-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_ACME);
269-
return ReturnPromiseRejectedWithPendingError(cx, args);
270-
}
271-
272-
if (strcmp(key_chars, ".") == 0 || strcmp(key_chars, "..") == 0) {
273-
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_STORE_KEY_RELATIVE);
210+
size_t key_len;
211+
std::optional<char *> key_chars = parse_and_validate_key(cx, args.get(0), &key_len);
212+
if (!key_chars) {
274213
return ReturnPromiseRejectedWithPendingError(cx, args);
275214
}
276-
277215
JS::HandleValue body_val = args.get(1);
278216

279217
// We currently support five types of body inputs:
@@ -302,7 +240,8 @@ bool put(JSContext *cx, unsigned argc, JS::Value *vp) {
302240
JS::RootedObject source_owner(cx, builtins::NativeStreamSource::owner(stream_source));
303241
BodyHandle body = RequestOrResponse::body_handle(source_owner);
304242

305-
int status = fastly_object_store_insert(object_store_handle(self), key_chars, key_len, body);
243+
int status =
244+
fastly_object_store_insert(object_store_handle(self), key_chars.value(), key_len, body);
306245
if (!HANDLE_RESULT(cx, status)) {
307246
return ReturnPromiseRejectedWithPendingError(cx, args);
308247
}
@@ -376,8 +315,8 @@ bool put(JSContext *cx, unsigned argc, JS::Value *vp) {
376315
return ReturnPromiseRejectedWithPendingError(cx, args);
377316
}
378317

379-
int status =
380-
fastly_object_store_insert(object_store_handle(self), key_chars, key_len, body_handle);
318+
int status = fastly_object_store_insert(object_store_handle(self), key_chars.value(), key_len,
319+
body_handle);
381320
// Ensure that we throw an exception for all unexpected host errors.
382321
if (!HANDLE_RESULT(cx, status)) {
383322
return RejectPromiseWithPendingError(cx, result_promise);

0 commit comments

Comments
 (0)