mini_racer 0.17.0.pre9 → 0.17.0.pre11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: