haraka-plugin-spamassassin 1.0.2 → 1.0.4

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
@@ -4,6 +4,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
4
4
 
5
5
  ### Unreleased
6
6
 
7
+ ### [1.0.4] - 2026-05-10
8
+
9
+ - fix: cleanup message pipe if spamd errors
10
+ - ci: updated configs
11
+ - deps: bumped all versions to latest
12
+
13
+ ### [1.0.3] - 2025-02-06
14
+
15
+ - results: tidying up duplicate data
16
+ - results.hits: deleted, alias for score
17
+ - results.status: deleted, alias for flag
18
+ - results.flag: change from Yes/No to boolean
19
+
7
20
  ### [1.0.2] - 2025-01-26
8
21
 
9
22
  - prettier: move config into package.json
@@ -16,6 +29,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
16
29
 
17
30
  - repackaged from haraka/Haraka
18
31
 
19
- [1.0.1]: https://github.com/haraka/haraka-plugin-spamassassin/releases/tag/v1.0.1
32
+ [1.0.1]: https://github.com/haraka/haraka-plugin-spamassassin/releases/tag/1.0.1
20
33
  [1.0.2]: https://github.com/haraka/haraka-plugin-spamassassin/releases/tag/v1.0.2
21
34
  [1.0.0]: https://github.com/haraka/haraka-plugin-spamassassin/releases/tag/1.0.0
35
+ [1.0.3]: https://github.com/haraka/haraka-plugin-spamassassin/releases/tag/v1.0.3
36
+ [1.0.4]: https://github.com/haraka/haraka-plugin-spamassassin/releases/tag/v1.0.4
package/README.md CHANGED
@@ -178,7 +178,6 @@ Other headers options you might find interesting or useful are:
178
178
  add_header all DCC _DCCB_: _DCCR_
179
179
  add_header all Tests _TESTS_
180
180
 
181
- ## USAGE
182
181
 
183
182
  <!-- leave these buried at the bottom of the document -->
184
183
 
package/index.js CHANGED
@@ -82,11 +82,9 @@ exports.hook_data_post = function (next, connection) {
82
82
  /Spam: (True|False) ; (-?\d+\.\d) \/ (-?\d+\.\d)/,
83
83
  )
84
84
  if (matches) {
85
- spamd_response.flag = matches[1]
85
+ spamd_response.flag = matches[1] === 'True'
86
86
  spamd_response.score = matches[2]
87
- spamd_response.hits = matches[2] // backwards compat
88
87
  spamd_response.reqd = matches[3]
89
- spamd_response.flag = spamd_response.flag === 'True' ? 'Yes' : 'No'
90
88
  }
91
89
  } else {
92
90
  state = 'headers'
@@ -131,7 +129,6 @@ exports.hook_data_post = function (next, connection) {
131
129
  txn.notes.spamassassin = spamd_response
132
130
  connection.results.add(this, {
133
131
  time: (Date.now() - start) / 1000,
134
- hits: spamd_response.hits,
135
132
  flag: spamd_response.flag,
136
133
  })
137
134
 
@@ -140,11 +137,11 @@ exports.hook_data_post = function (next, connection) {
140
137
  this.log_results(connection, spamd_response)
141
138
 
142
139
  const exceeds_err = this.score_too_high(connection, spamd_response)
143
- if (exceeds_err) return next(DENY, exceeds_err)
140
+ if (exceeds_err) return socket.nextOnce(DENY, exceeds_err)
144
141
 
145
142
  this.munge_subject(connection, spamd_response.score)
146
143
 
147
- next()
144
+ socket.nextOnce()
148
145
  })
149
146
  }
150
147
 
@@ -197,7 +194,7 @@ exports.munge_subject = function (conn, score) {
197
194
  }
198
195
 
199
196
  exports.do_header_updates = function (conn, spamd_response) {
200
- if (spamd_response.flag === 'Yes') {
197
+ if (spamd_response.flag) {
201
198
  // X-Spam-Flag is added by SpamAssassin
202
199
  conn.transaction.remove_header('precedence')
203
200
  conn.transaction.add_header('Precedence', 'junk')
@@ -285,6 +282,19 @@ exports.get_spamd_socket = function (next, conn, headers) {
285
282
  net_utils.add_line_processor(socket)
286
283
  const results_timeout = parseInt(plugin.cfg.main.results_timeout) || 300
287
284
 
285
+ // Idempotent terminal handler; exposed on the socket so hook_data_post's
286
+ // 'end' handler shares the guard. unpipe() before destroy() — see
287
+ // haraka/message-stream#22.
288
+ let calledNext = false
289
+ socket.nextOnce = function (code, msg) {
290
+ if (txn?.message_stream) txn.message_stream.unpipe()
291
+ if (!socket.destroyed) socket.destroy()
292
+ if (calledNext) return
293
+ calledNext = true
294
+ if (code) return next(code, msg)
295
+ return next()
296
+ }
297
+
288
298
  socket.on('connect', function () {
289
299
  // Abort if the transaction is gone
290
300
  if (!txn) {
@@ -301,24 +311,23 @@ exports.get_spamd_socket = function (next, conn, headers) {
301
311
  })
302
312
 
303
313
  socket.on('error', (err) => {
304
- socket.destroy()
305
314
  if (txn) txn.results.add(plugin, { err: `socket error: ${err.message}` })
306
- if (plugin.cfg.defer.error) return next(DENYSOFT, 'spamd scan error')
307
- return next()
315
+ if (plugin.cfg.defer.error)
316
+ return socket.nextOnce(DENYSOFT, 'spamd scan error')
317
+ return socket.nextOnce()
308
318
  })
309
319
 
310
320
  socket.on('timeout', function () {
311
- socket.destroy()
312
321
  if (!this.is_connected) {
313
322
  if (txn) txn.results.add(plugin, { err: `socket connect timeout` })
314
323
  if (plugin.cfg.defer.connect_timeout)
315
- return next(DENYSOFT, 'spamd connect timeout')
324
+ return socket.nextOnce(DENYSOFT, 'spamd connect timeout')
316
325
  } else {
317
326
  if (txn) txn.results.add(plugin, { err: `timeout waiting for results` })
318
327
  if (plugin.cfg.defer.scan_timeout)
319
- return next(DENYSOFT, 'spamd scan timeout')
328
+ return socket.nextOnce(DENYSOFT, 'spamd scan timeout')
320
329
  }
321
- return next()
330
+ return socket.nextOnce()
322
331
  })
323
332
 
324
333
  const connect_timeout = parseInt(plugin.cfg.main.connect_timeout) || 30
@@ -342,7 +351,7 @@ exports.log_results = function (conn, spamd_response) {
342
351
  : cfg.reject_threshold
343
352
 
344
353
  const human_text =
345
- `status=${spamd_response.flag}` +
354
+ `status=${spamd_response.flag ? 'Yes' : 'No'}` +
346
355
  `, score=${spamd_response.score}` +
347
356
  `, required=${spamd_response.reqd}` +
348
357
  `, reject=${reject_threshold}` +
@@ -350,7 +359,6 @@ exports.log_results = function (conn, spamd_response) {
350
359
 
351
360
  conn.transaction.results.add(this, {
352
361
  human: human_text,
353
- status: spamd_response.flag,
354
362
  score: parseFloat(spamd_response.score),
355
363
  required: parseFloat(spamd_response.reqd),
356
364
  reject: reject_threshold,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "haraka-plugin-spamassassin",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Haraka plugin that scans messages with SpamAssassin",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -14,8 +14,9 @@
14
14
  "prettier": "npx prettier . --check",
15
15
  "prettier:fix": "npx prettier . --write --log-level=warn",
16
16
  "test": "node --test",
17
- "versions": "npx dependency-version-checker check",
18
- "versions:fix": "npx dependency-version-checker update"
17
+ "versions": "npx npm-dep-mgr check",
18
+ "versions:fix": "npx npm-dep-mgr update",
19
+ "test:coverage": "npx c8 --reporter=text --reporter=text-summary npm test"
19
20
  },
20
21
  "repository": {
21
22
  "type": "git",
@@ -33,13 +34,13 @@
33
34
  },
34
35
  "homepage": "https://github.com/haraka/haraka-plugin-spamassassin#readme",
35
36
  "dependencies": {
36
- "haraka-net-utils": "^1.7.1",
37
- "haraka-utils": "^1.1.3"
37
+ "haraka-net-utils": "^1.8.1",
38
+ "haraka-utils": "^1.1.4"
38
39
  },
39
40
  "devDependencies": {
40
- "@haraka/eslint-config": "^2.0.2",
41
- "address-rfc2821": "^2.1.2",
42
- "haraka-test-fixtures": "1.3.8"
41
+ "@haraka/eslint-config": "^2.0.4",
42
+ "address-rfc2821": "^2.1.5",
43
+ "haraka-test-fixtures": "^1.4.3"
43
44
  },
44
45
  "prettier": {
45
46
  "singleQuote": true,