@yuneta/gobj-js 7.26.19 → 7.26.21

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.
package/README.md CHANGED
@@ -669,13 +669,36 @@ container at once) instead of guessing.
669
669
 
670
670
  ```javascript
671
671
  is_secret_name("password") // → true ("token", "jwt", "apikey"...)
672
+ is_secret_name("sim_pin") // → true (pass, pin, otp: whole segments)
673
+ is_secret_name("mapping") // → false (pin inside a word)
672
674
  is_secret_name("token_endpoint") // → false (names something ABOUT one)
673
675
  mask_secrets_inline("login user=a passw=x") // → "login user=a passw=********"
676
+ mask_secrets_inline("curl -H 'Authorization: Bearer abc' u")
677
+ // → "curl -H 'Authorization: ********' u"
678
+ mask_secrets_inline("sent Bearer mF_9.B5f-4.1JqM") // → "sent Bearer ********"
679
+ mask_secrets_inline("the bearer of this letter") // → null: prose
680
+ mask_secrets_inline("\"password\": hunter2") // → "\"password\": ********"
674
681
  json_mask_secrets({user: "a", password: "x"}) // → {user: "a", password: "********"}
682
+ json_mask_secrets({note: "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4In0.c2ln"})
683
+ // → {note: "********"}: a JWT under any key
675
684
  trace_json_masked(kw, "sent") // trace_json() of the masked copy
676
685
  ```
677
686
 
678
- As the C kernel's: linear and bounded (a text over 4 MB is not shown at all).
687
+ As the C kernel's: the same names, the same rules. By name: the value of a
688
+ `name=value` and of a `name: value` (`"password": x`, `Authorization: Basic x`,
689
+ `bearer: tok`, `{\"password\":\"x\"}` of a json inside a json) whose name is
690
+ a secret's, whole. By value: a JWT or a JWE (`eyJ`, base64url and a `.`: also
691
+ one a cap cut), and after `Bearer` or `Basic` the word that can be a credential
692
+ -- quoted, after the scheme doubled, or not a word of prose (a digit, a symbol,
693
+ a character that is no ASCII, its case mixed, 24 characters or more; for
694
+ `Bearer` 8 at least, for `Basic` a run of base64 that decodes to a
695
+ `user:password`). Each rule judges the text as it came, and a run of
696
+ characters to mask is one `********`: nothing that the masking of 7.26.9 hid is
697
+ shown, but the word after a bare `Bearer` that cannot be a credential. A key
698
+ is judged as a name, never as a value: a key that is itself a credential (a
699
+ dict keyed by tokens, `{"eyJ...": 1}`) is shown as it is. A NUL in a string is
700
+ read as a blank; linear and bounded (a text over 4 MB is not shown at all).
701
+ `key` and `cert` are not secrets' names.
679
702
 
680
703
  ### Logging
681
704
 
@@ -795,6 +818,11 @@ gobj_command(yuno, "set-global-no-trace", {level: "timer_periodic", set: 0}, yun
795
818
  // no_trace_levels.__global_no_trace__ = [] -- stays off after a reload
796
819
  ```
797
820
 
821
+ `set` is read as the C kernel reads it: a boolean, an integer, or text --
822
+ `set`/`reset`, a JSON integer (`"1"`, `"-1"`, `"0"`), `true`/`false`,
823
+ `yes`/`no`, `on`/`off` in any case. Anything else answers `-1` *"bitmask set
824
+ or re-set?"*: a real (`2.5`), `" 1"`, `"0x1"`, `"1e3"`, `"+5"`, `"007"`.
825
+
798
826
  The traffic of the websocket is `C_IEVENT_CLI`'s own level `ievents`; its lines
799
827
  go to the yuno attr `trace_ievent_callback`, or to the console.
800
828
 
@@ -3436,6 +3436,11 @@ var SECRET_NAME_JOINED = [
3436
3436
  "sessionkey",
3437
3437
  "authdata"
3438
3438
  ];
3439
+ var SECRET_NAME_SEGMENTS = /* @__PURE__ */ new Set([
3440
+ "pass",
3441
+ "pin",
3442
+ "otp"
3443
+ ]);
3439
3444
  var NOT_SECRET_SEGMENTS = /* @__PURE__ */ new Set([
3440
3445
  "endpoint",
3441
3446
  "url",
@@ -3468,10 +3473,16 @@ function is_secret_name(name) {
3468
3473
  if (typeof name !== "string" || name.length < 3) return false;
3469
3474
  let lower = name.toLowerCase();
3470
3475
  let hit = false;
3471
- for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
3476
+ for (const segment of lower.split(/[^a-z0-9]+/)) if (SECRET_NAME_SEGMENTS.has(segment)) {
3472
3477
  hit = true;
3473
3478
  break;
3474
3479
  }
3480
+ if (!hit) {
3481
+ for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
3482
+ hit = true;
3483
+ break;
3484
+ }
3485
+ }
3475
3486
  if (!hit) {
3476
3487
  let joined = lower.replace(/[_\-. \t]/g, "");
3477
3488
  for (const part of SECRET_NAME_JOINED) if (joined.indexOf(part) >= 0) {
@@ -3509,32 +3520,250 @@ function inline_secret_value_end(str, p, outer) {
3509
3520
  }
3510
3521
  }
3511
3522
  /************************************************************
3512
- * A text (a command line) with the value of every "name=value"
3513
- * whose name is a secret's written as "********", quoted or not
3514
- * (and the "value" of a write-attr whose attribute names a
3515
- * secret). The rest as it is. Return null when there was nothing
3516
- * to mask. As the C kernel's mask_secrets_inline().
3523
+ * A credential by its SHAPE, whatever names it -- the C kernel's
3524
+ * jwt_len() / scheme_credential(), the shapes the agent's audit
3525
+ * record redacts, and the schemes of an HTTP Authorization:
3526
+ * - a JWT or a JWE: "eyJ" (the base64url of '{"') at the start
3527
+ * of a word, base64url, and a '.' after it -- three parts
3528
+ * joined by '.' (a JWS), five (a JWE), or fewer when a cap cut
3529
+ * it; the dots at the end of the run are not its own (a JWT
3530
+ * ends a sentence: "... eyJ.eyJ.sig.");
3531
+ * - the credential after the scheme word "Bearer" or "Basic"
3532
+ * (any case, a word of its own): scheme_credential().
3517
3533
  ************************************************************/
3534
+ var MASK_TOKEN_MIN = 8;
3535
+ var MASK_PROSE_MAX = 24;
3536
+ function is_ascii_alnum_code(code) {
3537
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
3538
+ }
3539
+ function is_ascii_letter_code(code) {
3540
+ return code >= 65 && code <= 90 || code >= 97 && code <= 122;
3541
+ }
3542
+ function is_ascii_upper_code(code) {
3543
+ return code >= 65 && code <= 90;
3544
+ }
3545
+ function is_base64url_code(code) {
3546
+ return is_ascii_alnum_code(code) || code === 45 || code === 95;
3547
+ }
3548
+ function ascii_case_equal(str, p, word) {
3549
+ if (str.length - p < word.length) return false;
3550
+ for (let i = 0; i < word.length; i++) {
3551
+ let code = str.charCodeAt(p + i);
3552
+ if (code >= 65 && code <= 90) code += 32;
3553
+ if (code !== word.charCodeAt(i)) return false;
3554
+ }
3555
+ return true;
3556
+ }
3557
+ function jwt_len(str, p) {
3558
+ if (str.length - p < 3 || str[p] !== "e" || str[p + 1] !== "y" || str[p + 2] !== "J") return 0;
3559
+ if (p > 0 && (is_base64url_code(str.charCodeAt(p - 1)) || str[p - 1] === ".")) return 0;
3560
+ let e = p;
3561
+ let dotted = false;
3562
+ while (e < str.length && (is_base64url_code(str.charCodeAt(e)) || str[e] === ".")) {
3563
+ if (str[e] === ".") dotted = true;
3564
+ e++;
3565
+ }
3566
+ if (!dotted) return 0;
3567
+ while (str[e - 1] === ".") e--;
3568
+ return e - p;
3569
+ }
3570
+ function scheme_word_len(str, p) {
3571
+ if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return 0;
3572
+ for (const scheme of ["bearer", "basic"]) {
3573
+ const n = scheme.length;
3574
+ if (str.length - p > n && ascii_case_equal(str, p, scheme) && (str[p + n] === " " || str[p + n] === " ")) return n;
3575
+ }
3576
+ return 0;
3577
+ }
3578
+ function code_points(str, w, n) {
3579
+ let count = 0;
3580
+ for (let i = w; i < w + n; i++) {
3581
+ const code = str.charCodeAt(i);
3582
+ if (code < 56320 || code > 57343) count++;
3583
+ }
3584
+ return count;
3585
+ }
3586
+ function is_prose_word(str, w, n) {
3587
+ if (n === 0 || n >= MASK_PROSE_MAX) return false;
3588
+ let i = w;
3589
+ const end = w + n;
3590
+ while (true) {
3591
+ let r = i;
3592
+ let upper_rest = true;
3593
+ let lower_rest = true;
3594
+ while (r < end && is_ascii_letter_code(str.charCodeAt(r))) {
3595
+ if (r > i) {
3596
+ if (is_ascii_upper_code(str.charCodeAt(r))) lower_rest = false;
3597
+ else upper_rest = false;
3598
+ }
3599
+ r++;
3600
+ }
3601
+ if (r === i) return false;
3602
+ if (!lower_rest && !(upper_rest && is_ascii_upper_code(str.charCodeAt(i)))) return false;
3603
+ if (r === end) return true;
3604
+ if (str[r] !== "-" && str[r] !== "." && str[r] !== "/" || r + 1 === end) return false;
3605
+ i = r + 1;
3606
+ }
3607
+ }
3608
+ function base64_digit(code) {
3609
+ if (code >= 65 && code <= 90) return code - 65;
3610
+ if (code >= 97 && code <= 122) return code - 97 + 26;
3611
+ if (code >= 48 && code <= 57) return code - 48 + 52;
3612
+ if (code === 43) return 62;
3613
+ if (code === 47) return 63;
3614
+ return -1;
3615
+ }
3616
+ function is_basic_credential(str, w, n) {
3617
+ let bits = 0;
3618
+ let n_bits = 0;
3619
+ for (let i = 0; i < n; i++) {
3620
+ const v = base64_digit(str.charCodeAt(w + i));
3621
+ if (v < 0) return false;
3622
+ bits = (bits << 6 | v) & 16383;
3623
+ n_bits += 6;
3624
+ if (n_bits >= 8) {
3625
+ n_bits -= 8;
3626
+ if ((bits >> n_bits & 255) === 58) return true;
3627
+ }
3628
+ }
3629
+ return false;
3630
+ }
3631
+ var SCHEME_WORD_STOPS = " \r\n\"',;}]\0";
3632
+ function scheme_credential(str, p) {
3633
+ const n = scheme_word_len(str, p);
3634
+ if (!n) return [0, 0];
3635
+ const scheme = n === 5 ? "basic" : "bearer";
3636
+ let context = false;
3637
+ let t = p + n;
3638
+ while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
3639
+ if (t < str.length && scheme_word_len(str, t) === n && ascii_case_equal(str, t, scheme)) {
3640
+ t += n;
3641
+ context = true;
3642
+ while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
3643
+ }
3644
+ if (t >= str.length) return [0, 0];
3645
+ if (str[t] === "\"" || str[t] === "'") {
3646
+ let q = t + 1;
3647
+ while (q < str.length && str[q] !== str[t] && str[q] !== " " && str[q] !== " " && str[q] !== "\r" && str[q] !== "\n" && str[q] !== "\0") q++;
3648
+ if (q < str.length && str[q] === str[t] && q > t + 1) return [t - p, q + 1 - t];
3649
+ return [0, 0];
3650
+ }
3651
+ let e = t;
3652
+ while (e < str.length && SCHEME_WORD_STOPS.indexOf(str[e]) < 0) e++;
3653
+ if (e === t) return [0, 0];
3654
+ if (!context) {
3655
+ let w_end = e;
3656
+ while (w_end > t && str[w_end - 1] === ".") w_end--;
3657
+ const len = w_end - t;
3658
+ if (len === 0 || is_prose_word(str, t, len)) return [0, 0];
3659
+ if (n === 5) {
3660
+ if (!is_basic_credential(str, t, len)) return [0, 0];
3661
+ } else if (code_points(str, t, len) < MASK_TOKEN_MIN) return [0, 0];
3662
+ }
3663
+ return [t - p, e - t];
3664
+ }
3665
+ function shape_len(str, p) {
3666
+ const c = str[p];
3667
+ if (c === "e") return [0, jwt_len(str, p)];
3668
+ if (c === "b" || c === "B") return scheme_credential(str, p);
3669
+ return [0, 0];
3670
+ }
3671
+ function shape_len_7_26_9(str, p) {
3672
+ const c = str[p];
3673
+ if (c === "e") {
3674
+ if (str.length - p < 3 || str[p + 1] !== "y" || str[p + 2] !== "J") return [0, 0];
3675
+ if (p > 0 && (is_base64url_code(str.charCodeAt(p - 1)) || str[p - 1] === ".")) return [0, 0];
3676
+ let e = p;
3677
+ let dots = 0;
3678
+ while (e < str.length && (is_base64url_code(str.charCodeAt(e)) || str[e] === ".")) {
3679
+ if (str[e] === ".") dots++;
3680
+ e++;
3681
+ }
3682
+ while (str[e - 1] === ".") {
3683
+ e--;
3684
+ dots--;
3685
+ }
3686
+ return dots === 2 ? [0, e - p] : [0, 0];
3687
+ }
3688
+ if (c === "b" || c === "B") {
3689
+ if (str.length - p <= 6 || !ascii_case_equal(str, p, "bearer")) return [0, 0];
3690
+ if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return [0, 0];
3691
+ let t = p + 6;
3692
+ if (str[t] !== " " && str[t] !== " ") return [0, 0];
3693
+ while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
3694
+ let e = t;
3695
+ while (e < str.length && " \r\n'\",;}]\0".indexOf(str[e]) < 0) e++;
3696
+ return [t - p, e - t];
3697
+ }
3698
+ return [0, 0];
3699
+ }
3700
+ var COLON_VALUE_STOPS = " \r\n,;&)]}<>\"'\0";
3701
+ function colon_value_end(str, v) {
3702
+ if (v >= str.length) return v;
3703
+ const c = str[v];
3704
+ if (c === "\"" || c === "'") {
3705
+ let e = v + 1;
3706
+ while (e < str.length && str[e] !== c) {
3707
+ if (str[e] === "\\" && e + 1 < str.length) e++;
3708
+ e++;
3709
+ }
3710
+ return e < str.length ? e + 1 : str.length;
3711
+ }
3712
+ if (c === "{" || c === "[") {
3713
+ let depth = 0;
3714
+ let in_string = false;
3715
+ for (let e = v; e < str.length; e++) if (in_string) {
3716
+ if (str[e] === "\\" && e + 1 < str.length) e++;
3717
+ else if (str[e] === "\"") in_string = false;
3718
+ } else if (str[e] === "\"") in_string = true;
3719
+ else if (str[e] === "{" || str[e] === "[") depth++;
3720
+ else if (str[e] === "}" || str[e] === "]") {
3721
+ depth--;
3722
+ if (depth === 0) return e + 1;
3723
+ }
3724
+ return str.length;
3725
+ }
3726
+ if (c === "\\") {
3727
+ let q = v;
3728
+ while (q < str.length && str[q] === "\\") q++;
3729
+ if (q < str.length && (str[q] === "\"" || str[q] === "'")) {
3730
+ const level = q - v;
3731
+ let run = 0;
3732
+ for (let e = q + 1; e < str.length; e++) {
3733
+ if (str[e] === "\\") {
3734
+ run++;
3735
+ continue;
3736
+ }
3737
+ if (str[e] === str[q] && run === level) return e + 1;
3738
+ run = 0;
3739
+ }
3740
+ return str.length;
3741
+ }
3742
+ }
3743
+ let e = v;
3744
+ const n = scheme_word_len(str, v);
3745
+ if (n) {
3746
+ e = v + n;
3747
+ while (e < str.length && (str[e] === " " || str[e] === " ")) e++;
3748
+ if (e < str.length && (str[e] === "\"" || str[e] === "'")) return colon_value_end(str, e);
3749
+ }
3750
+ while (e < str.length && COLON_VALUE_STOPS.indexOf(str[e]) < 0) e++;
3751
+ return e;
3752
+ }
3518
3753
  var MASK_MAX_NAME = 128;
3519
3754
  var MASK_MAX_BYTES = 4194304;
3520
3755
  var MASK_TOO_LARGE = "<not shown: too large to mask>";
3521
- function mask_secrets_inline(str) {
3522
- if (typeof str !== "string" || str.indexOf("=") < 0) return null;
3523
- if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3524
- let value_is_secret = false;
3525
- let re_attr = /(^|[ \t])attribute=["']?([^ \t"']*)/g;
3526
- let m;
3527
- while ((m = re_attr.exec(str)) !== null) if (is_secret_name(m[2].slice(-128))) value_is_secret = true;
3528
- let parts = [];
3529
- let copied = 0;
3530
- let changed = false;
3756
+ function mark_range(marks, from, to) {
3757
+ for (let i = from; i < to; i++) marks[i] = 1;
3758
+ }
3759
+ function mark_params(str, value_is_secret, as_7_26_9, marks) {
3760
+ const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " " || str[i] === "\0";
3531
3761
  let outer = "";
3532
3762
  let word = 0;
3533
3763
  let p = 0;
3534
- const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " ";
3535
3764
  while (p < str.length) {
3536
- let c = str[p];
3537
- if (c === " " || c === " ") {
3765
+ const c = str[p];
3766
+ if (c === " " || c === " " || c === "\0") {
3538
3767
  p++;
3539
3768
  word = p;
3540
3769
  continue;
@@ -3543,43 +3772,124 @@ function mask_secrets_inline(str) {
3543
3772
  if (!outer && (p === 0 || str[p - 1] === " " || str[p - 1] === " " || str[p - 1] === "=")) outer = c;
3544
3773
  else if (c === outer && is_blank_or_end(p + 1)) outer = "";
3545
3774
  }
3775
+ if (as_7_26_9) {
3776
+ const [offset, n] = shape_len_7_26_9(str, p);
3777
+ if (n) {
3778
+ p += offset + n;
3779
+ word = p;
3780
+ continue;
3781
+ }
3782
+ }
3546
3783
  if (c !== "=") {
3547
3784
  p++;
3548
3785
  continue;
3549
3786
  }
3550
- let name = str.substring(Math.max(word, p - MASK_MAX_NAME), p);
3787
+ const name_from = Math.max(word, p - MASK_MAX_NAME);
3788
+ const name = str.substring(name_from, p);
3551
3789
  word = p + 1;
3552
- if (!(is_secret_name(name) || value_is_secret && name.toLowerCase() === "value")) {
3790
+ if (!(is_secret_name(name) || value_is_secret && name.length === 5 && ascii_case_equal(str, name_from, "value"))) {
3553
3791
  p++;
3554
3792
  continue;
3555
3793
  }
3556
3794
  p++;
3557
- let quote = str[p] === "\"" || str[p] === "'" ? str[p] : "";
3558
3795
  let end;
3796
+ const quote = p < str.length && (str[p] === "\"" || str[p] === "'") ? str[p] : "";
3559
3797
  if (quote) {
3560
3798
  end = p + 1;
3561
- while (end < str.length && str[end] !== quote) end++;
3562
- if (str[end] === quote) end++;
3799
+ while (end < str.length && str[end] !== quote && str[end] !== "\0") end++;
3800
+ if (end < str.length && str[end] === quote) end++;
3563
3801
  } else end = inline_secret_value_end(str, p, outer);
3564
- if (end > p) {
3565
- parts.push(str.substring(copied, p));
3802
+ mark_range(marks, p, end);
3803
+ p = end;
3804
+ word = p;
3805
+ }
3806
+ }
3807
+ function mark_shapes(str, marks) {
3808
+ for (let p = 0; p < str.length; p++) {
3809
+ const c = str[p];
3810
+ if (c !== "e" && c !== "b" && c !== "B") continue;
3811
+ const [offset, n] = shape_len(str, p);
3812
+ if (n) mark_range(marks, p + offset, p + offset + n);
3813
+ }
3814
+ }
3815
+ function mark_labels(str, marks) {
3816
+ let word = 0;
3817
+ let p = 0;
3818
+ while (p < str.length) {
3819
+ const c = str[p];
3820
+ if (c === " " || c === " " || c === "\0" || c === "=") {
3821
+ p++;
3822
+ word = p;
3823
+ continue;
3824
+ }
3825
+ if (c !== ":") {
3826
+ p++;
3827
+ continue;
3828
+ }
3829
+ if (!is_secret_name(str.substring(Math.max(word, p - MASK_MAX_NAME), p))) {
3830
+ p++;
3831
+ word = p;
3832
+ continue;
3833
+ }
3834
+ let v = p + 1;
3835
+ while (v < str.length && (str[v] === " " || str[v] === " ")) v++;
3836
+ const e = colon_value_end(str, v);
3837
+ mark_range(marks, v, e);
3838
+ const n_scheme = scheme_word_len(str, v);
3839
+ if (n_scheme) p = v + n_scheme;
3840
+ else p = e > v ? e : p + 1;
3841
+ word = p;
3842
+ }
3843
+ }
3844
+ /************************************************************
3845
+ * A text (a command line) with the secrets that can be told
3846
+ * written as "********" (each run of them as one), the rest as it
3847
+ * is: the value of every "name=value" whose name is a secret's,
3848
+ * quoted or not (and the "value" of a write-attr whose attribute
3849
+ * names a secret); the same for a "name: value", its name in quotes
3850
+ * or not (a json, a header, a label: "password": x, Authorization:
3851
+ * Basic x, bearer: tok); and a JWT or the credential after "Bearer "
3852
+ * or "Basic ", wherever they are, whatever names them. Each rule
3853
+ * judges the text as it came. Return null when there was nothing to
3854
+ * mask. As the C kernel's mask_secrets_inline().
3855
+ ************************************************************/
3856
+ function mask_secrets_inline(str) {
3857
+ if (typeof str !== "string") return null;
3858
+ if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
3859
+ if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3860
+ let value_is_secret = false;
3861
+ let re_attr = /(^|[ \t])attribute=["']?([^ \t"'\u0000]*)/g;
3862
+ let m;
3863
+ while ((m = re_attr.exec(str)) !== null) if (is_secret_name(m[2].slice(-128))) value_is_secret = true;
3864
+ const marks = new Uint8Array(str.length);
3865
+ mark_params(str, value_is_secret, true, marks);
3866
+ mark_params(str, value_is_secret, false, marks);
3867
+ mark_shapes(str, marks);
3868
+ mark_labels(str, marks);
3869
+ let parts = [];
3870
+ let changed = false;
3871
+ let i = 0;
3872
+ while (i < str.length) {
3873
+ let j = i;
3874
+ if (marks[i]) {
3875
+ while (j < str.length && marks[j]) j++;
3566
3876
  parts.push("********");
3567
- copied = end;
3568
3877
  changed = true;
3878
+ } else {
3879
+ while (j < str.length && !marks[j]) j++;
3880
+ parts.push(str.substring(i, j));
3569
3881
  }
3570
- p = end;
3571
- word = p;
3882
+ i = j;
3572
3883
  }
3573
- if (!changed) return null;
3574
- parts.push(str.substring(copied));
3575
- return parts.join("");
3884
+ return changed ? parts.join("") : null;
3576
3885
  }
3577
3886
  /************************************************************
3578
3887
  * A json as a log or a trace may show it: at any depth, the
3579
3888
  * value of a key with a secret's name is "********", whatever
3580
3889
  * its type (not an absent one, a null or an empty string); so is
3581
3890
  * the "value" of a dict whose "attribute" names a secret; and a
3582
- * string is masked as mask_secrets_inline(). Return a masked
3891
+ * string, under any key, is masked as mask_secrets_inline() (a
3892
+ * JWT, a "Bearer" token, a secret name=value). Return a masked
3583
3893
  * copy, or `jn` itself when there was nothing to mask. As the C
3584
3894
  * kernel's json_mask_secrets().
3585
3895
  *
@@ -3608,7 +3918,7 @@ function _json_mask_secrets(jn, memo, depth, budget) {
3608
3918
  return MASK_TOO_LARGE;
3609
3919
  }
3610
3920
  budget.left -= jn.length;
3611
- let masked = mask_secrets_inline(jn);
3921
+ let masked = mask_secrets_inline(jn.indexOf("\0") >= 0 ? jn.replace(/\u0000/g, " ") : jn);
3612
3922
  return masked !== null ? masked : jn;
3613
3923
  }
3614
3924
  let is_array = Array.isArray(jn);
@@ -7110,15 +7420,34 @@ function save_trace_scope(gobj, attr, scope, levels) {
7110
7420
  return gobj_save_persistent_attrs(gobj, attr);
7111
7421
  }
7112
7422
  /***************************************************************
7113
- * "set" as the C command reads it: TRUE / set / 1, FALSE / reset / 0
7423
+ * "set" as the C command reads it (trace_set_value() of c_yuno.c):
7424
+ * a boolean; an integer in the range of an int (true unless 0); or
7425
+ * text -- set / reset, a JSON integer -?(0|[1-9][0-9]*), or true /
7426
+ * false, yes / no, on / off, any case. null for anything else: a
7427
+ * real (2.5), " 1", "0x1", "1e3", "+5", "007" and "2.5" are refused,
7428
+ * as in C. Up to gobj-js 7.26.19 the text went through Number(),
7429
+ * which took all of them, and "yes" / "on" were refused.
7114
7430
  ***************************************************************/
7431
+ var INT_MIN = -2147483648;
7432
+ var INT_MAX = 2147483647;
7115
7433
  function parse_set(value) {
7116
7434
  if (value === true || value === false) return value;
7117
- let v = String(value === void 0 || value === null ? "" : value).toLowerCase();
7118
- if (v === "true" || v === "set") return true;
7119
- if (v === "false" || v === "reset") return false;
7120
- if (v === "" || isNaN(Number(v))) return null;
7121
- return Number(v) ? true : false;
7435
+ if (typeof value === "number") {
7436
+ if (!Number.isInteger(value) || value < INT_MIN || value > INT_MAX) return null;
7437
+ return value !== 0;
7438
+ }
7439
+ if (typeof value !== "string" || value === "") return null;
7440
+ let v = value.toLowerCase();
7441
+ if (v === "set") return true;
7442
+ if (v === "reset") return false;
7443
+ if (/^-?(0|[1-9][0-9]*)$/.test(value)) {
7444
+ let n = Number(value);
7445
+ if (n < INT_MIN || n > INT_MAX) return null;
7446
+ return n !== 0;
7447
+ }
7448
+ if (v === "true" || v === "yes" || v === "on") return true;
7449
+ if (v === "false" || v === "no" || v === "off") return false;
7450
+ return null;
7122
7451
  }
7123
7452
  function yuno_prefix() {
7124
7453
  return `${gobj_yuno_role()}^${gobj_yuno_name()}`;