@yuneta/gobj-js 7.26.24 → 7.26.25

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.
@@ -873,12 +873,48 @@
873
873
  function is_surrogate_pair(high, low) {
874
874
  return high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343;
875
875
  }
876
+ var JSON_MAX_DEPTH = 2048;
876
877
  function json_text_refused_by_c(s) {
877
878
  const n = s.length;
878
879
  let i = 0;
880
+ let depth = 0;
881
+ const in_object = [];
882
+ let key_next = false;
879
883
  while (i < n) {
880
884
  const c = s.charCodeAt(i);
885
+ if (c === 123 || c === 91) {
886
+ depth++;
887
+ if (depth > JSON_MAX_DEPTH) return true;
888
+ in_object.push(c === 123);
889
+ key_next = c === 123;
890
+ i++;
891
+ continue;
892
+ }
893
+ if (c === 125 || c === 93) {
894
+ depth--;
895
+ in_object.pop();
896
+ key_next = false;
897
+ i++;
898
+ continue;
899
+ }
900
+ if (c === 44) {
901
+ key_next = in_object.length > 0 && in_object[in_object.length - 1];
902
+ i++;
903
+ continue;
904
+ }
905
+ if (c === 58) {
906
+ key_next = false;
907
+ i++;
908
+ continue;
909
+ }
910
+ if (c === 116 || c === 102 || c === 110) {
911
+ if (depth + 1 > JSON_MAX_DEPTH) return true;
912
+ i += c === 102 ? 5 : 4;
913
+ continue;
914
+ }
881
915
  if (c === 34) {
916
+ if (!key_next && depth + 1 > JSON_MAX_DEPTH) return true;
917
+ key_next = false;
882
918
  i++;
883
919
  while (i < n) {
884
920
  const d = s.charCodeAt(i);
@@ -911,6 +947,7 @@
911
947
  continue;
912
948
  }
913
949
  if (c === 45 || c >= 48 && c <= 57) {
950
+ if (depth + 1 > JSON_MAX_DEPTH) return true;
914
951
  JSON_NUMBER_TOKEN.lastIndex = i;
915
952
  const m = JSON_NUMBER_TOKEN.exec(s);
916
953
  if (!m) return true;
@@ -949,7 +986,7 @@
949
986
  if (is_array(value)) return "an array";
950
987
  if (is_object(value)) return "an object";
951
988
  if (is_string(value)) return REFUSED_TEXT;
952
- if (typeof value === "number") return Number.isInteger(value) ? "an integer" : "a real";
989
+ if (typeof value === "number") return Number.isInteger(value) && Math.abs(value) <= 0x8000000000000000 ? "an integer" : "a real";
953
990
  if (typeof value === "boolean") return "a boolean";
954
991
  return "null";
955
992
  }
@@ -970,7 +1007,7 @@
970
1007
  value: value ? 1 : 0,
971
1008
  refused: null
972
1009
  };
973
- if (!(value >= -0x8000000000000000 && value < 0x8000000000000000)) return {
1010
+ if (!(value >= -0x8000000000000000 && value <= 0x8000000000000000)) return {
974
1011
  value: void 0,
975
1012
  refused: "a number out of its range"
976
1013
  };
@@ -1131,6 +1168,10 @@
1131
1168
  refused: shape_of_value(value)
1132
1169
  };
1133
1170
  if (DTP_IS_NUMBER(type)) return number_as_type(type, value);
1171
+ if (!is_json_value(value)) return {
1172
+ value: void 0,
1173
+ refused: NO_JSON_TEXT
1174
+ };
1134
1175
  return {
1135
1176
  value,
1136
1177
  refused: null
@@ -1149,8 +1190,34 @@
1149
1190
  data_type_t.DTP_LIST,
1150
1191
  data_type_t.DTP_DICT
1151
1192
  ];
1193
+ var LEAVE_NODE = Symbol("leave");
1194
+ function is_json_value(value) {
1195
+ const on_path = /* @__PURE__ */ new Set();
1196
+ const stack = [value];
1197
+ while (stack.length) {
1198
+ const v = stack.pop();
1199
+ if (v === LEAVE_NODE) {
1200
+ on_path.delete(stack.pop());
1201
+ continue;
1202
+ }
1203
+ if (typeof v === "bigint") return false;
1204
+ if (v === null || typeof v !== "object") continue;
1205
+ if (on_path.has(v)) return false;
1206
+ const array = Array.isArray(v);
1207
+ if (!array) {
1208
+ const proto = Object.getPrototypeOf(v);
1209
+ if (proto !== Object.prototype && proto !== null) return false;
1210
+ }
1211
+ on_path.add(v);
1212
+ stack.push(v, LEAVE_NODE);
1213
+ if (array) for (let i = 0; i < v.length; i++) stack.push(v[i]);
1214
+ else for (const key of Object.keys(v)) stack.push(v[key]);
1215
+ }
1216
+ return true;
1217
+ }
1152
1218
  var NO_JSON_TEXT = "a value with no json text";
1153
1219
  function json_text_of(value) {
1220
+ if (!is_json_value(value)) return;
1154
1221
  try {
1155
1222
  const s = JSON.stringify(value);
1156
1223
  return is_string(s) ? s : void 0;
@@ -4109,6 +4176,35 @@
4109
4176
  }
4110
4177
  return secret_name_check(str, i, to, true, by_segments);
4111
4178
  }
4179
+ function param_name_tail_from(str, from, to) {
4180
+ let bytes = 0;
4181
+ let i = to;
4182
+ while (i > from) {
4183
+ const code = str.charCodeAt(i - 1);
4184
+ let n = 3;
4185
+ let units = 1;
4186
+ if (code < 128) n = 1;
4187
+ else if (code < 2048) n = 2;
4188
+ else if (code >= 56320 && code <= 57343 && i - 2 >= from) {
4189
+ const high = str.charCodeAt(i - 2);
4190
+ if (high >= 55296 && high <= 56319) {
4191
+ n = 4;
4192
+ units = 2;
4193
+ }
4194
+ }
4195
+ if (bytes + n > MASK_MAX_NAME) return -1;
4196
+ if (code === 38 || code === 63 || code === 59) return i;
4197
+ bytes += n;
4198
+ i -= units;
4199
+ }
4200
+ return -1;
4201
+ }
4202
+ function is_param_name_secret(str, from, to, names) {
4203
+ const tail = names === NAMES_NOW ? param_name_tail_from(str, from, to) : -1;
4204
+ if (tail < 0) return is_secret_name_tail(str, from, to, names);
4205
+ if (is_secret_name_tail(str, from, to, NAMES_7_26_9)) return true;
4206
+ return is_secret_name_tail(str, from, to, NAMES_NOW) && secret_name_check(str, tail, to, false, true);
4207
+ }
4112
4208
  /************************************************************
4113
4209
  * Where an unquoted secret value that begins at `p` ends: at
4114
4210
  * the end, at the quote that closes an outer quoted value, or
@@ -4406,7 +4502,7 @@
4406
4502
  }
4407
4503
  const name_from = word;
4408
4504
  word = p + 1;
4409
- const secret = is_secret_name_tail(str, name_from, p, names);
4505
+ const secret = is_param_name_secret(str, name_from, p, names);
4410
4506
  if (secret && names === NAMES_NOW && !not_7_26_9 && !is_secret_name_tail(str, name_from, p, NAMES_7_26_9)) not_7_26_9 = true;
4411
4507
  if (!(secret || value_is_secret && p - name_from === 5 && ascii_case_equal(str, name_from, "value"))) {
4412
4508
  if (judged && query_name_from(str, p) === name_from) judged[p] = JUDGED_NOT_SECRET;
@@ -4593,7 +4689,7 @@
4593
4689
  p++;
4594
4690
  continue;
4595
4691
  }
4596
- if (!is_secret_name_tail(str, word, p, NAMES_NOW)) {
4692
+ if (!is_param_name_secret(str, word, p, NAMES_NOW)) {
4597
4693
  p++;
4598
4694
  word = p;
4599
4695
  continue;
@@ -4622,6 +4718,50 @@
4622
4718
  }
4623
4719
  return false;
4624
4720
  }
4721
+ function mark_inline_rules_of_now(str, marks) {
4722
+ const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
4723
+ const judged = new Uint8Array(str.length);
4724
+ mark_params(str, value_is_secret, READ_NOW, marks, judged);
4725
+ mark_query_params(str, marks, judged);
4726
+ mark_url_passwords(str, marks);
4727
+ mark_shapes(str, marks);
4728
+ mark_labels(str, marks);
4729
+ }
4730
+ var MASK_MAX_DECODINGS = 2;
4731
+ function encoded_ascii_at(str, p) {
4732
+ if (p + 3 > str.length || str.charCodeAt(p) !== 37) return -1;
4733
+ const hi = hex_digit_value(str.charCodeAt(p + 1));
4734
+ const lo = hi >= 0 ? hex_digit_value(str.charCodeAt(p + 2)) : -1;
4735
+ if (lo < 0) return -1;
4736
+ const c = hi * 16 + lo;
4737
+ return c > 0 && c < 128 ? c : -1;
4738
+ }
4739
+ function mark_decoded_view(str, marks, decodings) {
4740
+ if (decodings >= MASK_MAX_DECODINGS) return;
4741
+ const pieces = [];
4742
+ let last = 0;
4743
+ let p = str.indexOf("%");
4744
+ while (p >= 0) {
4745
+ const c = encoded_ascii_at(str, p);
4746
+ if (c >= 0) {
4747
+ pieces.push(str.substring(last, p), String.fromCharCode(c));
4748
+ last = p + 3;
4749
+ p = str.indexOf("%", last);
4750
+ } else p = str.indexOf("%", p + 1);
4751
+ }
4752
+ if (pieces.length === 0) return;
4753
+ pieces.push(str.substring(last));
4754
+ const view = pieces.join("");
4755
+ const view_marks = new Uint8Array(view.length);
4756
+ mark_inline_rules_of_now(view, view_marks);
4757
+ mark_decoded_view(view, view_marks, decodings + 1);
4758
+ let q = 0;
4759
+ for (let i = 0; i < view.length; i++) {
4760
+ const n = encoded_ascii_at(str, q) >= 0 ? 3 : 1;
4761
+ if (view_marks[i]) mark_range(marks, q, q + n);
4762
+ q += n;
4763
+ }
4764
+ }
4625
4765
  function mark_inline_secrets(str, marks) {
4626
4766
  const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
4627
4767
  const value_is_secret_7_26_9 = inline_value_is_secret(str, NAMES_7_26_9);
@@ -4632,6 +4772,7 @@
4632
4772
  mark_url_passwords(str, marks);
4633
4773
  mark_shapes(str, marks);
4634
4774
  mark_labels(str, marks);
4775
+ mark_decoded_view(str, marks, 0);
4635
4776
  }
4636
4777
  /************************************************************
4637
4778
  * A text (a command line) with the secrets that can be told
@@ -4643,13 +4784,14 @@
4643
4784
  * same for a "name: value", its name in quotes or not (a json, a
4644
4785
  * header, a label: "password": x, Authorization: Basic x, bearer:
4645
4786
  * tok); and a JWT or the credential after "Bearer " or "Basic ",
4646
- * wherever they are, whatever names them. Each rule judges the text
4647
- * as it came. Return null when there was nothing to mask. As the C
4648
- * kernel's mask_secrets_inline().
4787
+ * wherever they are, whatever names them; in a URL or a query
4788
+ * %-encoded inside a value, the same read decoded (mark_decoded_view()).
4789
+ * Each rule judges the text as it came. Return null when there was
4790
+ * nothing to mask. As the C kernel's mask_secrets_inline().
4649
4791
  ************************************************************/
4650
4792
  function mask_secrets_inline(str) {
4651
4793
  if (typeof str !== "string") return null;
4652
- if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
4794
+ if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("%") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
4653
4795
  if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
4654
4796
  const marks = new Uint8Array(str.length);
4655
4797
  mark_inline_secrets(str, marks);
@@ -8412,7 +8554,8 @@
8412
8554
  inform_on_close: false,
8413
8555
  websocket: null,
8414
8556
  inside_on_open: false,
8415
- retry_ms: 0
8557
+ retry_ms: 0,
8558
+ retry_ms_before_session: 0
8416
8559
  };
8417
8560
  var __gclass__ = null;
8418
8561
  /******************************
@@ -8463,6 +8606,7 @@
8463
8606
  function mt_start(gobj) {
8464
8607
  let priv = gobj.priv;
8465
8608
  priv.retry_ms = 0;
8609
+ priv.retry_ms_before_session = 0;
8466
8610
  if (gobj_current_state(gobj) !== "ST_DISCONNECTED") {
8467
8611
  gobj_send_event(gobj, "EV_ON_CLOSE", {
8468
8612
  url: priv.url,
@@ -8662,6 +8806,26 @@
8662
8806
  }
8663
8807
  }
8664
8808
  /***************************************************************
8809
+ * Close the channel for a fault of the server: a message for a role
8810
+ * or a yuno that is not this one, an event out of its place.
8811
+ *
8812
+ * Not with 1000: EV_ON_CLOSE would say the session ended well. A
8813
+ * browser closes a WebSocket only with 1000 or 3000-4999 (1002, the
8814
+ * protocol error, throws there), so it is 4002 -- and the server
8815
+ * echoes it, so EV_ON_CLOSE carries it.
8816
+ *
8817
+ * And a session the server broke was not a session reached: the
8818
+ * backoff the ack reset is put back, or a server that breaks every
8819
+ * session is reconnected at the first delay for ever, with an
8820
+ * EV_ON_OPEN and an EV_ON_CLOSE in the app per round.
8821
+ ***************************************************************/
8822
+ var CLOSE_PEER_FAULT = 4002;
8823
+ function close_for_peer_fault(gobj, reason) {
8824
+ let priv = gobj.priv;
8825
+ if (gobj_current_state(gobj) === "ST_SESSION") priv.retry_ms = priv.retry_ms_before_session;
8826
+ close_websocket(gobj, CLOSE_PEER_FAULT, reason);
8827
+ }
8828
+ /***************************************************************
8665
8829
  * A text of the server -- a frame, the name of an event, of a
8666
8830
  * service, of a role -- as a log shows it: masked, then capped on a
8667
8831
  * whole character (capped first, a credential the cap cut would
@@ -8943,7 +9107,8 @@
8943
9107
  *
8944
9108
  * Reset to zero when a session is actually reached
8945
9109
  * (ac_identity_card_ack), so a link that flaps once does not carry a
8946
- * minute-long penalty into its next real drop.
9110
+ * minute-long penalty into its next real drop -- and put back when the
9111
+ * server breaks that session (close_for_peer_fault()).
8947
9112
  ***************************************************************/
8948
9113
  function next_retry(gobj) {
8949
9114
  let priv = gobj.priv;
@@ -8993,6 +9158,7 @@
8993
9158
  } else {
8994
9159
  gobj_change_state(gobj, "ST_SESSION");
8995
9160
  priv.inside_on_open = true;
9161
+ priv.retry_ms_before_session = priv.retry_ms;
8996
9162
  priv.retry_ms = 0;
8997
9163
  if (!priv.inform_on_close) {
8998
9164
  priv.inform_on_close = true;
@@ -9032,7 +9198,7 @@
9032
9198
  if (String(iev_event).toLowerCase() === "ev_identity_card_ack") {
9033
9199
  if (!(gobj_send_event(gobj, "EV_IDENTITY_CARD_ACK", iev_kw, gobj) < 0)) return 0;
9034
9200
  } else log_warning(`${gobj_short_name(gobj)}: event UNKNOWN in not-session state ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
9035
- close_websocket(gobj);
9201
+ close_for_peer_fault(gobj, "event out of session");
9036
9202
  return -1;
9037
9203
  }
9038
9204
  iev_msg = null;
@@ -9041,13 +9207,13 @@
9041
9207
  let iev_dst_role = kw_get_str(gobj, event_id, "dst_role", "");
9042
9208
  if (!empty_string(iev_dst_role) && iev_dst_role.toLowerCase() !== String(gobj_yuno_role()).toLowerCase()) {
9043
9209
  log_warning(`${gobj_short_name(gobj)}: It's not my role, dst_role: ${peer_text_shown(iev_dst_role, PEER_NAME_SHOWN)}, my_role: ${gobj_yuno_role()}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
9044
- close_websocket(gobj);
9210
+ close_for_peer_fault(gobj, "not my role");
9045
9211
  return -1;
9046
9212
  }
9047
9213
  let iev_dst_yuno = kw_get_str(gobj, event_id, "dst_yuno", "");
9048
9214
  if (!empty_string(iev_dst_yuno) && iev_dst_yuno.toLowerCase() !== String(gobj_yuno_name()).toLowerCase()) {
9049
9215
  log_warning(`${gobj_short_name(gobj)}: It's not my name, dst_yuno: ${peer_text_shown(iev_dst_yuno, PEER_NAME_SHOWN)}, my_name: ${gobj_yuno_name()}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
9050
- close_websocket(gobj);
9216
+ close_for_peer_fault(gobj, "not my name");
9051
9217
  return -1;
9052
9218
  }
9053
9219
  let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
@@ -9059,8 +9225,14 @@
9059
9225
  log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${peer_text_shown(iev_dst_service, PEER_NAME_SHOWN)}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)})`);
9060
9226
  return -1;
9061
9227
  }
9062
- if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
9063
- gobj_send_event(gobj, iev_event, iev_kw, gobj);
9228
+ const declared = gobj_find_event_type(iev_event, 0, false);
9229
+ if (declared && gobj_has_event(gobj, declared.event_name, 0)) {
9230
+ const own_event = declared.event_name;
9231
+ if (!gobj_has_event(gobj, own_event, event_flag_t.EVF_PUBLIC_EVENT) || own_event === "EV_IDENTITY_CARD_ACK") {
9232
+ log_warning(`${gobj_short_name(gobj)}: event of the transport itself, or out of its place, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
9233
+ return -1;
9234
+ }
9235
+ gobj_send_event(gobj, own_event, iev_kw, gobj);
9064
9236
  return 0;
9065
9237
  }
9066
9238
  if (!empty_string(iev_dst_service)) {
@@ -9076,9 +9248,9 @@
9076
9248
  gobj_send_event(gobj_service, iev_event, iev_kw, gobj);
9077
9249
  return 0;
9078
9250
  }
9079
- let event_type = gobj_find_event_type(iev_event, 0, false);
9251
+ let event_type = gobj_find_event_type(iev_event, event_flag_t.EVF_PUBLIC_EVENT, false);
9080
9252
  if (!event_type) {
9081
- log_warning(`${gobj_short_name(gobj)}: event that no gclass declares, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
9253
+ log_warning(`${gobj_short_name(gobj)}: event that no gclass declares public, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
9082
9254
  return -1;
9083
9255
  }
9084
9256
  gobj_publish_event(gobj, event_type.event_name, iev_kw);
@@ -9201,7 +9373,7 @@
9201
9373
  __gclass__ = gclass_create(gclass_name, [
9202
9374
  ["EV_ON_MESSAGE", event_flag_t.EVF_OUTPUT_EVENT],
9203
9375
  ["EV_IDENTITY_CARD_ACK", event_flag_t.EVF_PUBLIC_EVENT],
9204
- ["EV_ON_ID_NAK", event_flag_t.EVF_PUBLIC_EVENT | event_flag_t.EVF_OUTPUT_EVENT],
9376
+ ["EV_ON_ID_NAK", event_flag_t.EVF_OUTPUT_EVENT],
9205
9377
  ["EV_MT_STATS", event_flag_t.EVF_PUBLIC_EVENT],
9206
9378
  ["EV_MT_COMMAND", event_flag_t.EVF_PUBLIC_EVENT],
9207
9379
  ["EV_ON_OPEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],