@yuneta/gobj-js 7.26.20 → 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.
@@ -3520,77 +3520,249 @@ function inline_secret_value_end(str, p, outer) {
3520
3520
  }
3521
3521
  /************************************************************
3522
3522
  * A credential by its SHAPE, whatever names it -- the C kernel's
3523
- * jwt_len() / bearer_token_len(), the shapes the agent's audit
3524
- * record redacts:
3525
- * - a JWT: "eyJ" (the base64url of '{"') at the start of a
3526
- * word, and three parts of base64url joined by '.'; the dots
3527
- * at the end of the run are not its own (a JWT ends a
3528
- * sentence: "... eyJ.eyJ.sig.");
3529
- * - the token after "Bearer " (any case, a word of its own).
3530
- ************************************************************/
3523
+ * jwt_len() / scheme_credential(), the shapes the agent's audit
3524
+ * record redacts, and the schemes of an HTTP Authorization:
3525
+ * - a JWT or a JWE: "eyJ" (the base64url of '{"') at the start
3526
+ * of a word, base64url, and a '.' after it -- three parts
3527
+ * joined by '.' (a JWS), five (a JWE), or fewer when a cap cut
3528
+ * it; the dots at the end of the run are not its own (a JWT
3529
+ * ends a sentence: "... eyJ.eyJ.sig.");
3530
+ * - the credential after the scheme word "Bearer" or "Basic"
3531
+ * (any case, a word of its own): scheme_credential().
3532
+ ************************************************************/
3533
+ var MASK_TOKEN_MIN = 8;
3534
+ var MASK_PROSE_MAX = 24;
3531
3535
  function is_ascii_alnum_code(code) {
3532
3536
  return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
3533
3537
  }
3538
+ function is_ascii_letter_code(code) {
3539
+ return code >= 65 && code <= 90 || code >= 97 && code <= 122;
3540
+ }
3541
+ function is_ascii_upper_code(code) {
3542
+ return code >= 65 && code <= 90;
3543
+ }
3534
3544
  function is_base64url_code(code) {
3535
3545
  return is_ascii_alnum_code(code) || code === 45 || code === 95;
3536
3546
  }
3547
+ function ascii_case_equal(str, p, word) {
3548
+ if (str.length - p < word.length) return false;
3549
+ for (let i = 0; i < word.length; i++) {
3550
+ let code = str.charCodeAt(p + i);
3551
+ if (code >= 65 && code <= 90) code += 32;
3552
+ if (code !== word.charCodeAt(i)) return false;
3553
+ }
3554
+ return true;
3555
+ }
3537
3556
  function jwt_len(str, p) {
3538
3557
  if (str.length - p < 3 || str[p] !== "e" || str[p + 1] !== "y" || str[p + 2] !== "J") return 0;
3539
3558
  if (p > 0 && (is_base64url_code(str.charCodeAt(p - 1)) || str[p - 1] === ".")) return 0;
3540
3559
  let e = p;
3541
- let dots = 0;
3560
+ let dotted = false;
3542
3561
  while (e < str.length && (is_base64url_code(str.charCodeAt(e)) || str[e] === ".")) {
3543
- if (str[e] === ".") dots++;
3562
+ if (str[e] === ".") dotted = true;
3544
3563
  e++;
3545
3564
  }
3546
- while (str[e - 1] === ".") {
3547
- e--;
3548
- dots--;
3565
+ if (!dotted) return 0;
3566
+ while (str[e - 1] === ".") e--;
3567
+ return e - p;
3568
+ }
3569
+ function scheme_word_len(str, p) {
3570
+ if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return 0;
3571
+ for (const scheme of ["bearer", "basic"]) {
3572
+ const n = scheme.length;
3573
+ if (str.length - p > n && ascii_case_equal(str, p, scheme) && (str[p + n] === " " || str[p + n] === " ")) return n;
3574
+ }
3575
+ return 0;
3576
+ }
3577
+ function code_points(str, w, n) {
3578
+ let count = 0;
3579
+ for (let i = w; i < w + n; i++) {
3580
+ const code = str.charCodeAt(i);
3581
+ if (code < 56320 || code > 57343) count++;
3582
+ }
3583
+ return count;
3584
+ }
3585
+ function is_prose_word(str, w, n) {
3586
+ if (n === 0 || n >= MASK_PROSE_MAX) return false;
3587
+ let i = w;
3588
+ const end = w + n;
3589
+ while (true) {
3590
+ let r = i;
3591
+ let upper_rest = true;
3592
+ let lower_rest = true;
3593
+ while (r < end && is_ascii_letter_code(str.charCodeAt(r))) {
3594
+ if (r > i) {
3595
+ if (is_ascii_upper_code(str.charCodeAt(r))) lower_rest = false;
3596
+ else upper_rest = false;
3597
+ }
3598
+ r++;
3599
+ }
3600
+ if (r === i) return false;
3601
+ if (!lower_rest && !(upper_rest && is_ascii_upper_code(str.charCodeAt(i)))) return false;
3602
+ if (r === end) return true;
3603
+ if (str[r] !== "-" && str[r] !== "." && str[r] !== "/" || r + 1 === end) return false;
3604
+ i = r + 1;
3605
+ }
3606
+ }
3607
+ function base64_digit(code) {
3608
+ if (code >= 65 && code <= 90) return code - 65;
3609
+ if (code >= 97 && code <= 122) return code - 97 + 26;
3610
+ if (code >= 48 && code <= 57) return code - 48 + 52;
3611
+ if (code === 43) return 62;
3612
+ if (code === 47) return 63;
3613
+ return -1;
3614
+ }
3615
+ function is_basic_credential(str, w, n) {
3616
+ let bits = 0;
3617
+ let n_bits = 0;
3618
+ for (let i = 0; i < n; i++) {
3619
+ const v = base64_digit(str.charCodeAt(w + i));
3620
+ if (v < 0) return false;
3621
+ bits = (bits << 6 | v) & 16383;
3622
+ n_bits += 6;
3623
+ if (n_bits >= 8) {
3624
+ n_bits -= 8;
3625
+ if ((bits >> n_bits & 255) === 58) return true;
3626
+ }
3549
3627
  }
3550
- return dots === 2 ? e - p : 0;
3628
+ return false;
3551
3629
  }
3552
- var BEARER_STOPS = " \r\n'\",;}]\0";
3553
- function bearer_token(str, p) {
3554
- const word_len = 6;
3555
- if (str.length - p <= word_len || str.slice(p, p + word_len).toLowerCase() !== "bearer") return [0, 0];
3556
- if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return [0, 0];
3557
- let t = p + word_len;
3558
- if (str[t] !== " " && str[t] !== " ") return [0, 0];
3630
+ var SCHEME_WORD_STOPS = " \r\n\"',;}]\0";
3631
+ function scheme_credential(str, p) {
3632
+ const n = scheme_word_len(str, p);
3633
+ if (!n) return [0, 0];
3634
+ const scheme = n === 5 ? "basic" : "bearer";
3635
+ let context = false;
3636
+ let t = p + n;
3559
3637
  while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
3638
+ if (t < str.length && scheme_word_len(str, t) === n && ascii_case_equal(str, t, scheme)) {
3639
+ t += n;
3640
+ context = true;
3641
+ while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
3642
+ }
3643
+ if (t >= str.length) return [0, 0];
3644
+ if (str[t] === "\"" || str[t] === "'") {
3645
+ let q = t + 1;
3646
+ while (q < str.length && str[q] !== str[t] && str[q] !== " " && str[q] !== " " && str[q] !== "\r" && str[q] !== "\n" && str[q] !== "\0") q++;
3647
+ if (q < str.length && str[q] === str[t] && q > t + 1) return [t - p, q + 1 - t];
3648
+ return [0, 0];
3649
+ }
3560
3650
  let e = t;
3561
- while (e < str.length && BEARER_STOPS.indexOf(str[e]) < 0) e++;
3651
+ while (e < str.length && SCHEME_WORD_STOPS.indexOf(str[e]) < 0) e++;
3652
+ if (e === t) return [0, 0];
3653
+ if (!context) {
3654
+ let w_end = e;
3655
+ while (w_end > t && str[w_end - 1] === ".") w_end--;
3656
+ const len = w_end - t;
3657
+ if (len === 0 || is_prose_word(str, t, len)) return [0, 0];
3658
+ if (n === 5) {
3659
+ if (!is_basic_credential(str, t, len)) return [0, 0];
3660
+ } else if (code_points(str, t, len) < MASK_TOKEN_MIN) return [0, 0];
3661
+ }
3562
3662
  return [t - p, e - t];
3563
3663
  }
3564
- /************************************************************
3565
- * A text (a command line) with the value of every "name=value"
3566
- * whose name is a secret's written as "********", quoted or not
3567
- * (and the "value" of a write-attr whose attribute names a
3568
- * secret); and a JWT or the token after "Bearer ", wherever they
3569
- * are, whatever names them. The rest as it is. Return null when
3570
- * there was nothing to mask. As the C kernel's
3571
- * mask_secrets_inline().
3572
- ************************************************************/
3664
+ function shape_len(str, p) {
3665
+ const c = str[p];
3666
+ if (c === "e") return [0, jwt_len(str, p)];
3667
+ if (c === "b" || c === "B") return scheme_credential(str, p);
3668
+ return [0, 0];
3669
+ }
3670
+ function shape_len_7_26_9(str, p) {
3671
+ const c = str[p];
3672
+ if (c === "e") {
3673
+ if (str.length - p < 3 || str[p + 1] !== "y" || str[p + 2] !== "J") return [0, 0];
3674
+ if (p > 0 && (is_base64url_code(str.charCodeAt(p - 1)) || str[p - 1] === ".")) return [0, 0];
3675
+ let e = p;
3676
+ let dots = 0;
3677
+ while (e < str.length && (is_base64url_code(str.charCodeAt(e)) || str[e] === ".")) {
3678
+ if (str[e] === ".") dots++;
3679
+ e++;
3680
+ }
3681
+ while (str[e - 1] === ".") {
3682
+ e--;
3683
+ dots--;
3684
+ }
3685
+ return dots === 2 ? [0, e - p] : [0, 0];
3686
+ }
3687
+ if (c === "b" || c === "B") {
3688
+ if (str.length - p <= 6 || !ascii_case_equal(str, p, "bearer")) return [0, 0];
3689
+ if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return [0, 0];
3690
+ let t = p + 6;
3691
+ if (str[t] !== " " && str[t] !== " ") return [0, 0];
3692
+ while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
3693
+ let e = t;
3694
+ while (e < str.length && " \r\n'\",;}]\0".indexOf(str[e]) < 0) e++;
3695
+ return [t - p, e - t];
3696
+ }
3697
+ return [0, 0];
3698
+ }
3699
+ var COLON_VALUE_STOPS = " \r\n,;&)]}<>\"'\0";
3700
+ function colon_value_end(str, v) {
3701
+ if (v >= str.length) return v;
3702
+ const c = str[v];
3703
+ if (c === "\"" || c === "'") {
3704
+ let e = v + 1;
3705
+ while (e < str.length && str[e] !== c) {
3706
+ if (str[e] === "\\" && e + 1 < str.length) e++;
3707
+ e++;
3708
+ }
3709
+ return e < str.length ? e + 1 : str.length;
3710
+ }
3711
+ if (c === "{" || c === "[") {
3712
+ let depth = 0;
3713
+ let in_string = false;
3714
+ for (let e = v; e < str.length; e++) if (in_string) {
3715
+ if (str[e] === "\\" && e + 1 < str.length) e++;
3716
+ else if (str[e] === "\"") in_string = false;
3717
+ } else if (str[e] === "\"") in_string = true;
3718
+ else if (str[e] === "{" || str[e] === "[") depth++;
3719
+ else if (str[e] === "}" || str[e] === "]") {
3720
+ depth--;
3721
+ if (depth === 0) return e + 1;
3722
+ }
3723
+ return str.length;
3724
+ }
3725
+ if (c === "\\") {
3726
+ let q = v;
3727
+ while (q < str.length && str[q] === "\\") q++;
3728
+ if (q < str.length && (str[q] === "\"" || str[q] === "'")) {
3729
+ const level = q - v;
3730
+ let run = 0;
3731
+ for (let e = q + 1; e < str.length; e++) {
3732
+ if (str[e] === "\\") {
3733
+ run++;
3734
+ continue;
3735
+ }
3736
+ if (str[e] === str[q] && run === level) return e + 1;
3737
+ run = 0;
3738
+ }
3739
+ return str.length;
3740
+ }
3741
+ }
3742
+ let e = v;
3743
+ const n = scheme_word_len(str, v);
3744
+ if (n) {
3745
+ e = v + n;
3746
+ while (e < str.length && (str[e] === " " || str[e] === " ")) e++;
3747
+ if (e < str.length && (str[e] === "\"" || str[e] === "'")) return colon_value_end(str, e);
3748
+ }
3749
+ while (e < str.length && COLON_VALUE_STOPS.indexOf(str[e]) < 0) e++;
3750
+ return e;
3751
+ }
3573
3752
  var MASK_MAX_NAME = 128;
3574
3753
  var MASK_MAX_BYTES = 4194304;
3575
3754
  var MASK_TOO_LARGE = "<not shown: too large to mask>";
3576
- function mask_secrets_inline(str) {
3577
- if (typeof str !== "string") return null;
3578
- if (str.indexOf("=") < 0 && str.indexOf("eyJ") < 0 && !/bearer/i.test(str)) return null;
3579
- if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3580
- let value_is_secret = false;
3581
- let re_attr = /(^|[ \t])attribute=["']?([^ \t"']*)/g;
3582
- let m;
3583
- while ((m = re_attr.exec(str)) !== null) if (is_secret_name(m[2].slice(-128))) value_is_secret = true;
3584
- let parts = [];
3585
- let copied = 0;
3586
- let changed = false;
3755
+ function mark_range(marks, from, to) {
3756
+ for (let i = from; i < to; i++) marks[i] = 1;
3757
+ }
3758
+ function mark_params(str, value_is_secret, as_7_26_9, marks) {
3759
+ const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " " || str[i] === "\0";
3587
3760
  let outer = "";
3588
3761
  let word = 0;
3589
3762
  let p = 0;
3590
- const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " ";
3591
3763
  while (p < str.length) {
3592
- let c = str[p];
3593
- if (c === " " || c === " ") {
3764
+ const c = str[p];
3765
+ if (c === " " || c === " " || c === "\0") {
3594
3766
  p++;
3595
3767
  word = p;
3596
3768
  continue;
@@ -3599,48 +3771,116 @@ function mask_secrets_inline(str) {
3599
3771
  if (!outer && (p === 0 || str[p - 1] === " " || str[p - 1] === " " || str[p - 1] === "=")) outer = c;
3600
3772
  else if (c === outer && is_blank_or_end(p + 1)) outer = "";
3601
3773
  }
3602
- let shape = c === "e" ? jwt_len(str, p) : 0;
3603
- let token_offset = 0;
3604
- if (c === "b" || c === "B") [token_offset, shape] = bearer_token(str, p);
3605
- if (shape) {
3606
- parts.push(str.substring(copied, p + token_offset));
3607
- parts.push("********");
3608
- p += token_offset + shape;
3609
- copied = p;
3610
- changed = true;
3611
- word = p;
3612
- continue;
3774
+ if (as_7_26_9) {
3775
+ const [offset, n] = shape_len_7_26_9(str, p);
3776
+ if (n) {
3777
+ p += offset + n;
3778
+ word = p;
3779
+ continue;
3780
+ }
3613
3781
  }
3614
3782
  if (c !== "=") {
3615
3783
  p++;
3616
3784
  continue;
3617
3785
  }
3618
- let name = str.substring(Math.max(word, p - MASK_MAX_NAME), p);
3786
+ const name_from = Math.max(word, p - MASK_MAX_NAME);
3787
+ const name = str.substring(name_from, p);
3619
3788
  word = p + 1;
3620
- if (!(is_secret_name(name) || value_is_secret && name.toLowerCase() === "value")) {
3789
+ if (!(is_secret_name(name) || value_is_secret && name.length === 5 && ascii_case_equal(str, name_from, "value"))) {
3621
3790
  p++;
3622
3791
  continue;
3623
3792
  }
3624
3793
  p++;
3625
- let quote = str[p] === "\"" || str[p] === "'" ? str[p] : "";
3626
3794
  let end;
3795
+ const quote = p < str.length && (str[p] === "\"" || str[p] === "'") ? str[p] : "";
3627
3796
  if (quote) {
3628
3797
  end = p + 1;
3629
- while (end < str.length && str[end] !== quote) end++;
3630
- if (str[end] === quote) end++;
3798
+ while (end < str.length && str[end] !== quote && str[end] !== "\0") end++;
3799
+ if (end < str.length && str[end] === quote) end++;
3631
3800
  } else end = inline_secret_value_end(str, p, outer);
3632
- if (end > p) {
3633
- parts.push(str.substring(copied, p));
3801
+ mark_range(marks, p, end);
3802
+ p = end;
3803
+ word = p;
3804
+ }
3805
+ }
3806
+ function mark_shapes(str, marks) {
3807
+ for (let p = 0; p < str.length; p++) {
3808
+ const c = str[p];
3809
+ if (c !== "e" && c !== "b" && c !== "B") continue;
3810
+ const [offset, n] = shape_len(str, p);
3811
+ if (n) mark_range(marks, p + offset, p + offset + n);
3812
+ }
3813
+ }
3814
+ function mark_labels(str, marks) {
3815
+ let word = 0;
3816
+ let p = 0;
3817
+ while (p < str.length) {
3818
+ const c = str[p];
3819
+ if (c === " " || c === " " || c === "\0" || c === "=") {
3820
+ p++;
3821
+ word = p;
3822
+ continue;
3823
+ }
3824
+ if (c !== ":") {
3825
+ p++;
3826
+ continue;
3827
+ }
3828
+ if (!is_secret_name(str.substring(Math.max(word, p - MASK_MAX_NAME), p))) {
3829
+ p++;
3830
+ word = p;
3831
+ continue;
3832
+ }
3833
+ let v = p + 1;
3834
+ while (v < str.length && (str[v] === " " || str[v] === " ")) v++;
3835
+ const e = colon_value_end(str, v);
3836
+ mark_range(marks, v, e);
3837
+ const n_scheme = scheme_word_len(str, v);
3838
+ if (n_scheme) p = v + n_scheme;
3839
+ else p = e > v ? e : p + 1;
3840
+ word = p;
3841
+ }
3842
+ }
3843
+ /************************************************************
3844
+ * A text (a command line) with the secrets that can be told
3845
+ * written as "********" (each run of them as one), the rest as it
3846
+ * is: the value of every "name=value" whose name is a secret's,
3847
+ * quoted or not (and the "value" of a write-attr whose attribute
3848
+ * names a secret); the same for a "name: value", its name in quotes
3849
+ * or not (a json, a header, a label: "password": x, Authorization:
3850
+ * Basic x, bearer: tok); and a JWT or the credential after "Bearer "
3851
+ * or "Basic ", wherever they are, whatever names them. Each rule
3852
+ * judges the text as it came. Return null when there was nothing to
3853
+ * mask. As the C kernel's mask_secrets_inline().
3854
+ ************************************************************/
3855
+ function mask_secrets_inline(str) {
3856
+ if (typeof str !== "string") return null;
3857
+ if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
3858
+ if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3859
+ let value_is_secret = false;
3860
+ let re_attr = /(^|[ \t])attribute=["']?([^ \t"'\u0000]*)/g;
3861
+ let m;
3862
+ while ((m = re_attr.exec(str)) !== null) if (is_secret_name(m[2].slice(-128))) value_is_secret = true;
3863
+ const marks = new Uint8Array(str.length);
3864
+ mark_params(str, value_is_secret, true, marks);
3865
+ mark_params(str, value_is_secret, false, marks);
3866
+ mark_shapes(str, marks);
3867
+ mark_labels(str, marks);
3868
+ let parts = [];
3869
+ let changed = false;
3870
+ let i = 0;
3871
+ while (i < str.length) {
3872
+ let j = i;
3873
+ if (marks[i]) {
3874
+ while (j < str.length && marks[j]) j++;
3634
3875
  parts.push("********");
3635
- copied = end;
3636
3876
  changed = true;
3877
+ } else {
3878
+ while (j < str.length && !marks[j]) j++;
3879
+ parts.push(str.substring(i, j));
3637
3880
  }
3638
- p = end;
3639
- word = p;
3881
+ i = j;
3640
3882
  }
3641
- if (!changed) return null;
3642
- parts.push(str.substring(copied));
3643
- return parts.join("");
3883
+ return changed ? parts.join("") : null;
3644
3884
  }
3645
3885
  /************************************************************
3646
3886
  * A json as a log or a trace may show it: at any depth, the