Skip to content

Commit 02aef9a

Browse files
committed
[Change] tests: end-to-end ignore_inotify ERE + ignore_paths regression; issue #484
[New] tests/48-monitor-ignore-regex.bats: 11 integration tests covering user regex passthrough, literal: prefix escape, defaults auto-escape, ignore_paths ERE consistency with scan-mode, malformed-regex symmetry (ignore_inotify + ignore_paths), and the sentinel against the misleading comment at lmd_monitor.sh:74 [Change] CHANGELOG, CHANGELOG.RELEASE: v2.0.1 [Change] entry
1 parent 49f7b5a commit 02aef9a

3 files changed

Lines changed: 237 additions & 0 deletions

File tree

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ v2.0.1 | Mar 25 2026:
9191

9292
[New] _monitor_to_ere_entry(): semantic dispatch helper for ignore_inotify
9393
entry preparation (defaults-always-escape, user-raw-or-literal:)
94+
[Change] tests/48-monitor-ignore-regex.bats: integration coverage for
95+
ignore_inotify ERE + literal: prefix + ignore_paths monitor-mode
96+
consistency + malformed-regex symmetry; issue #484
9497
[Change] tests: prune two tautological assertions — the
9598
"uninstall.sh delegates service removal to pkg_service_uninstall"
9699
grep-for-token check in 01-install-cli.bats (string presence, not

CHANGELOG.RELEASE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ v2.0.1 | Mar 25 2026:
9191

9292
[New] _monitor_to_ere_entry(): semantic dispatch helper for ignore_inotify
9393
entry preparation (defaults-always-escape, user-raw-or-literal:)
94+
[Change] tests/48-monitor-ignore-regex.bats: integration coverage for
95+
ignore_inotify ERE + literal: prefix + ignore_paths monitor-mode
96+
consistency + malformed-regex symmetry; issue #484
9497
[Change] tests: prune two tautological assertions — the
9598
"uninstall.sh delegates service removal to pkg_service_uninstall"
9699
grep-for-token check in 01-install-cli.bats (string presence, not

tests/48-monitor-ignore-regex.bats

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
#!/usr/bin/env bats
2+
# 48-monitor-ignore-regex.bats — end-to-end ignore_inotify ERE + ignore_paths
3+
# monitor-mode ERE consistency (issue #484 — v1.6.6 regex semantics restored)
4+
5+
load '/usr/local/lib/bats/bats-support/load'
6+
load '/usr/local/lib/bats/bats-assert/load'
7+
8+
LMD_INSTALL="/usr/local/maldetect"
9+
10+
_source_lmd_stack() {
11+
set +eu
12+
trap - ERR
13+
export inspath="$LMD_INSTALL"
14+
source "$LMD_INSTALL/internals/internals.conf"
15+
source "$LMD_INSTALL/conf.maldet"
16+
source "$LMD_INSTALL/internals/tlog_lib.sh"
17+
source "$LMD_INSTALL/internals/elog_lib.sh"
18+
source "$LMD_INSTALL/internals/alert_lib.sh"
19+
source "$LMD_INSTALL/internals/lmd_alert.sh"
20+
source "$LMD_INSTALL/internals/lmd.lib.sh"
21+
set -eu
22+
}
23+
24+
# Helper: build the exclude-regex string from a fixture pair.
25+
#
26+
# Mirrors the exclude-regex builder inline block in monitor_init()
27+
# (lmd_monitor.sh Change E / issue #484). The builder has no callable
28+
# name — it is a while-read loop inline with session-scoped state —
29+
# so this helper is a faithful, intentional duplicate for integration
30+
# test isolation. If the production loop changes (prefix letters,
31+
# separator, parenthesis wrapping), update this helper AND the tests
32+
# below in the same commit.
33+
_build_exclude() {
34+
_source_lmd_stack
35+
local _user="$1" _defaults="$2"
36+
local _igregexp="" _line _src _ent _prepared
37+
while IFS= read -r _line; do
38+
_src="${_line:0:1}"
39+
_ent="${_line:2}"
40+
case "$_src" in
41+
u) _prepared=$(_monitor_to_ere_entry "$_ent" "user") ;;
42+
d) _prepared=$(_monitor_to_ere_entry "$_ent" "defaults") ;;
43+
*) continue ;;
44+
esac
45+
[ -z "$_prepared" ] && continue
46+
[ -n "$_igregexp" ] && _igregexp="$_igregexp|$_prepared" || _igregexp="($_prepared"
47+
done < <(_monitor_load_ignore_inotify_union "$_user" "$_defaults")
48+
[ -n "$_igregexp" ] && printf '%s)' "$_igregexp"
49+
}
50+
51+
# bats test_tags=monitor,integration,issue484
52+
@test "regex: ^/tmp/.*scantem.* excludes ClamAV scantemp parent directory" {
53+
local tmpdir; tmpdir=$(mktemp -d)
54+
printf '%s\n' '^/tmp/.*scantem.*' > "$tmpdir/user"
55+
: > "$tmpdir/defaults"
56+
local regex; regex=$(_build_exclude "$tmpdir/user" "$tmpdir/defaults")
57+
local sample='/tmp/20260418_192807-scantemp.a96549c23f'
58+
# User regex must survive unescaped; sample must match it.
59+
[[ "$sample" =~ ^/tmp/.*scantem.* ]]
60+
run bash -c "printf '%s\n' '$sample' | grep -E -v '$regex'"
61+
[ -z "$output" ] # excluded
62+
rm -rf "$tmpdir"
63+
}
64+
65+
# bats test_tags=monitor,integration,issue484
66+
@test "regex: ^/var/tmp/.*scantem.* excludes ClamAV scantemp (Gazoo's pattern)" {
67+
local tmpdir; tmpdir=$(mktemp -d)
68+
printf '%s\n' '^/var/tmp/.*scantem.*' > "$tmpdir/user"
69+
: > "$tmpdir/defaults"
70+
local regex; regex=$(_build_exclude "$tmpdir/user" "$tmpdir/defaults")
71+
local sample='/var/tmp/20260418_192807-scantemp.a96549c23f/clamav-HASH.tmp'
72+
run bash -c "printf '%s\n' '$sample' | grep -E -v '$regex'"
73+
[ -z "$output" ]
74+
rm -rf "$tmpdir"
75+
}
76+
77+
# bats test_tags=monitor,integration,issue484
78+
@test "regex: Plesk vhost ^/var/www/vhosts/.*/logs/.*$ pattern works end-to-end" {
79+
local tmpdir; tmpdir=$(mktemp -d)
80+
printf '%s\n' '^/var/www/vhosts/.*/logs/.*$' > "$tmpdir/user"
81+
: > "$tmpdir/defaults"
82+
local regex; regex=$(_build_exclude "$tmpdir/user" "$tmpdir/defaults")
83+
local matches='/var/www/vhosts/example.com/logs/access_log'
84+
local nomatch='/var/www/vhosts/example.com/httpdocs/shell.php'
85+
run bash -c "printf '%s\n' '$matches' | grep -E -v '$regex'"
86+
[ -z "$output" ] # excluded — Plesk logs
87+
run bash -c "printf '%s\n' '$nomatch' | grep -E -v '$regex'"
88+
[ "$output" = "$nomatch" ] # NOT excluded — real web content
89+
rm -rf "$tmpdir"
90+
}
91+
92+
# bats test_tags=monitor,integration,issue484
93+
@test "literal: user entry 'literal:/tmp/app.cache' matches the literal path only" {
94+
local tmpdir; tmpdir=$(mktemp -d)
95+
printf '%s\n' 'literal:/tmp/app.cache' > "$tmpdir/user"
96+
: > "$tmpdir/defaults"
97+
local regex; regex=$(_build_exclude "$tmpdir/user" "$tmpdir/defaults")
98+
# Literal path matches; "app.cache" substring in unrelated path does not.
99+
run bash -c "printf '%s\n' '/tmp/app.cache' | grep -E -v '$regex'"
100+
[ -z "$output" ]
101+
run bash -c "printf '%s\n' '/tmp/appXcache' | grep -E -v '$regex'"
102+
[ "$output" = "/tmp/appXcache" ] # literal dot escape prevents .-match
103+
rm -rf "$tmpdir"
104+
}
105+
106+
# bats test_tags=monitor,integration,issue484
107+
@test "defaults: sql-temptable- from ignore_inotify.defaults excludes MySQL temp" {
108+
local tmpdir; tmpdir=$(mktemp -d)
109+
: > "$tmpdir/user"
110+
printf '%s\n' 'sql-temptable-' > "$tmpdir/defaults"
111+
local regex; regex=$(_build_exclude "$tmpdir/user" "$tmpdir/defaults")
112+
local sample='/var/lib/mysql/sql-temptable-12.MAD'
113+
run bash -c "printf '%s\n' '$sample' | grep -E -v '$regex'"
114+
[ -z "$output" ]
115+
rm -rf "$tmpdir"
116+
}
117+
118+
# bats test_tags=monitor,integration,issue484
119+
@test "mixed: user regex + literal: entry + defaults literal all coexist in exclude group" {
120+
local tmpdir; tmpdir=$(mktemp -d)
121+
printf '%s\n' '^/tmp/.*scantem.*' 'literal:/var/log/foo.bar' > "$tmpdir/user"
122+
printf '%s\n' '/var/tmp/systemd-private-' > "$tmpdir/defaults"
123+
local regex; regex=$(_build_exclude "$tmpdir/user" "$tmpdir/defaults")
124+
# All three should produce matching exclusions.
125+
run bash -c "printf '%s\n' '/tmp/20260418-scantemp.X' | grep -E -v '$regex'"
126+
[ -z "$output" ]
127+
run bash -c "printf '%s\n' '/var/log/foo.bar' | grep -E -v '$regex'"
128+
[ -z "$output" ]
129+
run bash -c "printf '%s\n' '/var/tmp/systemd-private-svc-xyz' | grep -E -v '$regex'"
130+
[ -z "$output" ]
131+
rm -rf "$tmpdir"
132+
}
133+
134+
# bats test_tags=monitor,integration,issue484
135+
@test "ignore_paths: monitor-mode ERE ^/var/www/vhosts/[^/]+/tmp/ pattern filters events" {
136+
# Simulates the monitor-mode pipe: _monitor_filter_events extracts paths,
137+
# grep -E -vf "$ignore_paths" filters them. Assert that a middle-wildcard
138+
# ERE in ignore_paths works in monitor mode (matches scan-mode semantics).
139+
_source_lmd_stack
140+
local tmpdir; tmpdir=$(mktemp -d)
141+
cat > "$tmpdir/events" <<'EVENTS'
142+
/var/www/vhosts/a.com/httpdocs/shell.php CREATE 18 Mar 10:30:01
143+
/var/www/vhosts/a.com/tmp/scratch.txt CREATE 18 Mar 10:30:02
144+
/home/user/public_html/index.php CREATE 18 Mar 10:30:03
145+
EVENTS
146+
printf '%s\n' '^/var/www/vhosts/[^/]+/tmp/' > "$tmpdir/ignore"
147+
# export -f required so bash -c subshell can call the function.
148+
export -f _monitor_filter_events
149+
run bash -c '_monitor_filter_events < "$1" | grep -E -vf "$2"' _ "$tmpdir/events" "$tmpdir/ignore"
150+
[ "$status" -eq 0 ]
151+
echo "$output" | grep -q "shell.php"
152+
echo "$output" | grep -q "index.php"
153+
! echo "$output" | grep -q "scratch.txt"
154+
rm -rf "$tmpdir"
155+
}
156+
157+
# bats test_tags=monitor,integration,issue484
158+
@test "ignore_paths: empty file → no filter stage, all events pass" {
159+
_source_lmd_stack
160+
local tmpdir; tmpdir=$(mktemp -d)
161+
cat > "$tmpdir/events" <<'EVENTS'
162+
/home/user/file.php CREATE 18 Mar 10:30:01
163+
EVENTS
164+
: > "$tmpdir/ignore"
165+
# With empty ignore file, new pipe must bypass grep -E -vf entirely.
166+
# Directly exercise via bash conditional mirroring lmd_monitor.sh caller.
167+
# export -f required so bash -c subshell can call the function.
168+
export -f _monitor_filter_events
169+
run bash -c '
170+
if [ -s "$2" ]; then
171+
_monitor_filter_events < "$1" | grep -E -vf "$2"
172+
else
173+
_monitor_filter_events < "$1"
174+
fi' _ "$tmpdir/events" "$tmpdir/ignore"
175+
[ "$status" -eq 0 ]
176+
echo "$output" | grep -q "file.php"
177+
rm -rf "$tmpdir"
178+
}
179+
180+
# bats test_tags=monitor,integration,issue484
181+
@test "union: exclude-regex builder wraps multiple entries in single OR group" {
182+
local tmpdir; tmpdir=$(mktemp -d)
183+
printf '%s\n' '/a' '/b' '/c' > "$tmpdir/user"
184+
: > "$tmpdir/defaults"
185+
local regex; regex=$(_build_exclude "$tmpdir/user" "$tmpdir/defaults")
186+
# Exactly one open-paren and one close-paren.
187+
[ "$(echo "$regex" | grep -c '^(')" -eq 1 ]
188+
[ "$(echo "$regex" | grep -cE '\)$')" -eq 1 ]
189+
echo "$regex" | grep -qE '/a\|/b\|/c'
190+
rm -rf "$tmpdir"
191+
}
192+
193+
# bats test_tags=monitor,integration,issue484
194+
@test "regression: misleading 'matching current grep -vf semantics' comment removed" {
195+
run grep -n 'matching current grep -vf' "$LMD_INSTALL/internals/lmd_monitor.sh"
196+
[ "$status" -eq 1 ] # grep exits 1 when no match — what we want
197+
}
198+
199+
# bats test_tags=monitor,integration,issue484
200+
@test "ignore_paths: malformed regex produces grep -E error without hanging monitor pipe" {
201+
# Gap-coverage: symmetric coverage for ignore_paths:
202+
# a malformed ERE (unclosed bracket) causes grep -E to exit non-zero
203+
# and emit to stderr; the monitor pipe returns an empty event list
204+
# rather than hanging or mis-filtering. Not a silent data-loss path —
205+
# next cycle catches up from the tlog cursor.
206+
_source_lmd_stack
207+
local tmpdir; tmpdir=$(mktemp -d)
208+
cat > "$tmpdir/events" <<'EVENTS'
209+
/home/user/public_html/shell.php CREATE 18 Mar 10:30:01
210+
/home/user/public_html/ok.js MODIFY 18 Mar 10:30:02
211+
EVENTS
212+
printf '%s\n' '[unclosed-bracket' > "$tmpdir/ignore"
213+
# Invoke the production pipe conditional mirror with stderr captured.
214+
# export -f required so bash -c subshell can call the function.
215+
export -f _monitor_filter_events
216+
local _stderr
217+
_stderr=$(mktemp)
218+
run bash -c '
219+
if [ -s "$2" ]; then
220+
_monitor_filter_events < "$1" | grep -E -vf "$2" 2>"$3"
221+
else
222+
_monitor_filter_events < "$1" 2>"$3"
223+
fi' _ "$tmpdir/events" "$tmpdir/ignore" "$_stderr"
224+
# grep emits to stderr (visible for operator diagnosis) and exits
225+
# non-zero on regex compile error; empty or partial event list is
226+
# acceptable. Assertion: the pipe terminates (no hang) and stderr
227+
# contains evidence of the compile failure.
228+
[ -s "$_stderr" ]
229+
grep -qE 'regex|bracket|unmatched|Invalid' "$_stderr"
230+
rm -rf "$tmpdir" "$_stderr"
231+
}

0 commit comments

Comments
 (0)