email-bounce-parser 1.0.22 → 1.1.1

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 CHANGED
@@ -10,7 +10,7 @@ Parses bounce emails and extracts details.
10
10
 
11
11
  <table>
12
12
  <tr>
13
- <td align="center"><a href="https://crisp.chat/"><img src="https://crisp.chat/favicon-256x256.png" height="64" /></a></td>
13
+ <td align="center"><a href="https://crisp.chat/"><img src="https://crisp.chat/favicons/favicon-256x256.png" height="64" /></a></td>
14
14
  </tr>
15
15
  <tr>
16
16
  <td align="center">Crisp</td>
@@ -24,6 +24,22 @@ _👋 You use this library and you want to be listed there? [Contact us](https:/
24
24
  This library is used at [Crisp](https://crisp.chat/) everyday to parse bounce emails.
25
25
  It supports most variations of bounce emails (Postfix, Dovecat, Gmail, Outlook).
26
26
 
27
+ ## RE2 Support
28
+
29
+ This library uses the [RE2](https://github.com/uhop/node-re2) regex engine when available for better performance and [ReDoS](https://en.wikipedia.org/wiki/ReDoS) protection. It automatically falls back to native JavaScript `RegExp` if RE2 is unavailable or fails to load.
30
+
31
+ RE2 is an optional dependency. To use it, install it alongside this library:
32
+
33
+ ```bash
34
+ npm install re2
35
+ ```
36
+
37
+ After a Node.js upgrade, you may need to rebuild RE2:
38
+
39
+ ```bash
40
+ npm rebuild re2
41
+ ```
42
+
27
43
  ## Usage
28
44
 
29
45
  ```js
package/lib/parser.js CHANGED
@@ -1,8 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
 
4
- var RE2 = require("re2");
5
- var { loopRegexes, trimString } = require("./utils");
4
+ var { buildSafeRegExp, loopRegexes, trimString } = require("./utils");
6
5
 
7
6
  var ERROR_CODES_TYPES = {
8
7
  "421" : "service_not_available",
@@ -265,13 +264,13 @@ class Parser {
265
264
 
266
265
  for (var _i = 0; _i < _entry.length; _i++) {
267
266
  _regexes[_key].push(
268
- new RE2(_entry[_i])
267
+ buildSafeRegExp(_entry[_i])
269
268
  );
270
269
  }
271
270
  } else if (typeof _entry === "object" && Object.keys(_entry).length > 0) {
272
271
  _regexes[_key] = this.__initRegexes(_entry);
273
272
  } else {
274
- _regexes[_key] = new RE2(_entry);
273
+ _regexes[_key] = buildSafeRegExp(_entry);
275
274
  }
276
275
  }
277
276
 
@@ -316,7 +315,7 @@ class Parser {
316
315
  // In cases where the bounce is detected as permanent, or the \
317
316
  // temporary / permanent status is still unclear, run additional detection
318
317
  if (_permanent === true || (_temporary === false && _permanent === false)) {
319
- var _match = this.__regexes.temporary_safe.match(str);
318
+ var _match = str.match(this.__regexes.temporary_safe);
320
319
 
321
320
  if (_match && _match.length > 1) {
322
321
  _temporary = true;
@@ -415,14 +414,14 @@ class Parser {
415
414
  }
416
415
 
417
416
  // Check if blocked
418
- var _blocked_match = this.__regexes.blocked.match(str);
417
+ var _blocked_match = str.match(this.__regexes.blocked);
419
418
 
420
419
  if (_blocked_match && _blocked_match.length > 1) {
421
420
  _blocked = true;
422
421
  }
423
422
 
424
423
  // Check if spam
425
- var _spam_match = this.__regexes.spam.match(str);
424
+ var _spam_match = str.match(this.__regexes.spam);
426
425
 
427
426
  if (_spam_match && _spam_match.length > 1) {
428
427
  _spam = true;
package/lib/utils.js CHANGED
@@ -4,12 +4,45 @@
4
4
  * IMPORTS
5
5
  ***************************************************************************/
6
6
 
7
- var RE2 = require("re2");
7
+ /**************************************************************************
8
+ * INSTANCES
9
+ ***************************************************************************/
10
+
11
+ var RE2Engine = detectRE2();
8
12
 
9
13
  /**************************************************************************
10
14
  * FUNCTIONS
11
15
  ***************************************************************************/
12
16
 
17
+ /**
18
+ * Detects whether the RE2 engine is available
19
+ * @private
20
+ * @return {function|null} The RE2 engine, or null if unavailable
21
+ */
22
+ function detectRE2() {
23
+ // Notice: RE2 can be explicitly disabled, to test the RegExp fallback
24
+ if (process.env.EMAIL_BOUNCE_PARSER_DISABLE_RE2) {
25
+ return null;
26
+ }
27
+
28
+ try {
29
+ return require("re2");
30
+ } catch (error) {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Builds a safe regular expression (RE2 when available, RegExp otherwise)
37
+ * @public
38
+ * @param {string|RegExp} source
39
+ * @return {RegExp} The regular expression
40
+ */
41
+ var buildSafeRegExp = function(source) {
42
+ return (RE2Engine !== null) ? new RE2Engine(source) : new RegExp(source);
43
+ }
44
+
45
+
13
46
  /**
14
47
  * Executes multiple regular expressions until one succeeds
15
48
  * @public
@@ -31,12 +64,19 @@ var loopRegexes = function(
31
64
 
32
65
  if (typeof regexes[_i] === "string") {
33
66
  // Build the regex
34
- _regex = new RE2(regexes[_i]);
67
+ _regex = buildSafeRegExp(regexes[_i]);
35
68
  }
36
69
 
37
- var _currentMatch = (
38
- _regex[mode](str || "", mode === "replace" ? "" : undefined)
39
- );
70
+ // Notice: call String methods, as they support both RE2 and RegExp
71
+ var _currentMatch;
72
+
73
+ if (mode === "replace") {
74
+ _currentMatch = (str || "").replace(_regex, "");
75
+ } else if (mode === "split") {
76
+ _currentMatch = (str || "").split(_regex);
77
+ } else {
78
+ _currentMatch = (str || "").match(_regex);
79
+ }
40
80
 
41
81
  if (mode === "replace") {
42
82
  _match = _currentMatch;
@@ -98,10 +138,12 @@ var trimString = function(str) {
98
138
  * EXPORTS
99
139
  ***************************************************************************/
100
140
 
101
- exports.loopRegexes = loopRegexes;
102
- exports.trimString = trimString;
141
+ exports.buildSafeRegExp = buildSafeRegExp;
142
+ exports.loopRegexes = loopRegexes;
143
+ exports.trimString = trimString;
103
144
 
104
145
  exports.default = {
105
- loopRegexes : loopRegexes,
106
- trimString : trimString
146
+ buildSafeRegExp : buildSafeRegExp,
147
+ loopRegexes : loopRegexes,
148
+ trimString : trimString
107
149
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "email-bounce-parser",
3
- "version": "1.0.22",
3
+ "version": "1.1.1",
4
4
  "description": "Parses bounce emails and extract errors",
5
5
  "author": "Eliott Vincent <eliott@crisp.chat>",
6
6
  "main": "lib/index.js",
@@ -14,16 +14,27 @@
14
14
  },
15
15
  "engineStrict": true,
16
16
  "engines": {
17
- "node": ">=14.18.0"
17
+ "node": ">= 22.0.0"
18
18
  },
19
19
  "scripts": {
20
- "test": "nodeunit ./test/index.js"
20
+ "unit": "nodeunit ./test/index.js",
21
+ "unit:regexp": "EMAIL_BOUNCE_PARSER_DISABLE_RE2=1 npm run unit",
22
+ "test": "npm run unit && npm run unit:regexp"
21
23
  },
22
24
  "devDependencies": {
23
- "nodeunit-x": "0.15.0"
25
+ "nodeunit-x": "0.15.0",
26
+ "re2": "1.27.0"
24
27
  },
25
- "dependencies": {
26
- "re2": "1.20.7"
28
+ "peerDependencies": {
29
+ "re2": "1.x"
30
+ },
31
+ "peerDependenciesMeta": {
32
+ "re2": {
33
+ "optional": true
34
+ }
35
+ },
36
+ "allowScripts": {
37
+ "re2@1.27.0": true
27
38
  },
28
39
  "keywords": [
29
40
  "parser",