haraka 0.0.33 → 3.3.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.
Files changed (254) hide show
  1. package/.githooks/pre-commit +41 -0
  2. package/.prettierignore +7 -0
  3. package/.qlty/.gitignore +7 -0
  4. package/.qlty/configs/.shellcheckrc +1 -0
  5. package/.qlty/qlty.toml +15 -0
  6. package/CHANGELOG.md +1898 -0
  7. package/CONTRIBUTORS.md +34 -0
  8. package/Dockerfile +50 -0
  9. package/LICENSE +22 -0
  10. package/Plugins.md +227 -0
  11. package/README.md +119 -4
  12. package/SECURITY.md +178 -0
  13. package/TODO +22 -0
  14. package/bin/haraka +593 -0
  15. package/bin/haraka_grep +32 -0
  16. package/config/aliases +2 -0
  17. package/config/auth_flat_file.ini +7 -0
  18. package/config/auth_vpopmaild.ini +9 -0
  19. package/config/connection.ini +79 -0
  20. package/config/delay_deny.ini +7 -0
  21. package/config/host_list +3 -0
  22. package/config/host_list_regex +6 -0
  23. package/config/http.ini +11 -0
  24. package/config/lmtp.ini +7 -0
  25. package/config/log.ini +11 -0
  26. package/config/outbound.bounce_message +18 -0
  27. package/config/outbound.bounce_message_html +36 -0
  28. package/config/outbound.bounce_message_image +106 -0
  29. package/config/outbound.ini +24 -0
  30. package/config/plugins +67 -0
  31. package/config/smtp.ini +37 -0
  32. package/config/smtp_bridge.ini +4 -0
  33. package/config/smtp_forward.ini +31 -0
  34. package/config/smtp_proxy.ini +27 -0
  35. package/config/tarpit.timeout +1 -0
  36. package/config/tls.ini +83 -0
  37. package/config/watch.ini +12 -0
  38. package/config/xclient.hosts +2 -0
  39. package/connection.js +1865 -0
  40. package/contrib/Haraka.cf +6 -0
  41. package/contrib/Haraka.pm +35 -0
  42. package/contrib/bad_smtp_server.pl +25 -0
  43. package/contrib/bsd-rc.d/haraka +63 -0
  44. package/contrib/debian-init.d/haraka +87 -0
  45. package/contrib/haraka.init +96 -0
  46. package/contrib/haraka.service +23 -0
  47. package/contrib/plugin2npm.sh +81 -0
  48. package/contrib/ubuntu-upstart/haraka.conf +27 -0
  49. package/docs/Body.md +1 -0
  50. package/docs/Config.md +1 -0
  51. package/docs/Connection.md +153 -0
  52. package/docs/CoreConfig.md +96 -0
  53. package/docs/CustomReturnCodes.md +3 -0
  54. package/docs/HAProxy.md +62 -0
  55. package/docs/Header.md +1 -0
  56. package/docs/Logging.md +129 -0
  57. package/docs/Outbound.md +210 -0
  58. package/docs/Plugins.md +372 -0
  59. package/docs/Results.md +7 -0
  60. package/docs/Transaction.md +135 -0
  61. package/docs/Tutorial.md +183 -0
  62. package/docs/deprecated/access.md +3 -0
  63. package/docs/deprecated/backscatterer.md +9 -0
  64. package/docs/deprecated/connect.rdns_access.md +53 -0
  65. package/docs/deprecated/data.headers.md +3 -0
  66. package/docs/deprecated/data.nomsgid.md +7 -0
  67. package/docs/deprecated/data.noreceived.md +11 -0
  68. package/docs/deprecated/data.rfc5322_header_checks.md +11 -0
  69. package/docs/deprecated/dkim_sign.md +97 -0
  70. package/docs/deprecated/dkim_verify.md +28 -0
  71. package/docs/deprecated/dnsbl.md +80 -0
  72. package/docs/deprecated/dnswl.md +73 -0
  73. package/docs/deprecated/lookup_rdns.strict.md +67 -0
  74. package/docs/deprecated/mail_from.access.md +52 -0
  75. package/docs/deprecated/mail_from.blocklist.md +18 -0
  76. package/docs/deprecated/mail_from.nobounces.md +8 -0
  77. package/docs/deprecated/rcpt_to.access.md +53 -0
  78. package/docs/deprecated/rcpt_to.blocklist.md +18 -0
  79. package/docs/deprecated/rcpt_to.routes.md +3 -0
  80. package/docs/deprecated/rdns.regexp.md +30 -0
  81. package/docs/plugins/aliases.md +3 -0
  82. package/docs/plugins/auth/auth_bridge.md +34 -0
  83. package/docs/plugins/auth/auth_ldap.md +4 -0
  84. package/docs/plugins/auth/auth_proxy.md +36 -0
  85. package/docs/plugins/auth/auth_vpopmaild.md +33 -0
  86. package/docs/plugins/auth/flat_file.md +40 -0
  87. package/docs/plugins/block_me.md +18 -0
  88. package/docs/plugins/data.signatures.md +11 -0
  89. package/docs/plugins/delay_deny.md +23 -0
  90. package/docs/plugins/max_unrecognized_commands.md +6 -0
  91. package/docs/plugins/prevent_credential_leaks.md +22 -0
  92. package/docs/plugins/process_title.md +42 -0
  93. package/docs/plugins/queue/deliver.md +3 -0
  94. package/docs/plugins/queue/discard.md +32 -0
  95. package/docs/plugins/queue/lmtp.md +24 -0
  96. package/docs/plugins/queue/qmail-queue.md +16 -0
  97. package/docs/plugins/queue/quarantine.md +87 -0
  98. package/docs/plugins/queue/smtp_bridge.md +32 -0
  99. package/docs/plugins/queue/smtp_forward.md +127 -0
  100. package/docs/plugins/queue/smtp_proxy.md +68 -0
  101. package/docs/plugins/queue/test.md +7 -0
  102. package/docs/plugins/rcpt_to.in_host_list.md +34 -0
  103. package/docs/plugins/rcpt_to.max_count.md +3 -0
  104. package/docs/plugins/record_envelope_addresses.md +20 -0
  105. package/docs/plugins/relay.md +3 -0
  106. package/docs/plugins/reseed_rng.md +16 -0
  107. package/docs/plugins/status.md +41 -0
  108. package/docs/plugins/tarpit.md +50 -0
  109. package/docs/plugins/tls.md +235 -0
  110. package/docs/plugins/toobusy.md +27 -0
  111. package/docs/plugins/xclient.md +10 -0
  112. package/docs/tutorials/Migrating_from_v1_to_v2.md +96 -0
  113. package/docs/tutorials/SettingUpOutbound.md +62 -0
  114. package/eslint.config.mjs +2 -0
  115. package/haraka.js +74 -0
  116. package/haraka.sh +2 -0
  117. package/http/html/404.html +58 -0
  118. package/http/html/index.html +47 -0
  119. package/http/package.json +21 -0
  120. package/line_socket.js +24 -0
  121. package/logger.js +322 -0
  122. package/outbound/client_pool.js +59 -0
  123. package/outbound/config.js +134 -0
  124. package/outbound/hmail.js +1504 -0
  125. package/outbound/index.js +349 -0
  126. package/outbound/qfile.js +93 -0
  127. package/outbound/queue.js +399 -0
  128. package/outbound/tls.js +85 -0
  129. package/outbound/todo.js +17 -0
  130. package/package.json +100 -4
  131. package/plugins/.eslintrc.yaml +3 -0
  132. package/plugins/auth/auth_base.js +261 -0
  133. package/plugins/auth/auth_bridge.js +20 -0
  134. package/plugins/auth/auth_proxy.js +227 -0
  135. package/plugins/auth/auth_vpopmaild.js +162 -0
  136. package/plugins/auth/flat_file.js +44 -0
  137. package/plugins/block_me.js +88 -0
  138. package/plugins/data.signatures.js +30 -0
  139. package/plugins/delay_deny.js +153 -0
  140. package/plugins/prevent_credential_leaks.js +61 -0
  141. package/plugins/process_title.js +197 -0
  142. package/plugins/profile.js +11 -0
  143. package/plugins/queue/deliver.js +12 -0
  144. package/plugins/queue/discard.js +27 -0
  145. package/plugins/queue/lmtp.js +45 -0
  146. package/plugins/queue/qmail-queue.js +93 -0
  147. package/plugins/queue/quarantine.js +133 -0
  148. package/plugins/queue/smtp_bridge.js +45 -0
  149. package/plugins/queue/smtp_forward.js +371 -0
  150. package/plugins/queue/smtp_proxy.js +142 -0
  151. package/plugins/queue/test.js +15 -0
  152. package/plugins/rcpt_to.host_list_base.js +65 -0
  153. package/plugins/rcpt_to.in_host_list.js +56 -0
  154. package/plugins/record_envelope_addresses.js +17 -0
  155. package/plugins/reseed_rng.js +7 -0
  156. package/plugins/status.js +274 -0
  157. package/plugins/tarpit.js +45 -0
  158. package/plugins/tls.js +164 -0
  159. package/plugins/toobusy.js +47 -0
  160. package/plugins/xclient.js +124 -0
  161. package/plugins.js +605 -0
  162. package/run_tests +11 -0
  163. package/server.js +827 -0
  164. package/smtp_client.js +504 -0
  165. package/test/.eslintrc.yaml +11 -0
  166. package/test/config/auth_flat_file.ini +5 -0
  167. package/test/config/block_me.recipient +1 -0
  168. package/test/config/block_me.senders +1 -0
  169. package/test/config/dhparams.pem +8 -0
  170. package/test/config/host_list +2 -0
  171. package/test/config/outbound_tls_cert.pem +1 -0
  172. package/test/config/outbound_tls_key.pem +1 -0
  173. package/test/config/plugins +7 -0
  174. package/test/config/smtp.ini +11 -0
  175. package/test/config/smtp_forward.ini +30 -0
  176. package/test/config/tls/example.com/_.example.com.key +28 -0
  177. package/test/config/tls/example.com/example.com.crt +25 -0
  178. package/test/config/tls/haraka.local.pem +51 -0
  179. package/test/config/tls.ini +45 -0
  180. package/test/config/tls_cert.pem +21 -0
  181. package/test/config/tls_key.pem +28 -0
  182. package/test/connection.js +820 -0
  183. package/test/fixtures/haproxy_allowed/config/connection.ini +3 -0
  184. package/test/fixtures/haproxy_disabled/config/connection.ini +3 -0
  185. package/test/fixtures/haproxy_untrusted/config/connection.ini +3 -0
  186. package/test/fixtures/line_socket.js +21 -0
  187. package/test/fixtures/todo_qfile.txt +0 -0
  188. package/test/fixtures/util_hmailitem.js +156 -0
  189. package/test/installation/config/test-plugin-flat +1 -0
  190. package/test/installation/config/test-plugin.ini +10 -0
  191. package/test/installation/config/tls.ini +1 -0
  192. package/test/installation/node_modules/load_first/index.js +5 -0
  193. package/test/installation/node_modules/load_first/package.json +11 -0
  194. package/test/installation/node_modules/test-plugin/config/test-plugin-flat +1 -0
  195. package/test/installation/node_modules/test-plugin/config/test-plugin.ini +9 -0
  196. package/test/installation/node_modules/test-plugin/package.json +5 -0
  197. package/test/installation/node_modules/test-plugin/test-plugin.js +5 -0
  198. package/test/installation/plugins/base_plugin.js +3 -0
  199. package/test/installation/plugins/folder_plugin/index.js +3 -0
  200. package/test/installation/plugins/folder_plugin/package.json +11 -0
  201. package/test/installation/plugins/inherits.js +7 -0
  202. package/test/installation/plugins/load_first.js +3 -0
  203. package/test/installation/plugins/plugin.js +1 -0
  204. package/test/installation/plugins/tls.js +3 -0
  205. package/test/logger.js +217 -0
  206. package/test/loud/config/dhparams.pem +0 -0
  207. package/test/loud/config/tls/goobered.pem +45 -0
  208. package/test/loud/config/tls.ini +43 -0
  209. package/test/mail_specimen/base64-root-part.txt +23 -0
  210. package/test/mail_specimen/varied-fold-lengths-preserve-data.txt +283 -0
  211. package/test/outbound/bounce_net_errors.js +133 -0
  212. package/test/outbound/bounce_rfc3464.js +226 -0
  213. package/test/outbound/hmail.js +210 -0
  214. package/test/outbound/index.js +385 -0
  215. package/test/outbound/qfile.js +124 -0
  216. package/test/outbound/queue.js +325 -0
  217. package/test/plugins/auth/auth_base.js +620 -0
  218. package/test/plugins/auth/auth_bridge.js +80 -0
  219. package/test/plugins/auth/auth_vpopmaild.js +81 -0
  220. package/test/plugins/auth/flat_file.js +123 -0
  221. package/test/plugins/block_me.js +141 -0
  222. package/test/plugins/data.signatures.js +111 -0
  223. package/test/plugins/delay_deny.js +262 -0
  224. package/test/plugins/prevent_credential_leaks.js +174 -0
  225. package/test/plugins/process_title.js +141 -0
  226. package/test/plugins/queue/deliver.js +98 -0
  227. package/test/plugins/queue/discard.js +78 -0
  228. package/test/plugins/queue/lmtp.js +137 -0
  229. package/test/plugins/queue/qmail-queue.js +98 -0
  230. package/test/plugins/queue/quarantine.js +80 -0
  231. package/test/plugins/queue/smtp_bridge.js +152 -0
  232. package/test/plugins/queue/smtp_forward.js +1023 -0
  233. package/test/plugins/queue/smtp_proxy.js +138 -0
  234. package/test/plugins/rcpt_to.host_list_base.js +102 -0
  235. package/test/plugins/rcpt_to.in_host_list.js +186 -0
  236. package/test/plugins/record_envelope_addresses.js +66 -0
  237. package/test/plugins/reseed_rng.js +34 -0
  238. package/test/plugins/status.js +207 -0
  239. package/test/plugins/tarpit.js +90 -0
  240. package/test/plugins/tls.js +86 -0
  241. package/test/plugins/toobusy.js +198 -0
  242. package/test/plugins/xclient.js +119 -0
  243. package/test/plugins.js +230 -0
  244. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  245. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  246. package/test/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  247. package/test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  248. package/test/queue/zero-length +0 -0
  249. package/test/server.js +1012 -0
  250. package/test/smtp_client.js +1303 -0
  251. package/test/tls_socket.js +321 -0
  252. package/test/transaction.js +554 -0
  253. package/tls_socket.js +771 -0
  254. package/transaction.js +267 -0
@@ -0,0 +1,138 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert')
4
+ const path = require('node:path')
5
+ const { describe, it, beforeEach, afterEach, before } = require('node:test')
6
+
7
+ const { makeConnection, makePlugin } = require('haraka-test-fixtures')
8
+
9
+ const tls_socket = require('../../../tls_socket')
10
+
11
+ before(() => {
12
+ require('haraka-constants').import(global)
13
+ })
14
+
15
+ describe('queue/smtp_proxy', () => {
16
+ let plugin, conn
17
+
18
+ beforeEach(() => {
19
+ plugin = makePlugin('queue/smtp_proxy', { register: false })
20
+ plugin.load_smtp_proxy_ini()
21
+ conn = makeConnection({ withTxn: true })
22
+ })
23
+
24
+ describe('hook_rset', () => {
25
+ it('calls next() when no smtp_client in notes', (t, done) => {
26
+ plugin.hook_rset((rc) => {
27
+ assert.equal(rc, undefined)
28
+ done()
29
+ }, conn)
30
+ })
31
+
32
+ it('releases smtp_client and calls next() when smtp_client exists', (t, done) => {
33
+ let released = false
34
+ conn.notes.smtp_client = {
35
+ release: () => {
36
+ released = true
37
+ },
38
+ }
39
+ plugin.hook_rset(() => {
40
+ assert.equal(released, true)
41
+ assert.equal(conn.notes.smtp_client, undefined)
42
+ done()
43
+ }, conn)
44
+ })
45
+ })
46
+
47
+ describe('hook_quit', () => {
48
+ it('calls next() when no smtp_client in notes', (t, done) => {
49
+ plugin.hook_quit((rc) => {
50
+ assert.equal(rc, undefined)
51
+ done()
52
+ }, conn)
53
+ })
54
+
55
+ it('is the same function as hook_rset', () => {
56
+ assert.equal(plugin.hook_rset, plugin.hook_quit)
57
+ })
58
+ })
59
+
60
+ describe('hook_disconnect', () => {
61
+ it('calls next() when no smtp_client in notes', (t, done) => {
62
+ plugin.hook_disconnect((rc) => {
63
+ assert.equal(rc, undefined)
64
+ done()
65
+ }, conn)
66
+ })
67
+
68
+ it('releases and calls next when smtp_client exists', (t, done) => {
69
+ let released = false
70
+ let callNextCalled = false
71
+ conn.notes.smtp_client = {
72
+ release: () => {
73
+ released = true
74
+ },
75
+ call_next: () => {
76
+ callNextCalled = true
77
+ },
78
+ }
79
+ plugin.hook_disconnect(() => {
80
+ assert.equal(released, true)
81
+ assert.equal(callNextCalled, true)
82
+ assert.equal(conn.notes.smtp_client, undefined)
83
+ done()
84
+ }, conn)
85
+ })
86
+ })
87
+
88
+ describe('hook_queue', () => {
89
+ it('calls next() when transaction is missing', (t, done) => {
90
+ const connNoTxn = makeConnection()
91
+ plugin.hook_queue((rc) => {
92
+ assert.equal(rc, undefined)
93
+ done()
94
+ }, connNoTxn)
95
+ })
96
+
97
+ it('calls next() when smtp_client is not in notes', (t, done) => {
98
+ plugin.hook_queue((rc) => {
99
+ assert.equal(rc, undefined)
100
+ done()
101
+ }, conn)
102
+ })
103
+ })
104
+
105
+ describe('tls_options', () => {
106
+ let origTlsConfig, origTlsCfg
107
+
108
+ beforeEach(() => {
109
+ origTlsConfig = tls_socket.config
110
+ origTlsCfg = tls_socket.cfg
111
+ tls_socket.config = require('haraka-config').module_config(path.resolve('test'))
112
+ tls_socket.cfg = undefined
113
+ // re-derive with test/config in scope
114
+ plugin.load_smtp_proxy_ini()
115
+ })
116
+
117
+ afterEach(() => {
118
+ tls_socket.config = origTlsConfig
119
+ tls_socket.cfg = origTlsCfg
120
+ })
121
+
122
+ it('populates tls_options from tls.ini [main]', () => {
123
+ assert.ok(plugin.tls_options)
124
+ assert.equal(plugin.tls_options.rejectUnauthorized, false)
125
+ assert.equal(plugin.tls_options.minVersion, 'TLSv1')
126
+ assert.ok(plugin.tls_options.ciphers)
127
+ assert.ok(Array.isArray(plugin.tls_options.no_tls_hosts))
128
+ assert.ok(Array.isArray(plugin.tls_options.force_tls_hosts))
129
+ })
130
+
131
+ it('reload re-derives tls_options', () => {
132
+ const first = plugin.tls_options
133
+ plugin.load_smtp_proxy_ini()
134
+ assert.ok(plugin.tls_options)
135
+ assert.notEqual(plugin.tls_options, first)
136
+ })
137
+ })
138
+ })
@@ -0,0 +1,102 @@
1
+ 'use strict'
2
+ const assert = require('node:assert/strict')
3
+ const { describe, it, beforeEach } = require('node:test')
4
+
5
+ const { Address } = require('@haraka/email-address')
6
+ const { assertResult, makeConnection, makePlugin } = require('haraka-test-fixtures')
7
+
8
+ const _set_up = () => {
9
+ this.plugin = makePlugin('rcpt_to.host_list_base', { register: false })
10
+ this.plugin.cfg = {}
11
+ this.plugin.host_list = {}
12
+
13
+ this.connection = makeConnection({ withTxn: true })
14
+ }
15
+
16
+ describe('rcpt_to.host_list_base', () => {
17
+ describe('in_host_list', () => {
18
+ beforeEach(_set_up)
19
+
20
+ it('miss', () => {
21
+ assert.equal(false, this.plugin.in_host_list('test.com'))
22
+ })
23
+
24
+ it('hit', () => {
25
+ this.plugin.host_list['test.com'] = true
26
+ assert.equal(true, this.plugin.in_host_list('test.com'))
27
+ })
28
+ })
29
+
30
+ describe('in_host_regex', () => {
31
+ beforeEach(_set_up)
32
+
33
+ const setRegex = (patterns) => {
34
+ this.plugin.host_list_regex = patterns
35
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
36
+ }
37
+
38
+ it('returns false when hl_re is not set', () => {
39
+ assert.equal(this.plugin.in_host_regex('test.com'), false)
40
+ })
41
+
42
+ const cases = [
43
+ { desc: 'miss', patterns: ['miss.com'], domain: 'test.com', expected: false },
44
+ { desc: 'exact hit', patterns: ['test.com'], domain: 'test.com', expected: true },
45
+ { desc: 're hit', patterns: ['.*est.com'], domain: 'test.com', expected: true },
46
+ ]
47
+ for (const { desc, patterns, domain, expected } of cases) {
48
+ it(desc, () => {
49
+ setRegex(patterns)
50
+ assert.equal(this.plugin.in_host_regex(domain), expected)
51
+ })
52
+ }
53
+ })
54
+
55
+ describe('hook_mail', () => {
56
+ beforeEach(_set_up)
57
+
58
+ const setRegex = (patterns) => {
59
+ this.plugin.host_list_regex = patterns
60
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
61
+ }
62
+
63
+ const callMailHook = (addr) =>
64
+ new Promise((resolve) => {
65
+ this.plugin.hook_mail((rc, msg) => resolve({ rc, msg }), this.connection, [new Address(addr)])
66
+ })
67
+
68
+ it('null sender always passes when relaying', async () => {
69
+ this.connection.relaying = true
70
+ const { rc, msg } = await callMailHook('<>')
71
+ assert.equal(rc, undefined)
72
+ assert.equal(msg, undefined)
73
+ })
74
+
75
+ it('miss: records mail_from!local in results', async () => {
76
+ this.plugin.host_list = { 'miss.com': true }
77
+ const { rc, msg } = await callMailHook('<user@example.com>')
78
+ assert.equal(rc, undefined)
79
+ assert.equal(msg, undefined)
80
+ assertResult(this.connection.transaction, 'rcpt_to.host_list_base', 'msg', /^mail_from!local$/)
81
+ })
82
+
83
+ for (const [desc, setup] of [
84
+ [
85
+ 'hit',
86
+ () => {
87
+ this.plugin.host_list = { 'example.com': true }
88
+ },
89
+ ],
90
+ ['hit, regex, exact', () => setRegex(['example.com'])],
91
+ ['hit, regex, pattern', () => setRegex(['.*mple.com'])],
92
+ ]) {
93
+ it(desc, async () => {
94
+ setup()
95
+ const { rc, msg } = await callMailHook('<user@example.com>')
96
+ assert.equal(rc, undefined)
97
+ assert.equal(msg, undefined)
98
+ assertResult(this.connection.transaction, 'rcpt_to.host_list_base', 'pass', /^mail_from$/)
99
+ })
100
+ }
101
+ })
102
+ })
@@ -0,0 +1,186 @@
1
+ 'use strict'
2
+ const assert = require('node:assert/strict')
3
+ const { describe, it, beforeEach } = require('node:test')
4
+
5
+ const { Address } = require('@haraka/email-address')
6
+ const fixtures = require('haraka-test-fixtures')
7
+ const { assertResult, makeConnection, makePlugin } = fixtures
8
+ require('haraka-constants').import(global)
9
+
10
+ const _set_up = () => {
11
+ this.plugin = makePlugin('rcpt_to.in_host_list', { register: false })
12
+ this.plugin.inherits('rcpt_to.host_list_base')
13
+ this.plugin.cfg = {}
14
+ this.plugin.host_list = {}
15
+
16
+ this.connection = makeConnection({ withTxn: true })
17
+ }
18
+
19
+ describe('in_host_list', () => {
20
+ beforeEach(_set_up)
21
+
22
+ it('miss', () => {
23
+ assert.equal(this.plugin.in_host_list('test.com'), false)
24
+ })
25
+
26
+ it('hit', () => {
27
+ this.plugin.host_list['test.com'] = true
28
+ assert.equal(this.plugin.in_host_list('test.com'), true)
29
+ })
30
+
31
+ describe('in_host_regex', () => {
32
+ beforeEach(_set_up)
33
+
34
+ const setRegex = (patterns) => {
35
+ this.plugin.host_list_regex = patterns
36
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
37
+ }
38
+
39
+ it('returns false when hl_re is not set', () => {
40
+ assert.equal(this.plugin.in_host_regex('test.com'), false)
41
+ })
42
+
43
+ const cases = [
44
+ { desc: 'miss', patterns: ['miss.com'], domain: 'test.com', expected: false },
45
+ { desc: 'exact hit', patterns: ['test.com'], domain: 'test.com', expected: true },
46
+ { desc: 're hit', patterns: ['.*est.com'], domain: 'test.com', expected: true },
47
+ ]
48
+ for (const { desc, patterns, domain, expected } of cases) {
49
+ it(desc, () => {
50
+ setRegex(patterns)
51
+ assert.equal(this.plugin.in_host_regex(domain), expected)
52
+ })
53
+ }
54
+ })
55
+
56
+ describe('hook_mail', () => {
57
+ beforeEach(_set_up)
58
+
59
+ const setRegex = (patterns) => {
60
+ this.plugin.host_list_regex = patterns
61
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
62
+ }
63
+
64
+ const callMailHook = (addr) =>
65
+ new Promise((resolve) => {
66
+ this.plugin.hook_mail((rc, msg) => resolve({ rc, msg }), this.connection, [new Address(addr)])
67
+ })
68
+
69
+ it('null sender always passes when relaying', async () => {
70
+ this.connection.relaying = true
71
+ const { rc, msg } = await callMailHook('<>')
72
+ assert.equal(rc, undefined)
73
+ assert.equal(msg, undefined)
74
+ })
75
+
76
+ it('miss: records mail_from!local in results', async () => {
77
+ this.plugin.host_list = { 'miss.com': true }
78
+ const { rc, msg } = await callMailHook('<user@example.com>')
79
+ assert.equal(rc, undefined)
80
+ assert.equal(msg, undefined)
81
+ assertResult(this.connection.transaction, 'rcpt_to.in_host_list', 'msg', /^mail_from!local$/)
82
+ })
83
+
84
+ for (const [desc, setup] of [
85
+ [
86
+ 'hit',
87
+ () => {
88
+ this.plugin.host_list = { 'example.com': true }
89
+ },
90
+ ],
91
+ ['hit, regex, exact', () => setRegex(['example.com'])],
92
+ ['hit, regex, pattern', () => setRegex(['.*mple.com'])],
93
+ ]) {
94
+ it(desc, async () => {
95
+ setup()
96
+ const { rc, msg } = await callMailHook('<user@example.com>')
97
+ assert.equal(rc, undefined)
98
+ assert.equal(msg, undefined)
99
+ assertResult(this.connection.transaction, 'rcpt_to.in_host_list', 'pass', /^mail_from$/)
100
+ })
101
+ }
102
+ })
103
+
104
+ describe('hook_rcpt', () => {
105
+ beforeEach(_set_up)
106
+
107
+ const setRegex = (patterns) => {
108
+ this.plugin.host_list_regex = patterns
109
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
110
+ }
111
+
112
+ const callRcptHook = (addr) =>
113
+ new Promise((resolve) => {
114
+ this.plugin.hook_rcpt((rc, msg) => resolve({ rc, msg }), this.connection, [new Address(addr)])
115
+ })
116
+
117
+ it('missing txn: does not call next (returns early)', () => {
118
+ // When transaction is missing the plugin returns without calling next()
119
+ delete this.connection.transaction
120
+ let nextCalled = false
121
+ this.plugin.hook_rcpt(
122
+ () => {
123
+ nextCalled = true
124
+ },
125
+ this.connection,
126
+ [new Address('test@test.com')],
127
+ )
128
+ assert.equal(nextCalled, false)
129
+ })
130
+
131
+ const cases = [
132
+ {
133
+ desc: 'hit list',
134
+ setup: () => {
135
+ this.plugin.host_list = { 'test.com': true }
136
+ },
137
+ addr: 'test@test.com',
138
+ expectedRC: OK,
139
+ },
140
+ {
141
+ desc: 'miss list',
142
+ setup: () => {
143
+ this.plugin.host_list = { 'miss.com': true }
144
+ },
145
+ addr: 'test@test.com',
146
+ expectedRC: undefined,
147
+ },
148
+ {
149
+ desc: 'hit regex, exact',
150
+ setup: () => setRegex(['test.com']),
151
+ addr: 'test@test.com',
152
+ expectedRC: OK,
153
+ },
154
+ {
155
+ desc: 'hit regex, pattern',
156
+ setup: () => setRegex(['.est.com']),
157
+ addr: 'test@test.com',
158
+ expectedRC: OK,
159
+ },
160
+ {
161
+ desc: 'miss regex, pattern',
162
+ setup: () => setRegex(['a.est.com']),
163
+ addr: 'test@test.com',
164
+ expectedRC: undefined,
165
+ },
166
+ {
167
+ desc: 'rcpt miss, relaying to local sender',
168
+ setup: () => {
169
+ this.connection.relaying = true
170
+ this.connection.transaction.notes = { local_sender: true }
171
+ },
172
+ addr: 'test@test.com',
173
+ expectedRC: OK,
174
+ },
175
+ ]
176
+
177
+ for (const { desc, setup, addr, expectedRC } of cases) {
178
+ it(desc, async () => {
179
+ setup()
180
+ const { rc, msg } = await callRcptHook(addr)
181
+ assert.equal(rc, expectedRC)
182
+ assert.equal(msg, undefined)
183
+ })
184
+ }
185
+ })
186
+ })
@@ -0,0 +1,66 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert')
4
+ const { describe, it, beforeEach } = require('node:test')
5
+
6
+ const { callMail, callRcpt, makeConnection, makePlugin } = require('haraka-test-fixtures')
7
+
8
+ const _set_up = () => {
9
+ this.plugin = makePlugin('record_envelope_addresses')
10
+ this.connection = makeConnection({ withTxn: true })
11
+ }
12
+
13
+ describe('record_envelope_addresses', () => {
14
+ beforeEach(_set_up)
15
+
16
+ describe('hook_mail', () => {
17
+ it('adds X-Envelope-From header from MAIL FROM address', (t, done) => {
18
+ callMail(this.plugin, this.connection, 'sender@example.com').then(() => {
19
+ const vals = this.connection.transaction.header.get_all('X-Envelope-From')
20
+ assert.equal(vals.length, 1, 'header was added')
21
+ assert.equal(vals[0], 'sender@example.com')
22
+ done()
23
+ })
24
+ })
25
+
26
+ it('does not throw when connection has no transaction', (t, done) => {
27
+ this.connection.transaction = null
28
+ callMail(this.plugin, this.connection, 'sender@example.com').then(() => {
29
+ assert.ok(true, 'next was called without error')
30
+ done()
31
+ })
32
+ })
33
+ })
34
+
35
+ describe('hook_rcpt', () => {
36
+ it('adds X-Envelope-To header from RCPT TO address', (t, done) => {
37
+ callRcpt(this.plugin, this.connection, 'rcpt@example.com').then(() => {
38
+ const vals = this.connection.transaction.header.get_all('X-Envelope-To')
39
+ assert.equal(vals.length, 1, 'header was added')
40
+ assert.equal(vals[0], 'rcpt@example.com')
41
+ done()
42
+ })
43
+ })
44
+
45
+ it('adds X-Envelope-To header for each recipient', (t, done) => {
46
+ Promise.all([
47
+ callRcpt(this.plugin, this.connection, 'one@example.com'),
48
+ callRcpt(this.plugin, this.connection, 'two@example.com'),
49
+ ]).then(() => {
50
+ const vals = this.connection.transaction.header.get_all('X-Envelope-To')
51
+ assert.equal(vals.length, 2, 'two headers added')
52
+ assert.equal(vals[0], 'one@example.com')
53
+ assert.equal(vals[1], 'two@example.com')
54
+ done()
55
+ })
56
+ })
57
+
58
+ it('does not throw when connection has no transaction', (t, done) => {
59
+ this.connection.transaction = null
60
+ callRcpt(this.plugin, this.connection, 'rcpt@example.com').then(() => {
61
+ assert.ok(true, 'next was called without error')
62
+ done()
63
+ })
64
+ })
65
+ })
66
+ })
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert/strict')
4
+ const { describe, it } = require('node:test')
5
+
6
+ const { makePlugin } = require('haraka-test-fixtures')
7
+
8
+ describe('reseed_rng', () => {
9
+ describe('hook_init_child', () => {
10
+ it('calls Math.seedrandom with a hex string and calls next', (t, done) => {
11
+ const plugin = makePlugin('reseed_rng', { register: false })
12
+ let called = false
13
+ let calledArg
14
+ Math.seedrandom = (arg) => {
15
+ called = true
16
+ calledArg = arg
17
+ }
18
+ plugin.hook_init_child((rc) => {
19
+ delete Math.seedrandom
20
+ assert.equal(rc, undefined)
21
+ assert.ok(called, 'Math.seedrandom should have been called')
22
+ assert.equal(typeof calledArg, 'string')
23
+ assert.ok(calledArg.length > 0)
24
+ done()
25
+ })
26
+ })
27
+
28
+ it('throws when Math.seedrandom is not defined', () => {
29
+ const plugin = makePlugin('reseed_rng', { register: false })
30
+ delete Math.seedrandom
31
+ assert.throws(() => plugin.hook_init_child(() => {}), /Math\.seedrandom is not a function/)
32
+ })
33
+ })
34
+ })