@yuneta/gobj-js 7.26.18 → 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.
- package/README.md +14 -1
- package/dist/gobj-js.cjs.js +112 -18
- package/dist/gobj-js.cjs.js.map +1 -1
- package/dist/gobj-js.cjs.min.js +962 -906
- package/dist/gobj-js.cjs.min.js.map +1 -1
- package/dist/gobj-js.es.js +112 -18
- package/dist/gobj-js.es.js.map +1 -1
- package/dist/gobj-js.es.min.js +983 -927
- package/dist/gobj-js.es.min.js.map +1 -1
- package/dist/gobj-js.iife.js +112 -18
- package/dist/gobj-js.iife.js.map +1 -1
- package/dist/gobj-js.iife.min.js +962 -906
- package/dist/gobj-js.iife.min.js.map +1 -1
- package/dist/gobj-js.umd.js +112 -18
- package/dist/gobj-js.umd.js.map +1 -1
- package/dist/gobj-js.umd.min.js +962 -906
- package/dist/gobj-js.umd.min.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -669,13 +669,21 @@ 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: Bearer ********' u"
|
|
674
678
|
json_mask_secrets({user: "a", password: "x"}) // → {user: "a", password: "********"}
|
|
679
|
+
json_mask_secrets({note: "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4In0.c2ln"})
|
|
680
|
+
// → {note: "********"}: a JWT under any key
|
|
675
681
|
trace_json_masked(kw, "sent") // trace_json() of the masked copy
|
|
676
682
|
```
|
|
677
683
|
|
|
678
|
-
As the C kernel's:
|
|
684
|
+
As the C kernel's: the same names, the same shapes by value (a JWT, the token
|
|
685
|
+
after `Bearer `), a NUL in a string read as a blank; linear and bounded (a text
|
|
686
|
+
over 4 MB is not shown at all). `key` and `cert` are not secrets' names.
|
|
679
687
|
|
|
680
688
|
### Logging
|
|
681
689
|
|
|
@@ -795,6 +803,11 @@ gobj_command(yuno, "set-global-no-trace", {level: "timer_periodic", set: 0}, yun
|
|
|
795
803
|
// no_trace_levels.__global_no_trace__ = [] -- stays off after a reload
|
|
796
804
|
```
|
|
797
805
|
|
|
806
|
+
`set` is read as the C kernel reads it: a boolean, an integer, or text --
|
|
807
|
+
`set`/`reset`, a JSON integer (`"1"`, `"-1"`, `"0"`), `true`/`false`,
|
|
808
|
+
`yes`/`no`, `on`/`off` in any case. Anything else answers `-1` *"bitmask set
|
|
809
|
+
or re-set?"*: a real (`2.5`), `" 1"`, `"0x1"`, `"1e3"`, `"+5"`, `"007"`.
|
|
810
|
+
|
|
798
811
|
The traffic of the websocket is `C_IEVENT_CLI`'s own level `ievents`; its lines
|
|
799
812
|
go to the yuno attr `trace_ievent_callback`, or to the console.
|
|
800
813
|
|
package/dist/gobj-js.cjs.js
CHANGED
|
@@ -3336,18 +3336,23 @@ function format_log(format) {
|
|
|
3336
3336
|
} else output = args.map((arg) => typeof arg === "object" ? log_stringify(arg) : log_string(arg)).join(" ");
|
|
3337
3337
|
return output;
|
|
3338
3338
|
}
|
|
3339
|
-
var __in_log_function__ = false;
|
|
3340
3339
|
function call_log_function(fn, ...args) {
|
|
3341
|
-
if (__in_log_function__) return;
|
|
3342
|
-
__in_log_function__ = true;
|
|
3343
3340
|
try {
|
|
3344
3341
|
fn(...args);
|
|
3345
3342
|
} catch (e) {
|
|
3346
3343
|
try {
|
|
3347
3344
|
_console.error("a log function threw:", e);
|
|
3348
3345
|
} catch (e2) {}
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
var __in_remote_log__ = false;
|
|
3349
|
+
function call_remote_log_function(fn, ...args) {
|
|
3350
|
+
if (__in_remote_log__) return;
|
|
3351
|
+
__in_remote_log__ = true;
|
|
3352
|
+
try {
|
|
3353
|
+
call_log_function(fn, ...args);
|
|
3349
3354
|
} finally {
|
|
3350
|
-
|
|
3355
|
+
__in_remote_log__ = false;
|
|
3351
3356
|
}
|
|
3352
3357
|
}
|
|
3353
3358
|
function log_error(format, ...args) {
|
|
@@ -3358,7 +3363,7 @@ function log_error(format, ...args) {
|
|
|
3358
3363
|
if (console_wants("error", msg)) call_log_function(f_error, "%c" + hora + " ERROR: " + String(msg), "color:red");
|
|
3359
3364
|
} else {
|
|
3360
3365
|
if (console_wants("error", msg)) _console.error("%c" + hora + " ERROR: " + String(msg), "color:red");
|
|
3361
|
-
|
|
3366
|
+
call_remote_log_function(f_error, `${hora} ERROR: ${String(msg)}`);
|
|
3362
3367
|
}
|
|
3363
3368
|
}
|
|
3364
3369
|
emit_log_callback("error", msg, hora);
|
|
@@ -3371,7 +3376,7 @@ function log_warning(format, ...args) {
|
|
|
3371
3376
|
if (console_wants("warning", msg)) call_log_function(f_warning, "%c" + hora + " WARNING: " + String(msg), "color:yellow");
|
|
3372
3377
|
} else {
|
|
3373
3378
|
if (console_wants("warning", msg)) _console.warn("%c" + hora + " WARNING: " + String(msg), "color:yellow");
|
|
3374
|
-
|
|
3379
|
+
call_remote_log_function(f_warning, `${hora} WARNING: ${String(msg)}`);
|
|
3375
3380
|
}
|
|
3376
3381
|
}
|
|
3377
3382
|
emit_log_callback("warning", msg, hora);
|
|
@@ -3431,6 +3436,11 @@ var SECRET_NAME_JOINED = [
|
|
|
3431
3436
|
"sessionkey",
|
|
3432
3437
|
"authdata"
|
|
3433
3438
|
];
|
|
3439
|
+
var SECRET_NAME_SEGMENTS = /* @__PURE__ */ new Set([
|
|
3440
|
+
"pass",
|
|
3441
|
+
"pin",
|
|
3442
|
+
"otp"
|
|
3443
|
+
]);
|
|
3434
3444
|
var NOT_SECRET_SEGMENTS = /* @__PURE__ */ new Set([
|
|
3435
3445
|
"endpoint",
|
|
3436
3446
|
"url",
|
|
@@ -3463,10 +3473,16 @@ function is_secret_name(name) {
|
|
|
3463
3473
|
if (typeof name !== "string" || name.length < 3) return false;
|
|
3464
3474
|
let lower = name.toLowerCase();
|
|
3465
3475
|
let hit = false;
|
|
3466
|
-
for (const
|
|
3476
|
+
for (const segment of lower.split(/[^a-z0-9]+/)) if (SECRET_NAME_SEGMENTS.has(segment)) {
|
|
3467
3477
|
hit = true;
|
|
3468
3478
|
break;
|
|
3469
3479
|
}
|
|
3480
|
+
if (!hit) {
|
|
3481
|
+
for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
|
|
3482
|
+
hit = true;
|
|
3483
|
+
break;
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3470
3486
|
if (!hit) {
|
|
3471
3487
|
let joined = lower.replace(/[_\-. \t]/g, "");
|
|
3472
3488
|
for (const part of SECRET_NAME_JOINED) if (joined.indexOf(part) >= 0) {
|
|
@@ -3504,17 +3520,63 @@ function inline_secret_value_end(str, p, outer) {
|
|
|
3504
3520
|
}
|
|
3505
3521
|
}
|
|
3506
3522
|
/************************************************************
|
|
3523
|
+
* A credential by its SHAPE, whatever names it -- the C kernel's
|
|
3524
|
+
* jwt_len() / bearer_token_len(), the shapes the agent's audit
|
|
3525
|
+
* record redacts:
|
|
3526
|
+
* - a JWT: "eyJ" (the base64url of '{"') at the start of a
|
|
3527
|
+
* word, and three parts of base64url joined by '.'; the dots
|
|
3528
|
+
* at the end of the run are not its own (a JWT ends a
|
|
3529
|
+
* sentence: "... eyJ.eyJ.sig.");
|
|
3530
|
+
* - the token after "Bearer " (any case, a word of its own).
|
|
3531
|
+
************************************************************/
|
|
3532
|
+
function is_ascii_alnum_code(code) {
|
|
3533
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
3534
|
+
}
|
|
3535
|
+
function is_base64url_code(code) {
|
|
3536
|
+
return is_ascii_alnum_code(code) || code === 45 || code === 95;
|
|
3537
|
+
}
|
|
3538
|
+
function jwt_len(str, p) {
|
|
3539
|
+
if (str.length - p < 3 || str[p] !== "e" || str[p + 1] !== "y" || str[p + 2] !== "J") return 0;
|
|
3540
|
+
if (p > 0 && (is_base64url_code(str.charCodeAt(p - 1)) || str[p - 1] === ".")) return 0;
|
|
3541
|
+
let e = p;
|
|
3542
|
+
let dots = 0;
|
|
3543
|
+
while (e < str.length && (is_base64url_code(str.charCodeAt(e)) || str[e] === ".")) {
|
|
3544
|
+
if (str[e] === ".") dots++;
|
|
3545
|
+
e++;
|
|
3546
|
+
}
|
|
3547
|
+
while (str[e - 1] === ".") {
|
|
3548
|
+
e--;
|
|
3549
|
+
dots--;
|
|
3550
|
+
}
|
|
3551
|
+
return dots === 2 ? e - p : 0;
|
|
3552
|
+
}
|
|
3553
|
+
var BEARER_STOPS = " \r\n'\",;}]\0";
|
|
3554
|
+
function bearer_token(str, p) {
|
|
3555
|
+
const word_len = 6;
|
|
3556
|
+
if (str.length - p <= word_len || str.slice(p, p + word_len).toLowerCase() !== "bearer") return [0, 0];
|
|
3557
|
+
if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return [0, 0];
|
|
3558
|
+
let t = p + word_len;
|
|
3559
|
+
if (str[t] !== " " && str[t] !== " ") return [0, 0];
|
|
3560
|
+
while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
|
|
3561
|
+
let e = t;
|
|
3562
|
+
while (e < str.length && BEARER_STOPS.indexOf(str[e]) < 0) e++;
|
|
3563
|
+
return [t - p, e - t];
|
|
3564
|
+
}
|
|
3565
|
+
/************************************************************
|
|
3507
3566
|
* A text (a command line) with the value of every "name=value"
|
|
3508
3567
|
* whose name is a secret's written as "********", quoted or not
|
|
3509
3568
|
* (and the "value" of a write-attr whose attribute names a
|
|
3510
|
-
* secret)
|
|
3511
|
-
*
|
|
3569
|
+
* secret); and a JWT or the token after "Bearer ", wherever they
|
|
3570
|
+
* are, whatever names them. The rest as it is. Return null when
|
|
3571
|
+
* there was nothing to mask. As the C kernel's
|
|
3572
|
+
* mask_secrets_inline().
|
|
3512
3573
|
************************************************************/
|
|
3513
3574
|
var MASK_MAX_NAME = 128;
|
|
3514
3575
|
var MASK_MAX_BYTES = 4194304;
|
|
3515
3576
|
var MASK_TOO_LARGE = "<not shown: too large to mask>";
|
|
3516
3577
|
function mask_secrets_inline(str) {
|
|
3517
|
-
if (typeof str !== "string"
|
|
3578
|
+
if (typeof str !== "string") return null;
|
|
3579
|
+
if (str.indexOf("=") < 0 && str.indexOf("eyJ") < 0 && !/bearer/i.test(str)) return null;
|
|
3518
3580
|
if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
|
|
3519
3581
|
let value_is_secret = false;
|
|
3520
3582
|
let re_attr = /(^|[ \t])attribute=["']?([^ \t"']*)/g;
|
|
@@ -3538,6 +3600,18 @@ function mask_secrets_inline(str) {
|
|
|
3538
3600
|
if (!outer && (p === 0 || str[p - 1] === " " || str[p - 1] === " " || str[p - 1] === "=")) outer = c;
|
|
3539
3601
|
else if (c === outer && is_blank_or_end(p + 1)) outer = "";
|
|
3540
3602
|
}
|
|
3603
|
+
let shape = c === "e" ? jwt_len(str, p) : 0;
|
|
3604
|
+
let token_offset = 0;
|
|
3605
|
+
if (c === "b" || c === "B") [token_offset, shape] = bearer_token(str, p);
|
|
3606
|
+
if (shape) {
|
|
3607
|
+
parts.push(str.substring(copied, p + token_offset));
|
|
3608
|
+
parts.push("********");
|
|
3609
|
+
p += token_offset + shape;
|
|
3610
|
+
copied = p;
|
|
3611
|
+
changed = true;
|
|
3612
|
+
word = p;
|
|
3613
|
+
continue;
|
|
3614
|
+
}
|
|
3541
3615
|
if (c !== "=") {
|
|
3542
3616
|
p++;
|
|
3543
3617
|
continue;
|
|
@@ -3574,7 +3648,8 @@ function mask_secrets_inline(str) {
|
|
|
3574
3648
|
* value of a key with a secret's name is "********", whatever
|
|
3575
3649
|
* its type (not an absent one, a null or an empty string); so is
|
|
3576
3650
|
* the "value" of a dict whose "attribute" names a secret; and a
|
|
3577
|
-
* string is masked as mask_secrets_inline()
|
|
3651
|
+
* string, under any key, is masked as mask_secrets_inline() (a
|
|
3652
|
+
* JWT, a "Bearer" token, a secret name=value). Return a masked
|
|
3578
3653
|
* copy, or `jn` itself when there was nothing to mask. As the C
|
|
3579
3654
|
* kernel's json_mask_secrets().
|
|
3580
3655
|
*
|
|
@@ -3603,7 +3678,7 @@ function _json_mask_secrets(jn, memo, depth, budget) {
|
|
|
3603
3678
|
return MASK_TOO_LARGE;
|
|
3604
3679
|
}
|
|
3605
3680
|
budget.left -= jn.length;
|
|
3606
|
-
let masked = mask_secrets_inline(jn);
|
|
3681
|
+
let masked = mask_secrets_inline(jn.indexOf("\0") >= 0 ? jn.replace(/\u0000/g, " ") : jn);
|
|
3607
3682
|
return masked !== null ? masked : jn;
|
|
3608
3683
|
}
|
|
3609
3684
|
let is_array = Array.isArray(jn);
|
|
@@ -7105,15 +7180,34 @@ function save_trace_scope(gobj, attr, scope, levels) {
|
|
|
7105
7180
|
return gobj_save_persistent_attrs(gobj, attr);
|
|
7106
7181
|
}
|
|
7107
7182
|
/***************************************************************
|
|
7108
|
-
* "set" as the C command reads it
|
|
7183
|
+
* "set" as the C command reads it (trace_set_value() of c_yuno.c):
|
|
7184
|
+
* a boolean; an integer in the range of an int (true unless 0); or
|
|
7185
|
+
* text -- set / reset, a JSON integer -?(0|[1-9][0-9]*), or true /
|
|
7186
|
+
* false, yes / no, on / off, any case. null for anything else: a
|
|
7187
|
+
* real (2.5), " 1", "0x1", "1e3", "+5", "007" and "2.5" are refused,
|
|
7188
|
+
* as in C. Up to gobj-js 7.26.19 the text went through Number(),
|
|
7189
|
+
* which took all of them, and "yes" / "on" were refused.
|
|
7109
7190
|
***************************************************************/
|
|
7191
|
+
var INT_MIN = -2147483648;
|
|
7192
|
+
var INT_MAX = 2147483647;
|
|
7110
7193
|
function parse_set(value) {
|
|
7111
7194
|
if (value === true || value === false) return value;
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7195
|
+
if (typeof value === "number") {
|
|
7196
|
+
if (!Number.isInteger(value) || value < INT_MIN || value > INT_MAX) return null;
|
|
7197
|
+
return value !== 0;
|
|
7198
|
+
}
|
|
7199
|
+
if (typeof value !== "string" || value === "") return null;
|
|
7200
|
+
let v = value.toLowerCase();
|
|
7201
|
+
if (v === "set") return true;
|
|
7202
|
+
if (v === "reset") return false;
|
|
7203
|
+
if (/^-?(0|[1-9][0-9]*)$/.test(value)) {
|
|
7204
|
+
let n = Number(value);
|
|
7205
|
+
if (n < INT_MIN || n > INT_MAX) return null;
|
|
7206
|
+
return n !== 0;
|
|
7207
|
+
}
|
|
7208
|
+
if (v === "true" || v === "yes" || v === "on") return true;
|
|
7209
|
+
if (v === "false" || v === "no" || v === "off") return false;
|
|
7210
|
+
return null;
|
|
7117
7211
|
}
|
|
7118
7212
|
function yuno_prefix() {
|
|
7119
7213
|
return `${gobj_yuno_role()}^${gobj_yuno_name()}`;
|