-
Notifications
You must be signed in to change notification settings - Fork 245
Expand file tree
/
Copy pathCHANGELOG.RELEASE
More file actions
380 lines (371 loc) · 26.8 KB
/
CHANGELOG.RELEASE
File metadata and controls
380 lines (371 loc) · 26.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
v2.0.1 | Mar 25 2026:
-- New Features --
[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
[New] Config: digest_interval, digest_escalate_hits, monitor_paths_extra for monitor mode
[New] TSV canonical session format: 11-field hit records replace plaintext session files;
session_legacy_compat config (auto/0/1) for backward compatibility
[New] JSON report output: --json-report [SCANID|list|newest] renders TSV sessions as
JSON (v1.0 schema); --format text|json|html and --mailto ADDRESS
[New] Compound signature (csig) scanning: multi-pattern boolean logic (AND/OR/threshold),
case-insensitive matching (i:), wide UTF-16LE matching (w:), bounded gap wildcards
({N-M}); csig.dat and custom.csig.dat; scan_csig config var
[New] SHA-256 hash-based scanning: CPU hardware acceleration auto-detection (SHA-NI on
x86, SHA2 on ARM); scan_hashtype config (auto/sha256/md5/both); sha256v2.dat
signature file; ClamAV .hsb integration with version gate (>= 0.97);
custom.sha256.dat for user-defined sigs
[New] Native batch scan engine: 43x faster HEX+CSIG; parallel grep workers;
micro-chunked processing (scan_hex_chunk_size config); Perl dependency removed
[New] Batch hit processing: unified bulk operations replace per-hit fork loops across
all scan engines; batched chattr/chmod/chown for quarantine
[New] Native YARA scanning: scan_yara=1 enables YARA as independent scan stage with
yara or yr (YARA-X) binary; custom.yara and custom.yara.d/ drop-in directory;
compiled rules via yarac, --scan-list batch scanning; issue #392, #277, #239
[New] Email alerts reimagined: HTML+text dual-format with template engine ({{TOKEN}}
placeholders); SMTP relay support (smtp_relay, smtp_from, smtp_user, smtp_pass);
email_format config (text/html/both); Slack Block Kit, Telegram MarkdownV2,
Discord embed templates; on-demand HTML rendering from session data
[New] Discord alerting: discord_alert and discord_webhook_url config; scan reports
uploaded via Discord webhook multipart upload
[New] Shared library integrations: alert_lib, elog_lib (structured event logging with
audit trail), tlog_lib (incremental log tracking), pkg_lib (packaging primitives)
[New] ClamAV signature validation gate: clamscan -d validates sig files before
deployment; sigup() SIGUSR2 reload gated on validation success; issue #467
[New] sigup_interval: independent 6-hourly signature update cron job via
/etc/cron.d/maldet-sigup; --cron-sigup handler; configurable interval (0 to disable)
[New] cron.watchdog: weekly watchdog script for independent fallback signature updates
[New] cron.daily: explicit cPanel addon/subdomain document root detection via
/etc/userdatadomains; Bitrix panel detection; issue #268, #381
[New] -v/--version flag displays version information and exits 0
[New] scan_clamscan and scan_yara support 'auto' mode for runtime binary detection
[New] Hook scanning API: modsec/ftp/proftpd/exim/generic mode dispatch; --list/--stdin
batch; rate limit; sig masking
[New] Hook escalation: immediate alert on hit threshold; deduplicated with digest
[New] Hook digest and reporting: --digest on-demand alerts; --report hooks with
time/mode filters
[New] --test-alert {scan|digest} {email|slack|telegram|discord}: send test alert with
synthetic data through the real rendering pipeline; channel isolation; root-only gate
[New] Audit log: 7 event types (purge, update, alert_failed, hookscan source/rate metadata)
[New] importconf: config migration extracted from install.sh for shared use
[New] RPM and DEB packaging: FHS-compliant layout with backward-compatible symlink farm
[New] Package install tests: FHS layout, symlink farm, permissions verification
[New] Symlink farm enforcement: static manifest ships with RPM/DEB; pkg_fhs_verify_farm
auto-detects and repairs broken symlinks at startup
[New] FHS fallback sourcing: maldet boots from /usr/lib/maldet when legacy symlinks broken
[New] Portable source-tree execution: run maldet directly from git clone or tarball without
install.sh; auto-detects inspath from script location; LMD_BASEDIR env override
[New] Scan lifecycle management: --kill, --pause/--unpause, --stop/--continue for running
scans; -L/--list-active shows running/paused/stopped scans (text/json/tsv);
--maintenance rotates history, compresses sessions, archives by month; session index
for O(1) report listing; scan.meta tracks per-scan state; scan_meta_cleanup_age config
[New] SCANID format validation: ^[0-9]{6}-[0-9]{4}\.[0-9]+$ regex enforced at all 5 CLI
entry points (--kill, --pause, --unpause, --stop, --continue)
[New] -e list: redesigned columnar output with header row; --all flag shows full scan
history (default: recent 14 entries); QUAR column; stopped/checkpointed scans visible
between active and history sections (text and JSON); pager when --all on TTY;
JSON active array; FreeBSD sort support
[New] Logrotate config: /etc/logrotate.d/maldet rotates event_log, clamscan_log, and
audit.log weekly (12 rotations); inotify_log excluded (managed by --maintenance);
installed only when logrotate is available; RPM/DEB: %config(noreplace)
[New] post-scan hook: configurable script execution after scan completion;
supports args/file/json output tiers, sync/async execution, timeout
with SIGTERM/SIGKILL, elog audit trail, min_hits threshold, scan type filter;
scan_start epoch in JSON payload (LMD_SCAN_START env); issue #477
[New] ignore_inotify: two-file union model — LMD-managed defaults
($inspath/internals/ignore_inotify.defaults) refreshed for systemd-private
tmpdirs, modern MariaDB, PostgreSQL, Redis, ClamAV runtime; user-owned
ignore_inotify retained for site overrides; dead legacy regex defaults
replaced (broken since _monitor_escape_ere change); issue #480
[New] monitor_scan_owner_filters config variable: when 0 (default), monitor mode
admits all files regardless of owner so root-owned malware is not silently
dropped; when 1, scan_ignore_root/user/group ownership filters are applied
in monitor mode (opt-in); issue #485
[New] tests/uat/05-monitor-mode.bats: UAT scenario asserting monitor quarantines
a root-owned EICAR dropped into a webuser-owned docroot under reporter's
config (scan_ignore_root=1 + monitor_scan_owner_filters=0 default);
regression test closing the coverage hole that let rc1..rc3 ship with the
filter bug; issue #485
-- Changes --
[Change] slop: remove 15 unused LMD_<STEM>_VERSION sub-lib constants +
paired shellcheck disable=SC2034 suppressions; set at sub-lib
extraction, never consumed by any code, test, packaging, or
doc surface; _LMD_<STEM>_LOADED source guards (distinct symbols)
remain as the idempotent-sourcing mechanism; round 3 slop audit
[New] _monitor_to_ere_entry(): semantic dispatch helper for ignore_inotify
entry preparation (defaults-always-escape, user-raw-or-literal:)
[Change] tests/48-monitor-ignore-regex.bats: integration coverage for
ignore_inotify ERE + literal: prefix + ignore_paths monitor-mode
consistency + malformed-regex symmetry; issue #484
[Change] docs: ignore_inotify ERE-default + literal: prefix semantics
across ignore_inotify header, README §7, maldet.1 FILES; issue #484
[Change] tests: prune two tautological assertions — the
"uninstall.sh delegates service removal to pkg_service_uninstall"
grep-for-token check in 01-install-cli.bats (string presence, not
behavior) and the "ignore_inotify.defaults is installed at
internals path" existence check in 47-ignore-inotify-defaults.bats
(adjacent "has non-comment entries" test already fails if the
file is missing)
[Change] monitor: log filtered-cycle event count when all events dropped by
tier-2 scan filter (_event_count > 0 but _tot_files == 0); issue #485
[Change] tests/helpers/uat-lmd.bash: de-duplicate Dockerfile-level
scan_ignore_root="0" bake — remove redundant sed from
uat_lmd_install and add opt-in uat_lmd_disable_root_filter
helper so each UAT caller explicitly documents its
dependency on the override; all 19 existing UAT callers
migrated (effective value unchanged — still 0 via
Dockerfile bake + explicit helper call); unblocks issue-485
UAT which re-sets scan_ignore_root=1 to exercise the
production default; issue #485
[Change] docs: conf.maldet scan_ignore_root comment cross-references monitor carve-out;
README.md scan_ignore_root row notes monitor exception, new
monitor_scan_owner_filters row added to monitor config table;
maldet.1 MONITOR MODE gains carve-out paragraph (issue #485 rationale,
monitor_scan_owner_filters semantics) and Monitoring config group
listing updated; issue #485
[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)
[Change] Alert templates: consolidate summary into headers; drop "TOTAL" prefix from
labels (HITS/CLEANED/QUARANTINED); add quarantine metrics; aligned column spacing
[Change] Hook scans write to rolling hook.hits.log instead of creating session files;
genalert suppressed for hook scans
[Change] HTML email rendered on-demand at send time from current templates; persistent
.html session files no longer stored; responsive layout with Outlook compatibility
[Change] CLI: modifier flags (-x, -i, -hscan, -qd, -co, --format, --mailto) are now
position-independent; -co uses in-memory parser; -qd with non-existent
directory exits 1
[Change] Rename sig_import_*_url config variables (from import_custsigs_*_url) for
sig_* namespace consistency; compat.conf migration maps old names
[Change] scan_workers default changed from "0" to "auto"; "0" silently accepted as
auto for backward compat
[Change] Update verification prefers SHA-256 over MD5; graceful MD5 fallback when
sha256sum absent
[Change] maldet.service: EnvironmentFile changed from conf.maldet to
/etc/sysconfig/maldet or /etc/default/maldet; init script uses -b daemonization
[Change] -E/--dump-report suppressed from help; case handler retained for backward compat
[Change] inotify_verbose config variable removed; per-file ClamAV logging was
superseded by batch engine log messages with no in-code consumer
[Change] telegram_file_caption config variable removed; was unreferenced — the
Telegram file upload passes an empty caption
[Change] Scan engine: HEX+CSIG merged into single worker pass; scan stage reorder
(strlen runs last); bulk awk HEX classifier
[Change] gensigs: awk compiler for faster signature compilation; dead helpers removed
[Change] _scan_progress(): plain newline-terminated output in non-TTY contexts;
ANSI escape sequences gated behind TTY check; logarithmic backoff reduces
progress I/O on non-native engines (ClamAV, YARA); watchdog semantics for
engine health monitoring
[Change] install.sh: delegate config/state migration to importconf
[Change] scan_hexdepth default reduced from 524288 (512 KB) to 262144 (256 KB); covers
98.9% of HEX patterns with YARA backstop for remaining 1.1%; importconf migration
for old default
[Change] FHS log path: /var/log/maldet/ is now the authoritative log directory for all
install methods; $inspath/logs is a backward-compatible symlink; install.sh
migrates existing logs on upgrade (cp -a, preserves timestamps/permissions)
[Change] --maintenance: session compression age raised from 1 hour to 30 days;
archive age raised from 30 days to 90 days; scan_meta_cleanup_age default
raised from 24 to 48 hours; new config variables maint_compress_age,
maint_archive_age (both disableable with 0)
[Change] cron.daily: run --maintenance after sigup/versionup for automated history
rotation and session cleanup; hardcoded log paths replaced with $maldet_log
[Change] Lifecycle JSON (-L --format json): "scan_id" is now the canonical field name;
"scanid" remains as a deprecated alias for one release cycle and will be
removed in v2.1.0. Consumers should switch to "scan_id"; issue #482
[Change] Lifecycle JSON (-L --format json): "workers" field type normalized to
unquoted number to match the field's underlying integer value; issue #482
[Change] _json_escape_string: promoted to lmd.lib.sh shared utilities so all JSON
emitters (scan list, active lifecycle, post-scan hook, ELK dispatch) share
one helper; sibling _json_escape_var out-parameter helper for hot loops
that must avoid a subshell fork per iteration; issue #482
[Change] --json-report list: active[]/stopped[] gain ppid + hashtype fields from
scan.meta; reports[] gains derived completed_epoch (started_epoch +
elapsed_seconds, null when inputs missing); field ordering normalized
across all three arrays (identity → process → location → time →
config → metrics)
[Change] --json-report list schema 1.1: top-level scanner block ({name, version,
sig_version}) and host block ({hostname, host_id}) mirror per-scan
JSON schema; field renames for parity with per-scan JSON — hashtype
→ hash_type, hits → total_hits, elapsed → elapsed_seconds; stopped[]
stopped_hr → stopped for symmetry with started/started_epoch pattern
(internal meta/checkpoint field names unchanged); active[]/stopped[]
add quarantine_enabled (boolean, from scan.meta) and null total_cleaned/
total_quarantined (post-scan actions not yet determined mid-scan);
stopped[] adds engine + sig_version parity with active[]; reports[]
adds sig_version + quarantine_enabled (null for pre-bump rows and
legacy plaintext sessions)
[Change] scan.meta: new quarantine_enabled field written by _lifecycle_write_meta
from $quarantine_hits; _lifecycle_read_meta parses into
_meta_quarantine_enabled
[Change] maldet.1, README.md: schema 1.2 docs — new JSON OUTPUT subsection
in maldet.1 documenting the uniform top-level shape; --json-report
description extended; session.index FILES entry describes 14-field
v2 schema with back-compat note for v1; README.md JSON references
updated v1.0 → v1.2
[Change] --json-report SCANID schema 1.2: per-scan JSON shape now mirrors
--json-report list — top-level keys are {schema_version, scanner,
host, reports[single]}; reports[0] carries scan_id (was scan.id),
started_epoch + completed_epoch (NEW), and the existing per-scan
fields (path, started, completed, elapsed_seconds, total_files,
total_hits, total_cleaned, total_quarantined, quarantine_enabled,
hits[], summary{}); scanner block at top level adds engine +
hash_type alongside name/version/sig_version; legacy plaintext
sessions emit the same shape with reports[0].source="legacy" and
scanner.{engine,hash_type,version,sig_version}=null
[Change] --json-report list schema_version 1.1→1.2: top-level "version" key
renamed to "schema_version" and bumped to "1.2"; reports[] entries
now carry "completed" (string from session.index field 12),
"engine" (field 13), "hash_type" (field 14) for parity with
per-scan JSON; pre-v1.2 index rows and pass-2 legacy plaintext
sessions emit literal JSON null (never "-", never empty)
[Change] session.index v1.2 readers: text path (view_report list) and JSON
list pass-1 extend their IFS=read named-vars lists with
_ix_completed_hr / _ix_engine / _ix_hashtype to consume the new
fields without spillage; pass-2 legacy reader unchanged (predates
v1.2 fields, will emit JSON null in Phase 4 emission step)
[Change] session.index schema 1.2: bumped 11→14 fields (completed_hr + engine
+ hash_type appended as fields 12-14); header sentinel #LMD_INDEX:v1
→ v2 for explicit consumer-side detection (readers tolerate both);
_session_index_append accepts 14 args (11-arg calls default fields
12-14 to "-"); _session_index_rebuild wires through previously-
discarded TSV throwaway vars _r_end_hr/_r_engine/_r_hashtype
[Change] session.index schema bumped 9→11 fields (sig_version + quarantine_enabled
appended as fields 10 + 11); _session_index_append accepts 11 args
(9-arg calls default fields 10-11 to "-" and "0"); _session_index_rebuild
emits 11 fields from TSV session headers; readers tolerate old 8/9-field
rows via existing backward-compat shim
[Change] lmd_lifecycle.sh: remove dead stage_started meta-schema parser case
and initializer (no code ever wrote stage_started to any meta file)
[Change] lmd_scan.sh, lmd_quarantine.sh: remove unused local variables (_total
in _wait_workers_with_progress, fname in restore, _filtered_sess/_ql
in _batch_quarantine)
[Change] conf.maldet / maldet.1: correct post_scan_hook_timeout documentation — prior
text claimed a 5-second grace period before SIGKILL, but timeout(1) is invoked
without --kill-after, so a hook that traps SIGTERM will run indefinitely; pr#478
-- Bug Fixes --
[Fix] monitor: restore v1.6.6 ERE semantics for ignore_inotify entries;
introduce literal: per-entry prefix for opt-in escape of paths
containing regex metachars; issue #484
[Fix] monitor: ignore_paths now uses grep -E -vf (ERE) to match scan-mode
semantics; fixes silent regression from awk index() substring
filter introduced by monitor-mode redesign; issue #484
[Fix] ignore_inotify.defaults: re-add "scantemp." (trailing-dot-safe) for
ClamAV scan-temp directory noise; trailing dot prevents matching
user files named scantemplate.*; issue #484, #104, #431
[Fix] monitor: ownership filters (scan_ignore_root/user/group) unconditionally
excluded root-owned files in monitor mode; root-owned malware drops silently
produced 0 quarantine hits despite detection; gated behind new
monitor_scan_owner_filters toggle (default 0 = filters off); issue #485
[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
[Fix] --json-report list: path field parity with text mode; unified JSON escaping
across reports[]/active[]/stopped[]; dedup+escape rewrite eliminates O(N^2)
hang at ~20K sessions (82s → 1.7s); active[] gains lifecycle schema
(eta/workers/sig_version/progress); stopped[] gains elapsed; issue #482
[Fix] --json-report list: reports[] now globally sorted by started_epoch
(newest first) across TSV index + legacy-session passes (pre-fix emitted
index entries in append-order then legacy, producing visible date
inversions); reports[]/active[]/stopped[] all gain started_epoch integer;
stopped[] gains stopped_epoch; pass-2 glob skips legacy session.*.html
artifacts (fixes 12s+ hangs on installs with pre-on-demand-HTML files);
issue #483
[Fix] clamav_linksigs: guard mktemp staging failure to prevent writing signature
files into the filesystem root. When mktemp -d fails, the empty _staging
variable caused "cp -f ... /" as root; pr#478
[Fix] _json_escape_string: replaced sed pipeline with bash parameter expansion;
fixes FreeBSD breakage (sed="$sed -E" execve) and multi-newline escaping
for post_scan_hook JSON payloads; pr#478
[Fix] DEB: override_dh_fixperms restores 640/750 modes (72 files incl.
conf.maldet.hookscan.default, /etc/default/maldet, and 15 sub-library shell
files) after debhelper normalizes executables to 755 and non-executables
to 644; RPM uses %attr() and is unaffected; pr#478
[Fix] --test-alert digest: seed $tmpdir/digest.hook.alert cursor to _orig_size
so first-run tlog_read dispatches synthetic hits on fresh installs; prevents
real-hit leakage into test email and re-send on next real digest; pr#478
[Fix] Hook validation: world-writable detection used broken glob extraction that always
returned empty string; replaced with bash substring to correctly extract last octal
digit; pr#478
[Fix] Scan lifecycle JSON: workers field emitted unquoted "auto" string producing invalid
JSON; now resolves to integer before writing scan.meta; pr#478
[Fix] Report list: --format tsv silently returned text output; now returns explicit error
directing users to --report active for TSV output; pr#478
[Fix] Background scans (-b): scan process recorded parent shell PID ($$) instead of
actual forked PID (BASHPID); --kill/--pause/--stop targeted wrong process; scan.meta
now stores ns_pid separately from pid for correct liveness/signal delivery
[Fix] Scan progress: clear TTY progress line before stage-transition output to
prevent overwritten/garbled text when scan moves between stages
[Fix] -L/--list-active: elapsed time showed 0h 00m for running scans; now
computed live from scan start epoch instead of reading the completed-only
meta field
[Fix] Checkpoint resume: eval replaced with printf -v and options gated through
-co allowlist; rejects PATH, IFS, LD_PRELOAD from tampered checkpoint files
[Fix] CLI error messages (--user, -s, -q, -n, unrecognized option) now write to
stderr; -co validation failure now exits instead of continuing silently
[Fix] checkout: returns error when file/directory not found (was silent return 0)
[Fix] smtp-relay: guard html_file before cat on text-format emails (was cat "")
[Fix] JSON report list hang: index-first hybrid eliminates O(N) per-file glob on servers
without session.index; legacy plaintext sessions preserved
[Fix] Package manifests: add lmd_lifecycle.sh to RPM spec, DEB rules, symlink-manifest
[Fix] Scan pause: suppress console progress and elapsed timer during --pause; elapsed
excludes paused duration
[Fix] CSIG compiler: reject invalid && separator and universal subsigs in OR groups
[Fix] install.sh: detect supervisor and legacy monitor; graceful stop+restart on upgrade;
man page compression on fresh install; backup robustness; no longer kills inotify
monitoring; Slackware init per rc.NAME convention; preserve hookscan config and
pub/ state; issue #414
[Fix] purge() dotfile glob: replace * glob with find -delete to catch dotfiles in
tmp/quarantine/sess
[Fix] Quarantine: correct scan ID in report warning; TOCTOU mv exit code check; inode
naming replaces $RANDOM; batch signature name; colon truncation; -q batch summary
now prints to stdout; issue #399
[Fix] Temp file leaks: cleanup .alert_html.* after email dispatch; add clean.$$ and
suspend.users.$$ to _scan_cleanup() rm list
[Fix] Restore orphan: remove .info metadata after successful quarantine restore
[Fix] Scan: extract hitname from session TSV for hook output
[Fix] ClamAV: sig deployment permissions 644, empty sig cleanup, linksigs ordering,
version-gated .hsb; artifacts cleaned from data dirs when ClamAV disabled;
MD5 merge format corrected for custom user signatures; _count_signatures()
counts from on-disk files instead of runtime variables
[Fix] --report: recognize "latest" as alias for "newest"
[Fix] -e list: strip timezone offset from TSV session timestamps for consistent alignment
[Fix] get_remote_file: track temp files (.tmpf_get.*) and clean up at end of remote
operations
[Fix] sigup: write downloaded version to maldet.sigs.ver to prevent CDN desync
repeated downloads
[Fix] Scan engine: isolate worker temp files using parent PID namespace to prevent
concurrent scan conflicts
[Fix] scan(): exit 1 when all provided scan paths do not exist
[Fix] Hash scanning: handle md5sum/sha256sum backslash-escaped filenames
[Fix] Alerting: Slack migrated from deprecated files.upload API; Telegram /bot prefix;
token security via curl -K; genalert() state isolation; issue #387, #458, #461, #426
[Fix] Security: _safe_source_conf() allowlist (79 vars); conf.maldet 0640; dirs 750;
remote config RCE prevention; semver validation prevents directory traversal;
hookscan.sh validates ModSecurity filenames; --user validates against path traversal;
JSON injection prevention; email header injection prevention
[Fix] view_report(): on-demand text rendering from TSV; "newest" and empty scan ID
resolve to most recent session; issue #336
[Fix] sigignore(): moved to runtime copies in gensigs(); signatures no longer
irrecoverably lost between signature updates
[Fix] CLI robustness: 19 bare exit statements given explicit codes; -c, -x, -i, -q,
-n, -s, --web-proxy reject missing/empty arguments; issue #366
[Fix] FreeBSD: detection via uname -s; id -u (POSIX); restore() touch -t for mtime;
portable command cp/mv/rm throughout; scan_strlen guard (wc -L is GNU-only)
[Fix] clamselector(): logs warning on clamd test failure instead of silently switching;
no longer overwrites user scan_max_filesize; issue #452, #410
[Fix] cron.daily: flock lock leak to backgrounded scans; lockfile prevents overlapping
runs; config errors propagate to cron; issue #373
[Fix] find/ignore: ignore_file_ext, --exclude-regex, --include-regex, ignore_paths
converted to bash arrays; scan_ignore_user/group handles non-existent entries;
issue #446, #438, #450, #72, #335, #440
[Fix] Alert rendering: hit type regex handles {MD5}/{CSIG}/{SHA256} prefixes; empty
quarpath field shift in text reports; empty SUMMARY_* tokens in single-hit messaging
[Fix] tlog trim cursor unit mismatch: purge() and monitor_cycle() subtracted byte
count from line-count cursor; issue #227; replace ed dependency; issue #308
[Fix] Miscellaneous portability: mktemp replaces $RANDOM/$$; command -v replaces
which; grep -E replaces egrep; POSIX ':' chown separator; hardcoded binary
paths replaced with command -v discovered variables
[Fix] restore() and restore_hitlist(): return exit 1 on all error paths
[Fix] YARA rules no longer counted as active when no engine available (YARA(no engine))
[Fix] Quarantine restore: quote $file_mode in chmod to prevent misinterpretation
when .info file is corrupt or has fewer than 9 fields
[Fix] Packaging: tar fallback excludes pkg/ directory (was pkg/build only)
[Fix] RPM/DEB: packages now run signature update on fresh install (was missing);
migrate core sig files from tarball installs; link ClamAV sigs and signal
clamd reload; shared pkg-postinst.sh for install.sh post-install parity
[Fix] RPM/DEB %pre: defensive detection for dir-vs-symlink cpio conflict; install
no longer fails with "rename failed - Is a directory" when prior install.sh
or partial install left real directories at symlink-target paths