@yuneta/gobj-js 7.26.19 → 7.26.20

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.
@@ -3439,6 +3439,11 @@
3439
3439
  "sessionkey",
3440
3440
  "authdata"
3441
3441
  ];
3442
+ var SECRET_NAME_SEGMENTS = /* @__PURE__ */ new Set([
3443
+ "pass",
3444
+ "pin",
3445
+ "otp"
3446
+ ]);
3442
3447
  var NOT_SECRET_SEGMENTS = /* @__PURE__ */ new Set([
3443
3448
  "endpoint",
3444
3449
  "url",
@@ -3471,10 +3476,16 @@
3471
3476
  if (typeof name !== "string" || name.length < 3) return false;
3472
3477
  let lower = name.toLowerCase();
3473
3478
  let hit = false;
3474
- for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
3479
+ for (const segment of lower.split(/[^a-z0-9]+/)) if (SECRET_NAME_SEGMENTS.has(segment)) {
3475
3480
  hit = true;
3476
3481
  break;
3477
3482
  }
3483
+ if (!hit) {
3484
+ for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
3485
+ hit = true;
3486
+ break;
3487
+ }
3488
+ }
3478
3489
  if (!hit) {
3479
3490
  let joined = lower.replace(/[_\-. \t]/g, "");
3480
3491
  for (const part of SECRET_NAME_JOINED) if (joined.indexOf(part) >= 0) {
@@ -3512,17 +3523,63 @@
3512
3523
  }
3513
3524
  }
3514
3525
  /************************************************************
3526
+ * A credential by its SHAPE, whatever names it -- the C kernel's
3527
+ * jwt_len() / bearer_token_len(), the shapes the agent's audit
3528
+ * record redacts:
3529
+ * - a JWT: "eyJ" (the base64url of '{"') at the start of a
3530
+ * word, and three parts of base64url joined by '.'; the dots
3531
+ * at the end of the run are not its own (a JWT ends a
3532
+ * sentence: "... eyJ.eyJ.sig.");
3533
+ * - the token after "Bearer " (any case, a word of its own).
3534
+ ************************************************************/
3535
+ function is_ascii_alnum_code(code) {
3536
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
3537
+ }
3538
+ function is_base64url_code(code) {
3539
+ return is_ascii_alnum_code(code) || code === 45 || code === 95;
3540
+ }
3541
+ function jwt_len(str, p) {
3542
+ if (str.length - p < 3 || str[p] !== "e" || str[p + 1] !== "y" || str[p + 2] !== "J") return 0;
3543
+ if (p > 0 && (is_base64url_code(str.charCodeAt(p - 1)) || str[p - 1] === ".")) return 0;
3544
+ let e = p;
3545
+ let dots = 0;
3546
+ while (e < str.length && (is_base64url_code(str.charCodeAt(e)) || str[e] === ".")) {
3547
+ if (str[e] === ".") dots++;
3548
+ e++;
3549
+ }
3550
+ while (str[e - 1] === ".") {
3551
+ e--;
3552
+ dots--;
3553
+ }
3554
+ return dots === 2 ? e - p : 0;
3555
+ }
3556
+ var BEARER_STOPS = " \r\n'\",;}]\0";
3557
+ function bearer_token(str, p) {
3558
+ const word_len = 6;
3559
+ if (str.length - p <= word_len || str.slice(p, p + word_len).toLowerCase() !== "bearer") return [0, 0];
3560
+ if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return [0, 0];
3561
+ let t = p + word_len;
3562
+ if (str[t] !== " " && str[t] !== " ") return [0, 0];
3563
+ while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
3564
+ let e = t;
3565
+ while (e < str.length && BEARER_STOPS.indexOf(str[e]) < 0) e++;
3566
+ return [t - p, e - t];
3567
+ }
3568
+ /************************************************************
3515
3569
  * A text (a command line) with the value of every "name=value"
3516
3570
  * whose name is a secret's written as "********", quoted or not
3517
3571
  * (and the "value" of a write-attr whose attribute names a
3518
- * secret). The rest as it is. Return null when there was nothing
3519
- * to mask. As the C kernel's mask_secrets_inline().
3572
+ * secret); and a JWT or the token after "Bearer ", wherever they
3573
+ * are, whatever names them. The rest as it is. Return null when
3574
+ * there was nothing to mask. As the C kernel's
3575
+ * mask_secrets_inline().
3520
3576
  ************************************************************/
3521
3577
  var MASK_MAX_NAME = 128;
3522
3578
  var MASK_MAX_BYTES = 4194304;
3523
3579
  var MASK_TOO_LARGE = "<not shown: too large to mask>";
3524
3580
  function mask_secrets_inline(str) {
3525
- if (typeof str !== "string" || str.indexOf("=") < 0) return null;
3581
+ if (typeof str !== "string") return null;
3582
+ if (str.indexOf("=") < 0 && str.indexOf("eyJ") < 0 && !/bearer/i.test(str)) return null;
3526
3583
  if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3527
3584
  let value_is_secret = false;
3528
3585
  let re_attr = /(^|[ \t])attribute=["']?([^ \t"']*)/g;
@@ -3546,6 +3603,18 @@
3546
3603
  if (!outer && (p === 0 || str[p - 1] === " " || str[p - 1] === " " || str[p - 1] === "=")) outer = c;
3547
3604
  else if (c === outer && is_blank_or_end(p + 1)) outer = "";
3548
3605
  }
3606
+ let shape = c === "e" ? jwt_len(str, p) : 0;
3607
+ let token_offset = 0;
3608
+ if (c === "b" || c === "B") [token_offset, shape] = bearer_token(str, p);
3609
+ if (shape) {
3610
+ parts.push(str.substring(copied, p + token_offset));
3611
+ parts.push("********");
3612
+ p += token_offset + shape;
3613
+ copied = p;
3614
+ changed = true;
3615
+ word = p;
3616
+ continue;
3617
+ }
3549
3618
  if (c !== "=") {
3550
3619
  p++;
3551
3620
  continue;
@@ -3582,7 +3651,8 @@
3582
3651
  * value of a key with a secret's name is "********", whatever
3583
3652
  * its type (not an absent one, a null or an empty string); so is
3584
3653
  * the "value" of a dict whose "attribute" names a secret; and a
3585
- * string is masked as mask_secrets_inline(). Return a masked
3654
+ * string, under any key, is masked as mask_secrets_inline() (a
3655
+ * JWT, a "Bearer" token, a secret name=value). Return a masked
3586
3656
  * copy, or `jn` itself when there was nothing to mask. As the C
3587
3657
  * kernel's json_mask_secrets().
3588
3658
  *
@@ -3611,7 +3681,7 @@
3611
3681
  return MASK_TOO_LARGE;
3612
3682
  }
3613
3683
  budget.left -= jn.length;
3614
- let masked = mask_secrets_inline(jn);
3684
+ let masked = mask_secrets_inline(jn.indexOf("\0") >= 0 ? jn.replace(/\u0000/g, " ") : jn);
3615
3685
  return masked !== null ? masked : jn;
3616
3686
  }
3617
3687
  let is_array = Array.isArray(jn);
@@ -7113,15 +7183,34 @@
7113
7183
  return gobj_save_persistent_attrs(gobj, attr);
7114
7184
  }
7115
7185
  /***************************************************************
7116
- * "set" as the C command reads it: TRUE / set / 1, FALSE / reset / 0
7186
+ * "set" as the C command reads it (trace_set_value() of c_yuno.c):
7187
+ * a boolean; an integer in the range of an int (true unless 0); or
7188
+ * text -- set / reset, a JSON integer -?(0|[1-9][0-9]*), or true /
7189
+ * false, yes / no, on / off, any case. null for anything else: a
7190
+ * real (2.5), " 1", "0x1", "1e3", "+5", "007" and "2.5" are refused,
7191
+ * as in C. Up to gobj-js 7.26.19 the text went through Number(),
7192
+ * which took all of them, and "yes" / "on" were refused.
7117
7193
  ***************************************************************/
7194
+ var INT_MIN = -2147483648;
7195
+ var INT_MAX = 2147483647;
7118
7196
  function parse_set(value) {
7119
7197
  if (value === true || value === false) return value;
7120
- let v = String(value === void 0 || value === null ? "" : value).toLowerCase();
7121
- if (v === "true" || v === "set") return true;
7122
- if (v === "false" || v === "reset") return false;
7123
- if (v === "" || isNaN(Number(v))) return null;
7124
- return Number(v) ? true : false;
7198
+ if (typeof value === "number") {
7199
+ if (!Number.isInteger(value) || value < INT_MIN || value > INT_MAX) return null;
7200
+ return value !== 0;
7201
+ }
7202
+ if (typeof value !== "string" || value === "") return null;
7203
+ let v = value.toLowerCase();
7204
+ if (v === "set") return true;
7205
+ if (v === "reset") return false;
7206
+ if (/^-?(0|[1-9][0-9]*)$/.test(value)) {
7207
+ let n = Number(value);
7208
+ if (n < INT_MIN || n > INT_MAX) return null;
7209
+ return n !== 0;
7210
+ }
7211
+ if (v === "true" || v === "yes" || v === "on") return true;
7212
+ if (v === "false" || v === "no" || v === "off") return false;
7213
+ return null;
7125
7214
  }
7126
7215
  function yuno_prefix() {
7127
7216
  return `${gobj_yuno_role()}^${gobj_yuno_name()}`;