@@ -2,7 +2,7 @@ v2.0.1 | Mar 25 2026:
22
33 -- New Features --
44
5- [New] Monitor mode: supervisor model replaces double-fork; 12 pre-existing defects resolved
5+ [New] Monitor mode: supervisor model replaces double-fork; 12 pre-existing defects resolved (graceful shutdown, crash recovery, session rotation, event filter, PID guard, ClamAV cache); issue #454, #447, #459
66[New] Config: digest_interval, digest_escalate_hits, monitor_paths_extra for monitor mode
77[New] TSV canonical session format: 11-field hit records replace plaintext session files;
88 session_legacy_compat config (auto/0/1) for backward compatibility
@@ -72,45 +72,15 @@ v2.0.1 | Mar 25 2026:
7272 supports args/file/json output tiers, sync/async execution, timeout
7373 with SIGTERM/SIGKILL, elog audit trail, min_hits threshold, scan type filter;
7474 scan_start epoch in JSON payload (LMD_SCAN_START env); issue #477
75- [New] ignore_inotify: versioned defaults file ($inspath/internals/ignore_inotify.defaults) consulted in addition to user ignore_inotify; refreshed for systemd-private tmpdirs, modern MariaDB, PostgreSQL, Redis; issue #480
76- [Fix] ignore_inotify: replace dead legacy regex defaults (broken since 2026-03-19 _monitor_escape_ere change) with user-template header; live defaults now ship in ignore_inotify.defaults; issue #480
77- [Fix] monitor: union-load ignore_inotify + ignore_inotify.defaults; new _monitor_load_ignore_inotify_union helper skips blanks+comments, dedupes across both files; issue #480
78- [New] packaging: ship ignore_inotify.defaults in RPM spec, DEB rules, links, symlink-manifest; non-conffile under /usr/lib/maldet/internals/ so upgrades refresh the file; override_dh_fixperms preserves 640 mode on DEB; issue #480
79- [Change] docs: document ignore_inotify.defaults two-file union model in README §5/§7 and maldet.1 MONITOR MODE; issue #480
80- [Fix] ignore_inotify.defaults: scope 'sql_' to /tmp/#sql_ + /var/tmp/#sql_ and drop bare 'scantemp' to prevent matching user files like sql_backup.php and scantemplate.php; add /var/lib/maldet/ for FHS scan-temp coverage; sentinel remediation for issue #480
75+ [New] ignore_inotify: two-file union model — LMD-managed defaults
76+ ($inspath/internals/ignore_inotify.defaults) refreshed for systemd-private
77+ tmpdirs, modern MariaDB, PostgreSQL, Redis, ClamAV runtime; user-owned
78+ ignore_inotify retained for site overrides; dead legacy regex defaults
79+ replaced (broken since _monitor_escape_ere change); issue #480
8180
8281 -- Changes --
8382
84- [Change] comments: T3 inline prose rehousing + restatement sweep across 18 files
85- [Change] comments: T2 function-header normalization — collapse signature-restatement blocks
86- [Change] comments: T1 mechanical cleanup — strip banner separators and file-header catalogues
87- [Change] - Delete 20 pure-dash banners (lmd_hook.sh: 14, lmd_alert.sh: 6)
88- [Change] - Transform 95 labeled banners (# --- label --- -> # label)
89- [Change] - Trim file-header catalogues (hookscan.sh, importconf, tlog, cron.watchdog, pkg-postinst.sh, uninstall.sh, files/uninstall.sh)
90- [Change] - Applies parent CLAUDE.md §Code Comments primitive (one-line headers, no banners, no tombstones)
91- [Change] - Preserves load-bearing keeps: BEGIN INIT INFO (service/maldet.sh), flock rationale (cron.daily), cron.watchdog decoupling invariant
92- [Change] Vendored lib sync: tlog_lib 2.0.5 → 2.0.6, alert_lib 1.0.6 → 1.0.7,
93- elog_lib 1.0.5 → 1.0.6, pkg_lib 1.0.9 → 1.0.10. All four are
94- comment-discipline cascade bumps from canonical (banner separators
95- removed, signature-restatement prose deleted from function headers,
96- prose config catalogues replaced with README cross-references).
97- Zero functional change in any of the four libraries; load-bearing
98- rationale (TLS posture, symlink-safety, eval-guard validation,
99- pkg_config_merge invariants, SIEM/CEF/GELF contract docs, journal
100- filter contracts, alert dispatch invariants) preserved intact.
101- Byte-identical to canonical: tlog_lib d65158c (v2.0.6),
102- alert_lib 3b66050 (v1.0.7), elog_lib 7a60165 (v1.0.6),
103- pkg_lib cdd0814 (v1.0.10).
104- [Change] files/internals/tlog (standalone CLI wrapper): 2.0.4 → 2.0.6 —
105- byte-synced to canonical. Wrapper had been drifting (skipped
106- during prior 2.0.5 sync); colocated with tlog_lib.sh so no
107- BFD-style fallback required. install.sh BASERUN sed pattern
108- still matches the canonical line format.
109- [Change] tests/infra: batsman 1.4.1 → 1.4.2 — runs-on input for
110- self-hosted runner support (CI workflow only, no test behaviour
111- changes).
112- [Change] Vendored lib sync: tlog_lib 2.0.5, alert_lib 1.0.6, elog_lib 1.0.5,
113- pkg_lib 1.0.8
83+ [Change] Vendored libs synced to canonical: tlog_lib 2.0.6, alert_lib 1.0.7, elog_lib 1.0.6, pkg_lib 1.0.10 (zero functional change)
11484[Change] Alert templates: consolidate summary into headers; drop "TOTAL" prefix from
11585 labels (HITS/CLEANED/QUARANTINED); add quarantine metrics; aligned column spacing
11686[Change] Hook scans write to rolling hook.hits.log instead of creating session files;
@@ -120,8 +90,6 @@ v2.0.1 | Mar 25 2026:
12090[Change] CLI: modifier flags (-x, -i, -hscan, -qd, -co, --format, --mailto) are now
12191 position-independent; -co uses in-memory parser; -qd with non-existent
12292 directory exits 1
123- [Change] Documentation: man page, README, and usage rewritten with complete option/config
124- coverage
12593[Change] Rename sig_import_*_url config variables (from import_custsigs_*_url) for
12694 sig_* namespace consistency; compat.conf migration maps old names
12795[Change] scan_workers default changed from "0" to "auto"; "0" silently accepted as
@@ -155,80 +123,46 @@ v2.0.1 | Mar 25 2026:
155123 maint_archive_age (both disableable with 0)
156124[Change] cron.daily: run --maintenance after sigup/versionup for automated history
157125 rotation and session cleanup; hardcoded log paths replaced with $maldet_log
158- [Change] Tests: batsman v1.4.2; per-test 180s timeout prevents hung scans;
159- GHA 24-way parallel matrix; JUnit XML reports
126+ [Change] Lifecycle JSON (-L --format json): "scan_id" is now the canonical field name;
127+ "scanid" remains as a deprecated alias for one release cycle and will be
128+ removed in v2.1.0. Consumers should switch to "scan_id"; issue #482
129+ [Change] Lifecycle JSON (-L --format json): "workers" field type normalized to
130+ unquoted number to match the field's underlying integer value; issue #482
131+ [Change] _json_escape_string: promoted to lmd.lib.sh shared utilities so all JSON
132+ emitters (scan list, active lifecycle, post-scan hook, ELK dispatch) share
133+ one helper; sibling _json_escape_var out-parameter helper for hot loops
134+ that must avoid a subshell fork per iteration; issue #482
135+ [Change] conf.maldet / maldet.1: correct post_scan_hook_timeout documentation — prior
136+ text claimed a 5-second grace period before SIGKILL, but timeout(1) is invoked
137+ without --kill-after, so a hook that traps SIGTERM will run indefinitely; pr#478
160138
161139 -- Bug Fixes --
162140
163- [Fix] --json-report list: reports[] entries now include a "path" field, matching
164- the text-mode list output; active[] entries gain the lifecycle schema
165- (eta, workers, sig_version, progress{}) so --json-report list and
166- -L/--list-active emit the same shape; stopped[] entries gain an elapsed
167- field; all string fields in active[] and stopped[] are now JSON-escaped
168- via the shared _json_escape_string helper (previously only "path" was
169- escaped — stage, engine, stopped_hr, stages, sig_version could emit
170- invalid JSON if scan.meta contained quotes, backslashes, or control
171- characters); issue #482
172- [Change] Lifecycle JSON (-L --format json): "scan_id" is now the canonical
173- field name; "scanid" remains as a deprecated alias for one release cycle
174- and will be removed in v2.1.0. Consumers should switch to "scan_id"
175- [Change] Lifecycle JSON (-L --format json): "workers" field type normalized to
176- unquoted number to match the field's underlying integer value
177- [Change] _json_escape_string: promoted from lmd_hook.sh (optional sub-lib) to
178- lmd.lib.sh shared utilities so all JSON emitters — scan list, active
179- lifecycle, post-scan hook, and ELK dispatch — share one helper. Three
180- call sites in lmd_alert.sh previously using _alert_json_escape (from the
181- vendored alert_lib) now use the project-owned helper; vendored library
182- coupling reduced to the lib's internal self-use. A sibling out-parameter
183- helper _json_escape_var (sets _JSON_ESC_OUT) is also defined for hot
184- loops that must avoid a subshell fork per iteration
185- [Fix] --json-report list: scaling regression at large session counts. Pre-fix
186- the function exhibited effective hang at ~20K indexed sessions from two
187- compounding costs: (1) _seen_ids built as a whitespace-joined string
188- that grew O(N^2) with glob-pattern dedup, and (2) one subshell fork per
189- report for path escaping. Dedup now uses a function-scoped local -A
190- associative array (O(N) total), and the reports[]/legacy hot loops use
191- the new _json_escape_var out-parameter helper instead of $() command
192- substitution. Measured: 20K reports 82s -> 1.7s; 50K reports now ~1.7s
193- (linear). Regression guard: tests/31-json-report.bats test 27 runs a
194- 10K-entry synthetic index under a 30s timeout
141+ [Fix] monitor: union-load ignore_inotify + ignore_inotify.defaults; new _monitor_load_ignore_inotify_union helper skips blanks+comments, dedupes across both files; issue #480
142+ [Fix] --json-report list: path field parity with text mode; unified JSON escaping
143+ across reports[]/active[]/stopped[]; dedup+escape rewrite eliminates O(N^2)
144+ hang at ~20K sessions (82s → 1.7s); active[] gains lifecycle schema
145+ (eta/workers/sig_version/progress); stopped[] gains elapsed; issue #482
146+ [Fix] --json-report list: reports[] now globally sorted by started_epoch
147+ (newest first) across TSV index + legacy-session passes (pre-fix emitted
148+ index entries in append-order then legacy, producing visible date
149+ inversions); reports[]/active[]/stopped[] all gain started_epoch integer;
150+ stopped[] gains stopped_epoch; pass-2 glob skips legacy session.*.html
151+ artifacts (fixes 12s+ hangs on installs with pre-on-demand-HTML files);
152+ issue #483
195153[Fix] clamav_linksigs: guard mktemp staging failure to prevent writing signature
196154 files into the filesystem root. When mktemp -d fails, the empty _staging
197155 variable caused "cp -f ... /" as root; pr#478
198- [Fix] _json_escape_string: replace sed pipeline with bash parameter expansion.
199- Two defects fixed in one rewrite: (1) the quoted "$sed" invocation tried
200- to execve a binary literally named "sed -E" on FreeBSD (where
201- internals.conf sets sed="$sed -E"), silently breaking post_scan_hook
202- JSON payload construction; (2) the N;s/\n/\\n/;P;D sed cycle only
203- escaped the first newline in multi-line input, emitting raw LF bytes
204- in JSON output for paths with two or more embedded newlines. The bash
205- parameter expansion form is portable, correct for any number of
206- newlines, and removes an external process from the hook hot path.
207- BATS coverage added (J-01 through J-15) including a structural guard
208- against sed reintroduction; pr#478
209- [Change] conf.maldet / maldet.1: correct post_scan_hook_timeout documentation.
210- The prior text claimed a 5-second grace period before SIGKILL, but the
211- code invokes timeout(1) without --kill-after, so a hook that traps
212- SIGTERM will run indefinitely. Updated to match actual behavior;
213- pr#478
214- [Fix] DEB: override_dh_fixperms target restores 640/750 modes after debhelper's
215- dh_fixperms normalizes executables to 755 and non-executables to 644.
216- Without this, 72 files shipped in the data.tar at the wrong mode including
217- all internals/*.sh sub-libraries, alert templates, cron fragments, and
218- runtime config files. conf.maldet was masked at runtime by an explicit
219- chmod in postinst, but conf.maldet.hookscan.default, /etc/default/maldet,
220- and the 15 sub-library shell files were genuinely wrong on installed
221- systems. RPM uses %attr() declarations and was unaffected; pr#478
222- [Fix] --test-alert digest email: seed $tmpdir/digest.hook.alert cursor to
223- _orig_size before dispatching through genalert. Three related defects in
224- the prior implementation: (1) first-run tlog_read path silently swallowed
225- the synthetic test hits on fresh installs, so the function printed
226- "test email digest alert sent" without actually dispatching anything;
227- (2) a trailing cursor caused unalerted real hook hits to leak into the
228- test email; (3) the cursor restore left those same hits to be re-sent
229- on the next real digest. Also guards post-dispatch truncate against
230- trim_log having shortened the log mid-digest. Positive-path BATS
231- coverage added to prevent regression; pr#478
156+ [Fix] _json_escape_string: replaced sed pipeline with bash parameter expansion;
157+ fixes FreeBSD breakage (sed="$sed -E" execve) and multi-newline escaping
158+ for post_scan_hook JSON payloads; pr#478
159+ [Fix] DEB: override_dh_fixperms restores 640/750 modes (72 files incl.
160+ conf.maldet.hookscan.default, /etc/default/maldet, and 15 sub-library shell
161+ files) after debhelper normalizes executables to 755 and non-executables
162+ to 644; RPM uses %attr() and is unaffected; pr#478
163+ [Fix] --test-alert digest: seed $tmpdir/digest.hook.alert cursor to _orig_size
164+ so first-run tlog_read dispatches synthetic hits on fresh installs; prevents
165+ real-hit leakage into test email and re-send on next real digest; pr#478
232166[Fix] Hook validation: world-writable detection used broken glob extraction that always
233167 returned empty string; replaced with bash substring to correctly extract last octal
234168 digit; pr#478
@@ -260,8 +194,6 @@ v2.0.1 | Mar 25 2026:
260194 man page compression on fresh install; backup robustness; no longer kills inotify
261195 monitoring; Slackware init per rc.NAME convention; preserve hookscan config and
262196 pub/ state; issue #414
263- [Fix] Docs: missing config vars (scan_hashtype, scan_find_timeout, scan_yara auto),
264- pkg_lib.sh in FILES section, CLI aliases, --cron-sigup
265197[Fix] purge() dotfile glob: replace * glob with find -delete to catch dotfiles in
266198 tmp/quarantine/sess
267199[Fix] Quarantine: correct scan ID in report warning; TOCTOU mv exit code check; inode
@@ -285,8 +217,6 @@ v2.0.1 | Mar 25 2026:
285217 concurrent scan conflicts
286218[Fix] scan(): exit 1 when all provided scan paths do not exist
287219[Fix] Hash scanning: handle md5sum/sha256sum backslash-escaped filenames
288- [Fix] Monitor mode: 12 defects fixed (graceful shutdown, crash recovery, session
289- rotation, event filter, PID guard, ClamAV cache); issue #454, #447, #459
290220[Fix] Alerting: Slack migrated from deprecated files.upload API; Telegram /bot prefix;
291221 token security via curl -K; genalert() state isolation; issue #387, #458, #461, #426
292222[Fix] Security: _safe_source_conf() allowlist (79 vars); conf.maldet 0640; dirs 750;
@@ -326,26 +256,6 @@ v2.0.1 | Mar 25 2026:
326256[Fix] RPM/DEB %pre: defensive detection for dir-vs-symlink cpio conflict; install
327257 no longer fails with "rename failed - Is a directory" when prior install.sh
328258 or partial install left real directories at symlink-target paths
329- [Fix] --json-report list: reports[] now globally sorted by started_epoch
330- (newest first) across TSV index + legacy-session passes; pre-fix
331- code emitted index entries in append-order then legacy entries,
332- producing visible date-order inversions (e.g. Mar 29 legacy after
333- Apr 18 TSV); issue #483
334- [New] --json-report list: reports[] entries include started_epoch integer
335- field for machine-readable absolute timestamps; additive schema
336- change (existing "started" string preserved); issue #483
337- [New] tests: 3 BATS regressions for --json-report list sort order,
338- started_epoch field, and legacy-session epoch derivation; issue #483
339- [New] --json-report list: active[] and stopped[] entries now include
340- started and started_epoch for consistency with reports[]; stopped[]
341- also gains stopped_epoch alongside existing stopped_hr; issue #483
342- [Fix] --report list / --json-report list / -e newest: pass-2 glob now
343- skips legacy session.*.html artifacts (caught pre-fix by
344- session.[0-9]* glob). _parse_session_metadata was slurping
345- multi-MB HTML files line-by-line searching for break markers that
346- never appear in HTML, producing 12s+ hangs on installs carrying
347- pre-on-demand-HTML legacy artifacts. Fix at 3 sites in lmd_alert.sh
348- + lmd_session.sh
349259
350260v1.6.6.1 | Feb 25 2025:
351261[Fix] find_recentopts incorrectly escaping find options to the right of ( -mtime .. -ctime ); previously normalized by eval; issue #440, pr#442
0 commit comments