@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 +29 -1
- package/dist/gobj-js.cjs.js +369 -40
- package/dist/gobj-js.cjs.js.map +1 -1
- package/dist/gobj-js.cjs.min.js +988 -749
- package/dist/gobj-js.cjs.min.js.map +1 -1
- package/dist/gobj-js.es.js +369 -40
- package/dist/gobj-js.es.js.map +1 -1
- package/dist/gobj-js.es.min.js +977 -738
- package/dist/gobj-js.es.min.js.map +1 -1
- package/dist/gobj-js.iife.js +369 -40
- package/dist/gobj-js.iife.js.map +1 -1
- package/dist/gobj-js.iife.min.js +1163 -924
- package/dist/gobj-js.iife.min.js.map +1 -1
- package/dist/gobj-js.umd.js +369 -40
- package/dist/gobj-js.umd.js.map +1 -1
- package/dist/gobj-js.umd.min.js +1163 -924
- package/dist/gobj-js.umd.min.js.map +1 -1
- package/package.json +1 -1
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:
|
|
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
|
|
package/dist/gobj-js.cjs.js
CHANGED
|
@@ -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
|
|
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
|
|
3513
|
-
*
|
|
3514
|
-
*
|
|
3515
|
-
*
|
|
3516
|
-
*
|
|
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
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
3565
|
-
|
|
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
|
-
|
|
3571
|
-
word = p;
|
|
3882
|
+
i = j;
|
|
3572
3883
|
}
|
|
3573
|
-
|
|
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()
|
|
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
|
|
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
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
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()}`;
|