@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 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`_
@@ -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
- export declare const extractSecretsFromUrl: (maybeUrlStr: string) => string[];
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;
@@ -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
- const SENSITIVE_SUBSTRINGS = [
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
- // doing this in a loop because there's not (yet) a replaceAll
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
- // was a url with secrets, save those but not the url itself
55
- if (urlSecrets.length > 0) {
56
- return [...result, ...urlSecrets];
49
+ if (urlSecrets == null) {
50
+ // value wasn't a url, pass it through
51
+ return [...result, value];
57
52
  }
58
- // otherwise, return the value
59
- return [...result, value];
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.substr(0, 10)}:`;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/secret-scrubber",
3
- "version": "1.0.3",
3
+ "version": "1.0.6",
4
4
  "description": "Confidently remove secrets and sensitive values from unstructured objects.",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",