• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

haraka / Haraka / 26427888890

26 May 2026 01:56AM UTC coverage: 73.353% (-0.3%) from 73.64%
26427888890

Pull #3577

github

web-flow
Merge d6883ed79 into c4173efb8
Pull Request #3577: Implicit TLS with Proxy Protocol

1807 of 2349 branches covered (76.93%)

186 of 220 new or added lines in 2 files covered. (84.55%)

93 existing lines in 7 files now uncovered.

8115 of 11063 relevant lines covered (73.35%)

24.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.3
/plugins.js
1
'use strict'
17✔
2
// load all defined plugins
17✔
3

17✔
4
// node built-ins
17✔
5
const fs = require('node:fs')
17✔
6
const path = require('node:path')
17✔
7
const vm = require('node:vm')
17✔
8

17✔
9
// npm modules
17✔
10
exports.config = require('haraka-config')
17✔
11
const constants = require('haraka-constants')
17✔
12

17✔
13
// local modules
17✔
14
const logger = require('./logger')
17✔
15

17✔
16
exports.registered_hooks = {}
17✔
17
exports.registered_plugins = {}
17✔
18
exports.plugin_list = []
17✔
19

17✔
20
let order = 0
17✔
21

17✔
22
class Plugin {
17✔
23
    constructor(name) {
17✔
24
        this.name = name
52✔
25
        this.base = {}
52✔
26
        this.timeout = get_timeout(name)
52✔
27
        this._get_plugin_path()
52✔
28
        this.config = this._get_config()
52✔
29
        this.hooks = {}
52✔
30
    }
52✔
31

17✔
32
    haraka_require(name) {
17✔
33
        return require(`./${name}`)
×
34
    }
×
35

17✔
36
    _get_plugin_path() {
17✔
37
        /* From https://github.com/haraka/Haraka/pull/1278#issuecomment-168856528
52✔
38
        In Development mode, or install via a plain "git clone":
52✔
39

52✔
40
            Plain plugin in plugins/ folder
52✔
41
            Plugin in a folder in plugins/<name>/ folder. Contains a package.json.
52✔
42
            Plugin in node_modules. Contains a package.json file.
52✔
43

52✔
44
        In "installed" mode (via haraka -i <path>):
52✔
45

52✔
46
            Plain plugin in <path>/plugins/ folder
52✔
47
            Plugin in a folder in <path>/plugins/<name>/folder. (same concept as above)
52✔
48
            Plugin in <path>/node_modules. Contains a package.json file.
52✔
49
            Core plugin in <core_haraka_dir>/plugins/ folder
52✔
50
            Plugin in a folder in <core_haraka_dir>/plugins/<name>/ folder. (same concept as above)
52✔
51
            Plugin in <core_haraka_dir>/node_modules.
52✔
52
        */
52✔
53

52✔
54
        this.hasPackageJson = false
52✔
55
        const name = this.name.startsWith('haraka-plugin-') ? this.name.slice(14) : this.name
52✔
56
        if (this.name !== name) this.name = name
52✔
57

52✔
58
        let paths = []
52✔
59
        if (process.env.HARAKA) {
52✔
60
            // Installed mode - started via bin/haraka
8✔
61
            paths = [...paths, ...plugin_search_paths(process.env.HARAKA, name)]
8✔
62

8✔
63
            // permit local "folder" plugins (/$name/package.json) (see #1649)
8✔
64
            paths.push(
8✔
65
                path.resolve(process.env.HARAKA, 'plugins', name, 'package.json'),
8✔
66
                path.resolve(process.env.HARAKA, 'node_modules', name, 'package.json'),
8✔
67
            )
8✔
68
        }
8✔
69

52✔
70
        // development mode
52✔
71
        paths = [...paths, ...plugin_search_paths(__dirname, name)]
52✔
72
        for (const pp of paths) {
52✔
73
            if (this.plugin_path) continue
196✔
74
            try {
74✔
75
                fs.statSync(pp)
74✔
76
                this.plugin_path = pp
74✔
77
                if (path.basename(pp) === 'package.json') {
196✔
78
                    this.hasPackageJson = true
4✔
79
                }
4✔
80
            } catch (ignore) {}
196✔
81
        }
196✔
82
    }
52✔
83

17✔
84
    _get_config() {
17✔
85
        if (this.hasPackageJson) {
52✔
86
            // It's a package/folder plugin - look in plugin folder for defaults,
4✔
87
            // haraka/config folder for overrides
4✔
88
            return exports.config.module_config(path.dirname(this.plugin_path), process.env.HARAKA || __dirname)
4✔
89
        }
4✔
90

48✔
91
        if (process.env.HARAKA) {
52✔
92
            // Plain .js file, installed mode - look in core folder for defaults,
5✔
93
            // install dir for overrides
5✔
94
            return exports.config.module_config(__dirname, process.env.HARAKA)
5✔
95
        }
5✔
96

43✔
97
        if (process.env.HARAKA_TEST_DIR) {
52✔
98
            return exports.config.module_config(process.env.HARAKA_TEST_DIR)
36✔
99
        }
36✔
100

7✔
101
        // Plain .js file, git mode - just look in this folder
7✔
102
        return exports.config.module_config(__dirname)
7✔
103
    }
52✔
104

17✔
105
    register_hook(hook_name, method_name, priority) {
17✔
106
        priority = parseInt(priority)
60✔
107
        if (!priority) priority = 0
60✔
108
        if (priority > 100) priority = 100
60!
109
        if (priority < -100) priority = -100
60!
110

60✔
111
        if (!Array.isArray(exports.registered_hooks[hook_name])) {
60✔
112
            exports.registered_hooks[hook_name] = []
42✔
113
        }
42✔
114
        exports.registered_hooks[hook_name].push({
60✔
115
            plugin: this.name,
60✔
116
            method: method_name,
60✔
117
            priority,
60✔
118
            timeout: this.timeout,
60✔
119
            order: order++,
60✔
120
        })
60✔
121
        this.hooks[hook_name] = this.hooks[hook_name] || []
60✔
122
        this.hooks[hook_name].push(method_name)
60✔
123

60✔
124
        plugins.logdebug(`registered hook ${hook_name} to ${this.name}.${method_name} priority ${priority}`)
60✔
125
    }
60✔
126

17✔
127
    register() {} // noop
17✔
128

17✔
129
    inherits(parent_name) {
17✔
130
        const parent_plugin = plugins._load_and_compile_plugin(parent_name)
13✔
131
        for (const method in parent_plugin) {
13✔
132
            if (!this[method]) {
324✔
133
                this[method] = parent_plugin[method]
104✔
134
            }
104✔
135
        }
324✔
136
        if (parent_plugin.register) {
13✔
137
            parent_plugin.register.call(this)
13✔
138
        }
13✔
139
        this.base[parent_name] = parent_plugin
13✔
140
    }
13✔
141

17✔
142
    _make_custom_require() {
17✔
143
        return (module) => {
41✔
144
            if (this.hasPackageJson) {
32✔
145
                const mod = require(module)
2✔
146
                constants.import(global)
2✔
147
                global.server = plugins.server
2✔
148
                return mod
2✔
149
            }
2✔
150

30✔
151
            if (module === './config') {
30!
152
                return this.config
×
153
            }
×
154

30✔
155
            if (!/^\./.test(module)) {
30✔
156
                return require(module)
24✔
157
            }
24✔
158

6✔
159
            for (const ext of [`${module}.js`, module]) {
30✔
160
                if (fs.existsSync(path.join(__dirname, ext))) {
12!
161
                    return require(module)
×
162
                }
×
163
            }
12✔
164

6✔
165
            return require(path.join(path.dirname(this.plugin_path), module))
6✔
166
        }
32✔
167
    }
41✔
168

17✔
169
    _get_code(pi_path) {
17✔
170
        if (this.hasPackageJson) {
41✔
171
            let packageDir = path.dirname(pi_path)
2✔
172
            if (/^win(32|64)/.test(process.platform)) {
2!
UNCOV
173
                // escape the c:\path\back\slashes else they disappear
×
UNCOV
174
                packageDir = packageDir.replace(/\\/g, '\\\\')
×
UNCOV
175
            }
×
176
            return `var _p = require("${packageDir}"); for (var k in _p) { exports[k] = _p[k] }`
2✔
177
        }
2✔
178

39✔
179
        try {
39✔
180
            return `"use strict";${fs.readFileSync(pi_path)}`
39✔
181
        } catch (err) {
41!
182
            if (exports.config.get('smtp.ini').main.ignore_bad_plugins) {
×
183
                plugins.logcrit(`Loading ${this.name} failed: ${err}`)
×
184
                return
×
185
            }
×
186
            throw `Loading plugin ${this.name} failed: ${err}`
×
187
        }
×
188
    }
41✔
189

17✔
190
    _compile() {
17✔
191
        const pp = this.plugin_path
41✔
192
        const code = this._get_code(pp)
41✔
193
        if (!code) return
41!
194

41✔
195
        const sandbox = {
41✔
196
            require: this._make_custom_require(),
41✔
197
            __filename: pp,
41✔
198
            __dirname: path.dirname(pp),
41✔
199
            exports: this,
41✔
200
            setTimeout,
41✔
201
            clearTimeout,
41✔
202
            setInterval,
41✔
203
            clearInterval,
41✔
204
            process,
41✔
205
            Buffer,
41✔
206
            Math,
41✔
207
            server: plugins.server,
41✔
208
            setImmediate,
41✔
209
        }
41✔
210
        if (this.hasPackageJson) {
41✔
211
            delete sandbox.__filename
2✔
212
        }
2✔
213
        constants.import(sandbox)
41✔
214
        try {
41✔
215
            vm.runInNewContext(code, sandbox, pp)
41✔
216
        } catch (err) {
41!
217
            plugins.logcrit(`compiling '${this.name}' failed`)
×
218
            if (exports.config.get('smtp.ini').main.ignore_bad_plugins) {
×
219
                plugins.logcrit(`Loading '${this.name}' failed: ${err.message} - skipping`)
×
220
                return
×
221
            }
×
222
            throw err // default is to re-throw and stop Haraka
×
223
        }
×
224
    }
41✔
225
}
17✔
226

17✔
227
exports.shutdown_plugins = () => {
17✔
228
    for (const i in exports.registered_plugins) {
×
229
        if (exports.registered_plugins[i].shutdown) {
×
230
            exports.registered_plugins[i].shutdown()
×
231
        }
×
232
    }
×
233
}
×
234

17✔
235
process.on('message', (msg) => {
17✔
236
    if (msg.event && msg.event == 'plugins.shutdown') {
×
237
        plugins.loginfo('Shutting down')
×
238
        exports.shutdown_plugins()
×
239
    }
×
240
})
17✔
241

17✔
242
function plugin_search_paths(prefix, name) {
60✔
243
    return [
60✔
244
        // Haraka/plugins/*.js
60✔
245
        path.resolve(prefix, 'plugins', `${name}.js`),
60✔
246

60✔
247
        // Haraka/node_modules/haraka-plugin-*/package.json
60✔
248
        path.resolve(prefix, 'node_modules', `haraka-plugin-${name}`, 'package.json'),
60✔
249

60✔
250
        // global node_modules/haraka-plugin-*/package.json
60✔
251
        path.resolve(prefix, '..', `haraka-plugin-${name}`, 'package.json'),
60✔
252
    ]
60✔
253
}
60✔
254

17✔
255
function get_timeout(name) {
52✔
256
    let timeout = parseFloat(exports.config.get(`${name}.timeout`))
52✔
257
    if (isNaN(timeout)) {
52✔
258
        plugins.logdebug(`no timeout in ${name}.timeout`)
49✔
259
        timeout = parseFloat(exports.config.get('plugin_timeout'))
49✔
260
    }
49✔
261
    if (isNaN(timeout)) {
52✔
262
        plugins.logdebug('no timeout in plugin_timeout')
49✔
263
        timeout = 30
49✔
264
    }
49✔
265

52✔
266
    plugins.logdebug(`plugin ${name} timeout is: ${timeout}s`)
52✔
267
    return timeout
52✔
268
}
52✔
269

17✔
270
logger.add_log_methods(Plugin)
17✔
271

17✔
272
const plugins = exports
17✔
273

17✔
274
logger.add_log_methods(plugins, 'plugins')
17✔
275

17✔
276
plugins.Plugin = Plugin
17✔
277

17✔
278
plugins.load_plugins = (override) => {
17✔
279
    plugins.logdebug('Loading')
6✔
280
    let plugin_list
6✔
281
    if (override) {
6!
282
        if (!Array.isArray(override)) override = [override]
×
283
        plugin_list = override
×
284
    } else {
6✔
285
        plugin_list = exports.config.get('plugins', 'list')
6✔
286
    }
6✔
287

6✔
288
    for (let plugin of plugin_list) {
6✔
289
        if (plugin.startsWith('haraka-plugin-')) plugin = plugin.substring(14)
24!
290
        if (plugins.deprecated[plugin]) {
24!
291
            plugins.lognotice(
×
292
                `${plugin} has been replaced by '${plugins.deprecated[plugin]}'. Please update config/plugins`,
×
293
            )
×
294
            plugins.load_plugin(plugins.deprecated[plugin])
×
295
        } else {
24✔
296
            plugins.load_plugin(plugin)
24✔
297
        }
24✔
298
    }
24✔
299

6✔
300
    plugins.plugin_list = Object.keys(plugins.registered_plugins)
6✔
301

6✔
302
    // Sort registered_hooks by priority
6✔
303
    for (const hook of Object.keys(plugins.registered_hooks)) {
6✔
304
        plugins.registered_hooks[hook].sort((a, b) => {
42✔
305
            if (a.priority < b.priority) return -1
18!
306
            if (a.priority > b.priority) return 1
18!
307
            if (a.priority == b.priority) {
18✔
308
                if (a.order > b.order) return 1
18✔
309
                return -1
×
310
            }
×
311
            return 0
×
312
        })
42✔
313
    }
42✔
314

6✔
315
    logger.dump_logs() // now logging plugins are loaded.
6✔
316
}
6✔
317

17✔
318
plugins.deprecated = {
17✔
319
    'auth/auth_ldap': 'auth-ldap',
17✔
320
    backscatterer: 'dns-list',
17✔
321
    'connect.asn': 'asn',
17✔
322
    'connect.fcrdns': 'fcrdns',
17✔
323
    'connect.geoip': 'geoip',
17✔
324
    'connect.p0f': 'p0f',
17✔
325
    'connect.rdns_access': 'access',
17✔
326
    'data.nomsgid': 'headers',
17✔
327
    'data.noreceived': 'headers',
17✔
328
    'data.rfc5322_header_checks': 'headers',
17✔
329
    'data.headers': 'headers',
17✔
330
    dkim_sign: 'dkim',
17✔
331
    dkim_verify: 'dkim',
17✔
332
    'data.uribl': 'uribl',
17✔
333
    dnsbl: 'dns-list',
17✔
334
    dnswl: 'dns-list',
17✔
335
    'log.syslog': 'syslog',
17✔
336
    'mail_from.access': 'access',
17✔
337
    'mail_from.blocklist': 'access',
17✔
338
    'mail_from.nobounces': 'bounce',
17✔
339
    max_unrecognized_commands: 'limit',
17✔
340
    rate_limit: 'limit',
17✔
341
    'rcpt_to.access': 'access',
17✔
342
    'rcpt_to.blocklist': 'access',
17✔
343
    'rcpt_to.ldap': 'rcpt-ldap',
17✔
344
    'rcpt_to.max_count': 'limit',
17✔
345
    'rcpt_to.qmail_deliverable': 'qmail-deliverable',
17✔
346
    'rdns.regexp': 'access',
17✔
347
    relay_acl: 'relay',
17✔
348
    relay_all: 'relay',
17✔
349
    relay_force_routing: 'relay',
17✔
350
}
17✔
351

17✔
352
plugins.load_plugin = (name) => {
17✔
353
    plugins.loginfo(`loading ${name}`)
24✔
354

24✔
355
    const plugin = plugins._load_and_compile_plugin(name)
24✔
356
    if (plugin) {
24✔
357
        plugins._register_plugin(plugin)
24✔
358
    }
24✔
359

24✔
360
    plugins.registered_plugins[name] = plugin
24✔
361
}
24✔
362

17✔
363
// Set in server.js; initialized to empty object
17✔
364
// to prevent it from blowing up any unit tests.
17✔
365
plugins.server = { notes: {} }
17✔
366

17✔
367
plugins._load_and_compile_plugin = (name) => {
17✔
368
    const plugin = new Plugin(name)
37✔
369
    if (!plugin.plugin_path) {
37!
370
        const err = `Loading plugin ${plugin.name} failed: No plugin with this name found`
×
371
        if (exports.config.get('smtp.ini').main.ignore_bad_plugins) {
×
372
            plugins.logcrit(err)
×
373
            return
×
374
        }
×
375
        throw err
×
376
    }
×
377
    plugin._compile()
37✔
378
    return plugin
37✔
379
}
37✔
380

17✔
381
plugins._register_plugin = (plugin) => {
17✔
382
    plugin.register()
24✔
383

24✔
384
    // register any hook_blah methods.
24✔
385
    for (const method in plugin) {
24✔
386
        const result = method.match(/^hook_(\w+)\b/)
618✔
387
        if (result) {
618✔
388
            plugin.register_hook(result[1], method)
30✔
389
        }
30✔
390
    }
618✔
391
}
24✔
392

17✔
393
plugins.run_hooks = (hook, object, params) => {
17✔
394
    if (client_disconnected(object) && !is_required_hook(hook)) {
276!
395
        object.logdebug(`aborting ${hook} hook`)
×
396
        return
×
397
    }
×
398

276✔
399
    if (hook !== 'log') object.logdebug(`running ${hook} hooks`)
276✔
400

276✔
401
    if (is_required_hook(hook) && object.current_hook) {
276✔
402
        object.current_hook[2]() // call cancel function
1✔
403
    }
1✔
404

276✔
405
    if (!is_required_hook(hook) && hook !== 'deny' && object.hooks_to_run && object.hooks_to_run.length) {
276!
406
        throw new Error('We are already running hooks! Fatal error!')
×
407
    }
×
408

276✔
409
    if (hook === 'deny') {
276!
410
        // Save the hooks_to_run list so that we can run any remaining
×
411
        // plugins on the previous hook once this hook is complete.
×
412
        object.saved_hooks_to_run = object.hooks_to_run
×
413
    }
×
414
    object.hooks_to_run = []
276✔
415

276✔
416
    if (plugins.registered_hooks[hook]) {
276✔
417
        for (const item of plugins.registered_hooks[hook]) {
20✔
418
            const plugin = plugins.registered_plugins[item.plugin]
33✔
419
            object.hooks_to_run.push([plugin, item.method])
33✔
420
        }
33✔
421
    }
20✔
422

276✔
423
    plugins.run_next_hook(hook, object, params)
276✔
424
}
276✔
425

17✔
426
plugins.run_next_hook = (hook, object, params) => {
17✔
427
    if (client_disconnected(object) && !is_required_hook(hook)) {
288!
428
        object.logdebug(`aborting ${hook} hook`)
×
429
        return
×
430
    }
×
431
    let called_once = false
288✔
432
    let timeout_id
288✔
433
    let timed_out = false
288✔
434
    let cancelled = false
288✔
435
    let item
288✔
436

288✔
437
    function cancel() {
288✔
438
        if (timeout_id) clearTimeout(timeout_id)
1✔
439
        cancelled = true
1✔
440
    }
1✔
441
    function callback(retval, msg) {
288✔
442
        if (timeout_id) clearTimeout(timeout_id)
288✔
443
        object.current_hook = null
288✔
444
        if (cancelled) return // This hook has been cancelled
288✔
445

287✔
446
        // Bail if client has disconnected
287✔
447
        if (client_disconnected(object) && !is_required_hook(hook)) {
288!
448
            object.logdebug(`ignoring ${item[0].name} plugin callback`)
×
449
            return
×
450
        }
×
451
        if (called_once && hook !== 'log') {
288!
452
            if (!timed_out) {
×
453
                object.logerror(`${item[0].name} plugin ran callback ` + `multiple times - ignoring subsequent calls`)
×
454
                // Write a stack trace to the log to aid debugging
×
455
                object.logerror(new Error().stack)
×
456
            }
×
457
            return
×
458
        }
×
459
        called_once = true
287✔
460
        if (!retval) retval = constants.cont
288✔
461

287✔
462
        log_run_item(item, hook, retval, object, params, msg)
287✔
463

287✔
464
        if (object.hooks_to_run.length !== 0) {
288✔
465
            if (retval === constants.cont) {
12✔
466
                return plugins.run_next_hook(hook, object, params)
12✔
467
            }
12✔
468
            if (hook === 'connect_init' || hook === 'disconnect') {
12!
UNCOV
469
                // these hooks ignore retval and always run for every plugin
×
UNCOV
470
                return plugins.run_next_hook(hook, object, params)
×
UNCOV
471
            }
×
472
        }
12✔
473

275✔
474
        const respond_method = `${hook}_respond`
275✔
475
        if (item && is_deny_retval(retval) && hook.substring(0, 5) !== 'init_') {
288!
476
            object.deny_respond = get_denyfn(object, hook, params, retval, msg, respond_method)
×
477
            plugins.run_hooks('deny', object, [retval, msg, item[0].name, item[1], params, hook])
×
478
        } else {
288✔
479
            object.hooks_to_run = []
275✔
480
            object[respond_method](retval, msg, params)
275✔
481
        }
275✔
482
    }
288✔
483

288✔
484
    if (!object.hooks_to_run.length) return callback()
288✔
485

32✔
486
    // shift the next one off the stack and run it.
32✔
487
    item = object.hooks_to_run.shift()
32✔
488
    item.push(cancel)
32✔
489

32✔
490
    if (hook !== 'log' && item[0].timeout) {
288✔
491
        timeout_id = setTimeout(() => {
32✔
492
            timed_out = true
×
493
            object.logcrit(`Plugin ${item[0].name} timed out on hook ${hook} - make sure it calls the callback`)
×
494
            callback(constants.denysoft, 'plugin timeout')
×
495
        }, item[0].timeout * 1000)
32✔
496
    }
32✔
497

32✔
498
    if (hook !== 'log') {
32✔
499
        object.logdebug(`running ${hook} hook in ${item[0].name} plugin`)
32✔
500
    }
32✔
501

32✔
502
    if (object.transaction?.notes.skip_plugins.includes(item[0].name)) {
288!
503
        object.logdebug(`skipping ${item[0].name}_${hook} by request in notes`)
×
504
        return callback()
×
505
    }
×
506

32✔
507
    try {
32✔
508
        object.current_hook = item
32✔
509
        object.hook = hook
32✔
510
        item[0][item[1]].call(item[0], callback, object, params)
32✔
511
    } catch (err) {
266!
512
        if (hook !== 'log') {
×
513
            object.logcrit(`Plugin ${item[0].name} failed: ${err.stack || err}`)
×
514
        }
×
515
        callback()
×
516
    }
×
517
}
288✔
518

17✔
519
function client_disconnected(object) {
851✔
520
    if (object.constructor.name === 'Connection' && object.state >= constants.connection.state.DISCONNECTING) {
851✔
521
        object.logdebug('client has disconnected')
21✔
522
        return true
21✔
523
    }
21✔
524
    return false
830✔
525
}
851✔
526

17✔
527
function is_required_hook(hook) {
573✔
528
    // Hooks that must always run
573✔
529
    switch (hook) {
573✔
530
        case 'reset_transaction':
573✔
531
        case 'disconnect':
573✔
532
        case 'log':
573✔
533
            return true
431✔
534
        default:
573✔
535
            return false
142✔
536
    }
573✔
537
}
573✔
538

17✔
539
function log_run_item(item, hook, retval, object, params, msg) {
287✔
540
    if (!item) return
287✔
541
    if (hook === 'log') return
265!
542

31✔
543
    let log = 'logdebug'
31✔
544
    const is_not_cont = retval !== constants.cont && logger.would_log(logger.LOGINFO)
265✔
545
    if (is_not_cont) log = 'loginfo'
287✔
546
    if (is_not_cont || logger.would_log(logger.LOGDEBUG)) {
287✔
547
        object[log]({
10✔
548
            hook,
10✔
549
            plugin: item[0].name,
10✔
550
            function: item[1],
10✔
551
            params: params ? (typeof params === 'string' ? params : params[0]) : '',
10!
552
            retval: constants.translate(retval),
10✔
553
            msg: sanitize(msg),
10✔
554
        })
10✔
555
    }
10✔
556
}
287✔
557

17✔
558
function sanitize(msg) {
10✔
559
    if (!msg) return ''
10✔
560
    if (typeof msg === 'string') return msg
×
561
    if (typeof msg === 'object') {
×
562
        if (msg.constructor.name === 'DSN') return msg.reply
×
563
        const sanitized = { ...msg } // copy the message
×
564
        for (const priv of ['password', 'auth_pass']) {
×
565
            delete sanitized[priv]
×
566
        }
×
567
        return JSON.stringify(sanitized)
×
568
    }
×
569
    logger.logerror(`what is ${msg} (typeof ${typeof msg})?`)
×
570
}
10✔
571

17✔
572
function is_deny_retval(val) {
19✔
573
    switch (val) {
19✔
574
        case constants.deny:
19!
575
        case constants.denysoft:
19!
576
        case constants.denydisconnect:
19!
577
        case constants.denysoftdisconnect:
19!
578
            return true
×
579
    }
19✔
580
    return false
19✔
581
}
19✔
582

17✔
583
function get_denyfn(object, hook, params, retval, msg, respond_method) {
×
584
    return (deny_retval, deny_msg) => {
×
585
        switch (deny_retval) {
×
586
            case constants.ok:
×
587
                // Override rejection
×
588
                object.loginfo(`deny(soft?) overriden by deny hook${deny_msg ? ': deny_msg' : ''}`)
×
589
                // Restore hooks_to_run with saved copy so that
×
590
                // any other plugins on this hook can also run.
×
591
                if (object.saved_hooks_to_run.length > 0) {
×
592
                    object.hooks_to_run = object.saved_hooks_to_run
×
593
                    plugins.run_next_hook(hook, object, params)
×
594
                } else {
×
595
                    object[respond_method](constants.cont, deny_msg, params)
×
596
                }
×
597
                break
×
598
            default:
×
599
                object.saved_hooks_to_run = []
×
600
                object.hooks_to_run = []
×
601
                object[respond_method](retval, msg, params)
×
602
        }
×
603
    }
×
604
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc