kryptheon 0.1.30 → 0.1.31
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/kryptheon-keys.js +33 -0
- package/package.json +1 -1
package/kryptheon-keys.js
CHANGED
|
@@ -101,6 +101,23 @@ function looksLikeServiceRoleKey(value) {
|
|
|
101
101
|
return (value.match(/[0-9]/g) || []).length >= 4;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
// A quoted value given to something named as a secret, in a format this does
|
|
105
|
+
// not know. Found on a blind test (HarborLine): api.js held
|
|
106
|
+
// `const replaySigningKey = 'hl_replay_8JQ4-...'` in code every visitor
|
|
107
|
+
// downloads, and the read named nothing - it knew only famous formats. The
|
|
108
|
+
// name says what it is meant to be; the value has to look like one too:
|
|
109
|
+
// long, letters and digits, no spaces, not a placeholder. Names that are
|
|
110
|
+
// public by design (anon, publishable, public) are left alone.
|
|
111
|
+
const NAMED_SECRET = /\b([A-Za-z_$][\w$]*)\s*[:=]\s*(["'`])([^"'`\s]{16,})\2/g;
|
|
112
|
+
const SECRET_NAME = /(secret|signingkey|privatekey|apikey|accesstoken|authtoken|refreshtoken|password|passwd|clientsecret|webhooksecret|encryptionkey|hmackey)$/;
|
|
113
|
+
function looksLikeNamedSecret(name, value) {
|
|
114
|
+
const bare = name.toLowerCase().replace(/[_$]/g, '');
|
|
115
|
+
if (!SECRET_NAME.test(bare) || /anon|publishable|public/.test(bare)) return false;
|
|
116
|
+
if (/your|example|placeholder|changeme|x{4,}|here|dummy|redacted|<|\$\{/i.test(value)) return false;
|
|
117
|
+
if (/^(https?:|\/|\.)/i.test(value) || /^eyJ/.test(value)) return false; // an address or a path; JWTs are read above
|
|
118
|
+
return (value.match(/[0-9]/g) || []).length >= 3 && (value.match(/[A-Za-z]/g) || []).length >= 3;
|
|
119
|
+
}
|
|
120
|
+
|
|
104
121
|
/** First characters and length - enough to find it, too little to use it. */
|
|
105
122
|
function masked(value) {
|
|
106
123
|
return value.slice(0, 6) + '... (' + value.length + ' characters, not shown)';
|
|
@@ -293,6 +310,22 @@ function secretsIn(rel, source, loadedBy) {
|
|
|
293
310
|
}, 'medium');
|
|
294
311
|
}
|
|
295
312
|
|
|
313
|
+
// Never in a test, whose secrets are made up for it and ship nowhere.
|
|
314
|
+
const inTest = /(^|\/)__tests__\/|\.(test|spec|check)\.[cm]?[jt]sx?$/i.test(rel);
|
|
315
|
+
NAMED_SECRET.lastIndex = 0;
|
|
316
|
+
let s;
|
|
317
|
+
while (!inTest && (s = NAMED_SECRET.exec(source))) {
|
|
318
|
+
if (!looksLikeNamedSecret(s[1], s[3])) continue;
|
|
319
|
+
add(s.index + s[0].indexOf(s[3]), s[3], {
|
|
320
|
+
label: 'secret ("' + s[1] + '")',
|
|
321
|
+
opens: 'Its name says it is a secret, and whoever has it can do whatever it signs or unlocks. ' +
|
|
322
|
+
'Its format is not one I know, so check whether it is a real one.',
|
|
323
|
+
rotate: 'wherever it was issued, issue a new one and revoke this one',
|
|
324
|
+
instead: 'a call to your own server, which holds the secret',
|
|
325
|
+
severity: runs.browser ? 'HIGH' : 'MEDIUM',
|
|
326
|
+
}, 'medium');
|
|
327
|
+
}
|
|
328
|
+
|
|
296
329
|
// Only where it is read from the environment, and never in a test: found on
|
|
297
330
|
// Kryptheon's own code, a constant called PUBLIC_SECRET_NAME is a name in a
|
|
298
331
|
// program, not a variable the build ships - and a test file ships nothing.
|