@zapier/secret-scrubber 1.0.3 → 1.0.6
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/CHANGELOG.md +26 -0
- package/lib/convenience.d.ts +7 -1
- package/lib/convenience.js +11 -4
- package/lib/index.js +6 -13
- package/lib/utils.d.ts +11 -0
- package/lib/utils.js +30 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## 1.0.6
|
|
2
|
+
|
|
3
|
+
_released `2022-04-06`_
|
|
4
|
+
|
|
5
|
+
- tweak `findSensitiveValues` to no longer return _any_ url with a querystring. It's always tried to extract secrets from a url, but now doesn't fall back to censoring the whole url.
|
|
6
|
+
|
|
7
|
+
Calling `findSensitiveValues` with a structure containing urls:
|
|
8
|
+
|
|
9
|
+
| input | before | after |
|
|
10
|
+
| ---------------------------- | -------------------------------- | ---------- |
|
|
11
|
+
| `zapier.com` | `[]` | `[]` |
|
|
12
|
+
| `zapier.com?api_key=123456` | `[123456]` | `[123456]` |
|
|
13
|
+
| `zapier.com?safe_key=123456` | `["zapier.com?safe_key=123456"]` | `[]` |
|
|
14
|
+
|
|
15
|
+
## 1.0.5
|
|
16
|
+
|
|
17
|
+
_released `2021-10-25`_
|
|
18
|
+
|
|
19
|
+
- Reduce `scrub` memory usage
|
|
20
|
+
|
|
21
|
+
## 1.0.4
|
|
22
|
+
|
|
23
|
+
_released `2021-10-04`_
|
|
24
|
+
|
|
25
|
+
- add `api-key` to sensitive substrings [!4](https://gitlab.com/zapier/team-developer-platform/secret-scrubber-js/-/merge_requests/4)
|
|
26
|
+
|
|
1
27
|
## 1.0.3
|
|
2
28
|
|
|
3
29
|
_released `2021-09-20`_
|
package/lib/convenience.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const SENSITIVE_SUBSTRINGS: string[];
|
|
1
2
|
/**
|
|
2
3
|
* Determine if a string:
|
|
3
4
|
* * is a url
|
|
@@ -5,4 +6,9 @@
|
|
|
5
6
|
*/
|
|
6
7
|
export declare const isUrlWithSecrets: (val: any) => boolean;
|
|
7
8
|
export declare const isSensitiveKey: (key: string) => boolean;
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Given a string:
|
|
11
|
+
* * if it's a valid url, return a `string[]` of any extracted secrets. Only secret keys will be pulled from the querystring
|
|
12
|
+
* * if it's not a valid url, return `null`
|
|
13
|
+
*/
|
|
14
|
+
export declare const extractSecretsFromUrl: (maybeUrlStr: string) => string[] | null;
|
package/lib/convenience.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractSecretsFromUrl = exports.isSensitiveKey = exports.isUrlWithSecrets = void 0;
|
|
3
|
+
exports.extractSecretsFromUrl = exports.isSensitiveKey = exports.isUrlWithSecrets = exports.SENSITIVE_SUBSTRINGS = void 0;
|
|
4
4
|
const url_1 = require("url");
|
|
5
5
|
// a couple of functions that power the public conveience functions, namely findSensitiveValues
|
|
6
|
-
|
|
6
|
+
// only exported for testing
|
|
7
|
+
exports.SENSITIVE_SUBSTRINGS = [
|
|
7
8
|
'api_key',
|
|
8
9
|
'apikey',
|
|
10
|
+
'api-key',
|
|
9
11
|
'auth',
|
|
10
12
|
'jwt',
|
|
11
13
|
'passwd',
|
|
@@ -31,8 +33,13 @@ const isUrlWithSecrets = (val) => {
|
|
|
31
33
|
return (url.username.length > 0 || url.password.length > 0 || url.search.length > 0);
|
|
32
34
|
};
|
|
33
35
|
exports.isUrlWithSecrets = isUrlWithSecrets;
|
|
34
|
-
const isSensitiveKey = (key) => SENSITIVE_SUBSTRINGS.some((substr) => key.toLowerCase().includes(substr));
|
|
36
|
+
const isSensitiveKey = (key) => exports.SENSITIVE_SUBSTRINGS.some((substr) => key.toLowerCase().includes(substr));
|
|
35
37
|
exports.isSensitiveKey = isSensitiveKey;
|
|
38
|
+
/**
|
|
39
|
+
* Given a string:
|
|
40
|
+
* * if it's a valid url, return a `string[]` of any extracted secrets. Only secret keys will be pulled from the querystring
|
|
41
|
+
* * if it's not a valid url, return `null`
|
|
42
|
+
*/
|
|
36
43
|
const extractSecretsFromUrl = (maybeUrlStr) => {
|
|
37
44
|
try {
|
|
38
45
|
const subValues = [];
|
|
@@ -54,7 +61,7 @@ const extractSecretsFromUrl = (maybeUrlStr) => {
|
|
|
54
61
|
const err = e;
|
|
55
62
|
if (err.code === 'ERR_INVALID_URL') {
|
|
56
63
|
// not a url, ignore
|
|
57
|
-
return
|
|
64
|
+
return null;
|
|
58
65
|
}
|
|
59
66
|
// otherwise, actual error - throw!
|
|
60
67
|
throw e;
|
package/lib/index.js
CHANGED
|
@@ -15,12 +15,7 @@ const scrub = (input, secretValues) => {
|
|
|
15
15
|
let copiedVal = val;
|
|
16
16
|
// have to look for substrings in the value instead of looking for the value in the map
|
|
17
17
|
Object.entries(sensitiveBank).forEach(([transformed, censored]) => {
|
|
18
|
-
|
|
19
|
-
// .replace takes a regex, but I'm worried about being able to properly excape incoming secrets
|
|
20
|
-
// so until 2023-04-30, we'll loop. Once we can use node 16 everywhere, this loop can be a single .replaceAll
|
|
21
|
-
while (copiedVal.includes(transformed)) {
|
|
22
|
-
copiedVal = copiedVal.replace(transformed, censored);
|
|
23
|
-
}
|
|
18
|
+
copiedVal = (0, utils_1.replaceAll)(copiedVal, transformed, censored);
|
|
24
19
|
});
|
|
25
20
|
return copiedVal;
|
|
26
21
|
}
|
|
@@ -51,15 +46,13 @@ const findSensitiveValues = (obj) => {
|
|
|
51
46
|
.reduce((result, value) => {
|
|
52
47
|
// iterate the first pass and try to pull more specific info about of urls
|
|
53
48
|
const urlSecrets = (0, convenience_1.extractSecretsFromUrl)(value);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return [...result,
|
|
49
|
+
if (urlSecrets == null) {
|
|
50
|
+
// value wasn't a url, pass it through
|
|
51
|
+
return [...result, value];
|
|
57
52
|
}
|
|
58
|
-
//
|
|
59
|
-
return [...result,
|
|
53
|
+
// return any secrets found (could be 0), but not the full url
|
|
54
|
+
return [...result, ...urlSecrets];
|
|
60
55
|
}, []);
|
|
61
|
-
// iterate the first pass and try to pull out
|
|
62
|
-
// return values.re
|
|
63
56
|
};
|
|
64
57
|
exports.findSensitiveValues = findSensitiveValues;
|
|
65
58
|
var utils_2 = require("./utils");
|
package/lib/utils.d.ts
CHANGED
|
@@ -28,3 +28,14 @@ export declare const recurseReplace: (obj: any, replacer: (val: any) => any) =>
|
|
|
28
28
|
* * otherwise, we pass the key and value to the matcher. If it returns `true`, we collect the value
|
|
29
29
|
*/
|
|
30
30
|
export declare const recurseExtract: (obj: object | any[], matcher: (key: string, value: any) => boolean) => string[];
|
|
31
|
+
/**
|
|
32
|
+
* Custom implementation of String.prototype.replaceAll (not tied to the String namespace though).
|
|
33
|
+
*
|
|
34
|
+
* Key differences between String.prototype.replaceAll and this implementation:
|
|
35
|
+
* - searchString only supports Strings (not regex)
|
|
36
|
+
* - replaceValue only supports Strings (not functions)
|
|
37
|
+
* - replaceValue must not be the empty string
|
|
38
|
+
*
|
|
39
|
+
* Source for inspiration: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.replace-all.js
|
|
40
|
+
*/
|
|
41
|
+
export declare const replaceAll: (baseString: string, searchString: string, replaceValue: string) => string;
|
package/lib/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.recurseExtract = exports.recurseReplace = exports.makeSensitiveBank = exports.censor = exports.base64 = exports.hash = exports.isLongEnoughToBeSecret = void 0;
|
|
3
|
+
exports.replaceAll = exports.recurseExtract = exports.recurseReplace = exports.makeSensitiveBank = exports.censor = exports.base64 = exports.hash = exports.isLongEnoughToBeSecret = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
|
5
5
|
const isPlainObject = require("lodash.isplainobject");
|
|
6
6
|
const MIN_SECRET_SIZE = 6;
|
|
@@ -25,7 +25,7 @@ const censor = (val) => {
|
|
|
25
25
|
// }
|
|
26
26
|
const salted = val + ((_a = process.env.ZAPIER_SCRUBBER_SALT) !== null && _a !== void 0 ? _a : 'averysecretsalt');
|
|
27
27
|
const hashed = (0, exports.hash)(salted);
|
|
28
|
-
return `:censored:${val.length}:${hashed.
|
|
28
|
+
return `:censored:${val.length}:${hashed.substring(0, 10)}:`;
|
|
29
29
|
};
|
|
30
30
|
exports.censor = censor;
|
|
31
31
|
/**
|
|
@@ -120,9 +120,6 @@ const recurseExtract = (obj, matcher) => {
|
|
|
120
120
|
}
|
|
121
121
|
// value is simple-ish
|
|
122
122
|
if (matcher(key, value)) {
|
|
123
|
-
if (value == null) {
|
|
124
|
-
return value; // gets filtered below
|
|
125
|
-
}
|
|
126
123
|
return value === null || value === void 0 ? void 0 : value.toString();
|
|
127
124
|
}
|
|
128
125
|
})
|
|
@@ -131,3 +128,31 @@ const recurseExtract = (obj, matcher) => {
|
|
|
131
128
|
return result;
|
|
132
129
|
};
|
|
133
130
|
exports.recurseExtract = recurseExtract;
|
|
131
|
+
/**
|
|
132
|
+
* Custom implementation of String.prototype.replaceAll (not tied to the String namespace though).
|
|
133
|
+
*
|
|
134
|
+
* Key differences between String.prototype.replaceAll and this implementation:
|
|
135
|
+
* - searchString only supports Strings (not regex)
|
|
136
|
+
* - replaceValue only supports Strings (not functions)
|
|
137
|
+
* - replaceValue must not be the empty string
|
|
138
|
+
*
|
|
139
|
+
* Source for inspiration: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.replace-all.js
|
|
140
|
+
*/
|
|
141
|
+
const replaceAll = (baseString, searchString, replaceValue) => {
|
|
142
|
+
if (searchString === '') {
|
|
143
|
+
return baseString;
|
|
144
|
+
}
|
|
145
|
+
let result = '';
|
|
146
|
+
let endOfLastMatch = 0;
|
|
147
|
+
let position = baseString.indexOf(searchString);
|
|
148
|
+
while (position !== -1) {
|
|
149
|
+
result += baseString.slice(endOfLastMatch, position) + replaceValue;
|
|
150
|
+
endOfLastMatch = position + searchString.length;
|
|
151
|
+
position = baseString.indexOf(searchString, endOfLastMatch);
|
|
152
|
+
}
|
|
153
|
+
if (endOfLastMatch < baseString.length) {
|
|
154
|
+
result += baseString.slice(endOfLastMatch);
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
};
|
|
158
|
+
exports.replaceAll = replaceAll;
|