mini_racer 0.17.0.pre9 → 0.17.0.pre11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3be215eb4d4e72b01480ffa28863ea7ff1947b3f8cf0b6dcd860ffde2e48799
4
- data.tar.gz: adcae327e54c2c372871c255e4bfd19a07b28964cd4c7c51cbc1cc251d1f71db
3
+ metadata.gz: 6851fdd7226dd520cf865af8b60e64b3456a0b0cfd31a22d4f275495c8f42aef
4
+ data.tar.gz: ffd5db728de9571a2b7e7d83348a95b658809d36f869050338c1f5b0a814146b
5
5
  SHA512:
6
- metadata.gz: 98bdc234f17c0e16ae995c8fe3566f6ec80cbd92c569e4aa31ac1999d309ae2a197b0c7cb1c5de62f542f25b4f73287d0a34ab7967e1e24c32d02fc73ade33f2
7
- data.tar.gz: 7ade32c683f240c3e19eaa58b6a044192f57b1e651966c042654d401f1f5dae1aff20d1c545dd7fd05afb2b0f70659080825b5a788ab82379b119c497ac31e47
6
+ metadata.gz: 76e7edac2249f6aa850bd03f1a7d4c353095f4e3f0a89466c83763f945de44ac68881b57d5b784ac7b6391dd467812a7d27eeeba98ba13624f60dbe4e1db70ab
7
+ data.tar.gz: 9519ee4b2601ab14a0a1898518bdcda5f318b66b4b4631d7881c2e83ea656c461cbf3fa38682d8702234e01d1cad7c2358125c9b4689333db686ca7207b27fc0
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ - 0.17.0.pre11 - 21-01-2025
2
+ - Corrected encoding bug with deserialization of strings - Ben Noordhuis
3
+
4
+ - 0.17.0.pre10 - 20-01-2025
5
+ - Added back support for partially deserialized objects (objects that do not translate across boundaries are returned as Error properties) - Ben Noordhuis
6
+
1
7
  - 0.17.0.pre9 - 13-01-2025
2
8
  - For backwards compatibility convert v8 return values to UTF-8 (invalidly encoded string still get returned using V8 encoding)
3
9
 
@@ -171,13 +171,14 @@ static void *rendezvous_callback(void *arg);
171
171
  typedef struct State
172
172
  {
173
173
  VALUE a, b;
174
+ uint8_t verbatim_keys:1;
174
175
  } State;
175
176
 
176
177
  // note: must be stack-allocated or VALUEs won't be visible to ruby's GC
177
178
  typedef struct DesCtx
178
179
  {
179
180
  State *tos;
180
- VALUE refs; // array
181
+ VALUE refs; // object refs array
181
182
  uint8_t transcode_latin1:1;
182
183
  char err[64];
183
184
  State stack[512];
@@ -199,7 +200,7 @@ static void DesCtx_init(DesCtx *c)
199
200
  {
200
201
  c->tos = c->stack;
201
202
  c->refs = rb_ary_new();
202
- *c->tos = (State){Qundef, Qundef};
203
+ *c->tos = (State){Qundef, Qundef, /*verbatim_keys*/0};
203
204
  *c->err = '\0';
204
205
  c->transcode_latin1 = 1; // convert to utf8
205
206
  }
@@ -220,7 +221,8 @@ static void put(DesCtx *c, VALUE v)
220
221
  if (*b == Qundef) {
221
222
  *b = v;
222
223
  } else {
223
- *b = rb_funcall(*b, rb_intern("to_s"), 0);
224
+ if (!c->tos->verbatim_keys)
225
+ *b = rb_funcall(*b, rb_intern("to_s"), 0);
224
226
  rb_hash_aset(*a, *b, v);
225
227
  *b = Qundef;
226
228
  }
@@ -242,7 +244,7 @@ static void push(DesCtx *c, VALUE v)
242
244
  snprintf(c->err, sizeof(c->err), "stack overflow");
243
245
  return;
244
246
  }
245
- *++c->tos = (State){v, Qundef};
247
+ *++c->tos = (State){v, Qundef, /*verbatim_keys*/0};
246
248
  rb_ary_push(c->refs, v);
247
249
  }
248
250
 
@@ -337,13 +339,24 @@ static VALUE str_encode_bang(VALUE v)
337
339
 
338
340
  static void des_string8(void *arg, const uint8_t *s, size_t n)
339
341
  {
342
+ rb_encoding *e;
340
343
  DesCtx *c;
341
344
  VALUE v;
342
345
 
343
346
  c = arg;
344
- v = rb_enc_str_new((char *)s, n, rb_ascii8bit_encoding());
345
- if (c->transcode_latin1)
347
+ if (*c->err)
348
+ return;
349
+ if (c->transcode_latin1) {
350
+ e = rb_enc_find("ISO-8859-1"); // TODO cache?
351
+ if (!e) {
352
+ snprintf(c->err, sizeof(c->err), "no ISO-8859-1 encoding");
353
+ return;
354
+ }
355
+ v = rb_enc_str_new((char *)s, n, e);
346
356
  v = str_encode_bang(v); // cannot fail
357
+ } else {
358
+ v = rb_enc_str_new((char *)s, n, rb_ascii8bit_encoding());
359
+ }
347
360
  put(c, v);
348
361
  }
349
362
 
@@ -426,6 +439,20 @@ static void des_object_end(void *arg)
426
439
  pop(arg);
427
440
  }
428
441
 
442
+ static void des_map_begin(void *arg)
443
+ {
444
+ DesCtx *c;
445
+
446
+ c = arg;
447
+ push(c, rb_hash_new());
448
+ c->tos->verbatim_keys = 1; // don't stringify or intern keys
449
+ }
450
+
451
+ static void des_map_end(void *arg)
452
+ {
453
+ pop(arg);
454
+ }
455
+
429
456
  static void des_object_ref(void *arg, uint32_t id)
430
457
  {
431
458
  DesCtx *c;
@@ -11,6 +11,56 @@
11
11
  #include <cstring>
12
12
  #include <vector>
13
13
 
14
+ // note: the filter function gets called inside the safe context,
15
+ // i.e., the context that has not been tampered with by user JS
16
+ // convention: $-prefixed identifiers signify objects from the
17
+ // user JS context and should be handled with special care
18
+ static const char safe_context_script_source[] = R"js(
19
+ ;(function($globalThis) {
20
+ const {Map: $Map, Set: $Set} = $globalThis
21
+ const sentinel = {}
22
+ return function filter(v) {
23
+ if (typeof v === "function")
24
+ return sentinel
25
+ if (typeof v !== "object" || v === null)
26
+ return v
27
+ if (v instanceof $Map) {
28
+ const m = new Map()
29
+ for (let [k, t] of Map.prototype.entries.call(v)) {
30
+ t = filter(t)
31
+ if (t !== sentinel)
32
+ m.set(k, t)
33
+ }
34
+ return m
35
+ } else if (v instanceof $Set) {
36
+ const s = new Set()
37
+ for (let t of Set.prototype.values.call(v)) {
38
+ t = filter(t)
39
+ if (t !== sentinel)
40
+ s.add(t)
41
+ }
42
+ return s
43
+ } else {
44
+ const o = Array.isArray(v) ? [] : {}
45
+ const pds = Object.getOwnPropertyDescriptors(v)
46
+ for (const [k, d] of Object.entries(pds)) {
47
+ if (!d.enumerable)
48
+ continue
49
+ let t = d.value
50
+ if (d.get) {
51
+ // *not* d.get.call(...), may have been tampered with
52
+ t = Function.prototype.call.call(d.get, v, k)
53
+ }
54
+ t = filter(t)
55
+ if (t !== sentinel)
56
+ Object.defineProperty(o, k, {value: t, enumerable: true})
57
+ }
58
+ return o
59
+ }
60
+ }
61
+ })
62
+ )js";
63
+
14
64
  struct Callback
15
65
  {
16
66
  struct State *st;
@@ -32,8 +82,10 @@ struct State
32
82
  // extra context for when we need access to built-ins like Array
33
83
  // and want to be sure they haven't been tampered with by JS code
34
84
  v8::Local<v8::Context> safe_context;
35
- v8::Persistent<v8::Context> persistent_context; // single-thread mode only
36
- v8::Persistent<v8::Context> persistent_safe_context; // single-thread mode only
85
+ v8::Local<v8::Function> safe_context_function;
86
+ v8::Persistent<v8::Context> persistent_context; // single-thread mode only
87
+ v8::Persistent<v8::Context> persistent_safe_context; // single-thread mode only
88
+ v8::Persistent<v8::Function> persistent_safe_context_function; // single-thread mode only
37
89
  Context *ruby_context;
38
90
  int64_t max_memory;
39
91
  int err_reason;
@@ -73,6 +125,23 @@ struct Serialized
73
125
  // throws JS exception on serialization error
74
126
  bool reply(State& st, v8::Local<v8::Value> v)
75
127
  {
128
+ v8::TryCatch try_catch(st.isolate);
129
+ {
130
+ Serialized serialized(st, v);
131
+ if (serialized.data) {
132
+ v8_reply(st.ruby_context, serialized.data, serialized.size);
133
+ return true;
134
+ }
135
+ }
136
+ if (!try_catch.CanContinue()) {
137
+ try_catch.ReThrow();
138
+ return false;
139
+ }
140
+ auto recv = v8::Undefined(st.isolate);
141
+ if (!st.safe_context_function->Call(st.safe_context, recv, 1, &v).ToLocal(&v)) {
142
+ try_catch.ReThrow();
143
+ return false;
144
+ }
76
145
  Serialized serialized(st, v);
77
146
  if (serialized.data)
78
147
  v8_reply(st.ruby_context, serialized.data, serialized.size);
@@ -240,7 +309,29 @@ extern "C" State *v8_thread_init(Context *c, const uint8_t *snapshot_buf,
240
309
  st.safe_context = v8::Context::New(st.isolate);
241
310
  st.context = v8::Context::New(st.isolate);
242
311
  v8::Context::Scope context_scope(st.context);
312
+ {
313
+ v8::Context::Scope context_scope(st.safe_context);
314
+ auto source = v8::String::NewFromUtf8Literal(st.isolate, safe_context_script_source);
315
+ auto filename = v8::String::NewFromUtf8Literal(st.isolate, "safe_context_script.js");
316
+ v8::ScriptOrigin origin(filename);
317
+ auto script =
318
+ v8::Script::Compile(st.safe_context, source, &origin)
319
+ .ToLocalChecked();
320
+ auto function_v = script->Run(st.safe_context).ToLocalChecked();
321
+ auto function = v8::Function::Cast(*function_v);
322
+ auto recv = v8::Undefined(st.isolate);
323
+ v8::Local<v8::Value> arg = st.context->Global();
324
+ // grant the safe context access to the user context's globalThis
325
+ st.safe_context->SetSecurityToken(st.context->GetSecurityToken());
326
+ function_v =
327
+ function->Call(st.safe_context, recv, 1, &arg)
328
+ .ToLocalChecked();
329
+ // revoke access again now that the script did its one-time setup
330
+ st.safe_context->UseDefaultSecurityToken();
331
+ st.safe_context_function = v8::Local<v8::Function>::Cast(function_v);
332
+ }
243
333
  if (single_threaded) {
334
+ st.persistent_safe_context_function.Reset(st.isolate, st.safe_context_function);
244
335
  st.persistent_safe_context.Reset(st.isolate, st.safe_context);
245
336
  st.persistent_context.Reset(st.isolate, st.context);
246
337
  return pst; // intentionally returning early and keeping alive
@@ -792,12 +883,14 @@ extern "C" void v8_single_threaded_enter(State *pst, Context *c, void (*f)(Conte
792
883
  v8::Isolate::Scope isolate_scope(st.isolate);
793
884
  v8::HandleScope handle_scope(st.isolate);
794
885
  {
886
+ st.safe_context_function = v8::Local<v8::Function>::New(st.isolate, st.persistent_safe_context_function);
795
887
  st.safe_context = v8::Local<v8::Context>::New(st.isolate, st.persistent_safe_context);
796
888
  st.context = v8::Local<v8::Context>::New(st.isolate, st.persistent_context);
797
889
  v8::Context::Scope context_scope(st.context);
798
890
  f(c);
799
891
  st.context = v8::Local<v8::Context>();
800
892
  st.safe_context = v8::Local<v8::Context>();
893
+ st.safe_context_function = v8::Local<v8::Function>();
801
894
  }
802
895
  }
803
896
 
@@ -31,6 +31,8 @@ static void des_named_props_begin(void *arg);
31
31
  static void des_named_props_end(void *arg);
32
32
  static void des_object_begin(void *arg);
33
33
  static void des_object_end(void *arg);
34
+ static void des_map_begin(void *arg);
35
+ static void des_map_end(void *arg);
34
36
  static void des_object_ref(void *arg, uint32_t id);
35
37
  // des_error_begin: followed by des_object_begin + des_object_end calls
36
38
  static void des_error_begin(void *arg);
@@ -642,7 +644,7 @@ again:
642
644
  des_object_end(arg);
643
645
  break;
644
646
  case ';': // Map
645
- des_object_begin(arg);
647
+ des_map_begin(arg);
646
648
  for (u = 0; /*empty*/; u++) {
647
649
  if (*p >= pe)
648
650
  goto too_short;
@@ -658,7 +660,7 @@ again:
658
660
  goto bad_varint;
659
661
  if (t != 2*u)
660
662
  return bail(err, "map element count mismatch");
661
- des_object_end(arg);
663
+ des_map_end(arg);
662
664
  break;
663
665
  case '\'': // Set
664
666
  des_array_begin(arg);
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniRacer
4
- VERSION = "0.17.0.pre9"
4
+ VERSION = "0.17.0.pre11"
5
5
  LIBV8_NODE_VERSION = "~> 22.7.0.4"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_racer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.0.pre9
4
+ version: 0.17.0.pre11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-13 00:00:00.000000000 Z
11
+ date: 2025-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -123,9 +123,9 @@ licenses:
123
123
  - MIT
124
124
  metadata:
125
125
  bug_tracker_uri: https://github.com/discourse/mini_racer/issues
126
- changelog_uri: https://github.com/discourse/mini_racer/blob/v0.17.0.pre9/CHANGELOG
127
- documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.17.0.pre9
128
- source_code_uri: https://github.com/discourse/mini_racer/tree/v0.17.0.pre9
126
+ changelog_uri: https://github.com/discourse/mini_racer/blob/v0.17.0.pre11/CHANGELOG
127
+ documentation_uri: https://www.rubydoc.info/gems/mini_racer/0.17.0.pre11
128
+ source_code_uri: https://github.com/discourse/mini_racer/tree/v0.17.0.pre11
129
129
  post_install_message:
130
130
  rdoc_options: []
131
131
  require_paths: