Skip to content

Commit f6703c6

Browse files
committed
[New] --json-report list: started_epoch parity across active/stopped/reports; issue #483
[New] _lmd_render_json_list() active[]: emit started + started_epoch from _meta_started_hr / _meta_started (already loaded by _lifecycle_read_meta). [New] _lmd_render_json_list() stopped[]: emit started + started_epoch + stopped_epoch for symmetry with reports[]; existing stopped_hr preserved (no schema break). [New] tests/31-json-report.bats: 2 BATS cases using synthetic scan.meta.* fixtures to assert active[] and stopped[] carry started_epoch; stopped[] asserts stopped_epoch. [Change] CHANGELOG + CHANGELOG.RELEASE: v2.0.1 Bug Fixes — parity entry.
1 parent 4b01f8a commit f6703c6

4 files changed

Lines changed: 123 additions & 4 deletions

File tree

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,9 @@ v2.0.1 | Mar 25 2026:
336336
change (existing "started" string preserved); issue #483
337337
[New] tests: 3 BATS regressions for --json-report list sort order,
338338
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
339342

340343
v1.6.6.1 | Feb 25 2025:
341344
[Fix] find_recentopts incorrectly escaping find options to the right of ( -mtime .. -ctime ); previously normalized by eval; issue #440, pr#442

CHANGELOG.RELEASE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,6 @@ v2.0.1 | Mar 25 2026:
336336
change (existing "started" string preserved); issue #483
337337
[New] tests: 3 BATS regressions for --json-report list sort order,
338338
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

files/internals/lmd_alert.sh

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,10 @@ _lmd_render_json_list() {
721721
_jl_path=$(_json_escape_string "$_meta_path")
722722
_jl_engine=$(_json_escape_string "${_meta_engine:--}")
723723
_jl_sig=$(_json_escape_string "${_meta_sig_version:--}")
724+
local _jl_started_hr _jl_started_ep
725+
_jl_started_hr=$(_json_escape_string "${_meta_started_hr:--}")
726+
_jl_started_ep="${_meta_started:-0}"
727+
[ "$_jl_started_ep" = "-" ] && _jl_started_ep=0
724728
local _jl_pid="${_meta_pid:-0}"; [ "$_jl_pid" = "-" ] && _jl_pid=0
725729
local _jl_files="${_meta_total_files:-0}"; [ "$_jl_files" = "-" ] && _jl_files=0
726730
local _jl_hits="${_meta_hits:-0}"; [ "$_jl_hits" = "-" ] && _jl_hits=0
@@ -734,8 +738,9 @@ _lmd_render_json_list() {
734738
local _jl_prog_total="${_meta_progress_total:-0}"; [ "$_jl_prog_total" = "-" ] && _jl_prog_total=0
735739
local _jl_eta
736740
_jl_eta=$(_lifecycle_compute_eta "${_meta_engine:-}" "$_jl_elapsed" "$_jl_prog_pos" "$_jl_prog_total")
737-
printf '\n {"scan_id": "%s", "state": "%s", "pid": %s, "path": "%s", "engine": "%s", "total_files": %s, "hits": %s, "elapsed": %s, "eta": %s, "workers": %s, "sig_version": "%s", "progress": {"position": %s, "total": %s}}' \
741+
printf '\n {"scan_id": "%s", "state": "%s", "pid": %s, "path": "%s", "started": "%s", "started_epoch": %s, "engine": "%s", "total_files": %s, "hits": %s, "elapsed": %s, "eta": %s, "workers": %s, "sig_version": "%s", "progress": {"position": %s, "total": %s}}' \
738742
"$_jl_scanid" "$_jl_state" "$_jl_pid" "$_jl_path" \
743+
"$_jl_started_hr" "$_jl_started_ep" \
739744
"$_jl_engine" "$_jl_files" "$_jl_hits" "$_jl_elapsed" \
740745
"$_jl_eta" "$_jl_workers" "$_jl_sig" \
741746
"$_jl_prog_pos" "$_jl_prog_total"
@@ -760,13 +765,22 @@ _lmd_render_json_list() {
760765
_jl_sp=$(_json_escape_string "$_meta_path")
761766
_jl_sstage=$(_json_escape_string "${_meta_stage:--}")
762767
_jl_shr=$(_json_escape_string "${_meta_stopped_hr:-unknown}")
768+
local _jl_sstarted_hr _jl_sstarted_ep _jl_sstopped_ep
769+
_jl_sstarted_hr=$(_json_escape_string "${_meta_started_hr:--}")
770+
_jl_sstarted_ep="${_meta_started:-0}"
771+
[ "$_jl_sstarted_ep" = "-" ] && _jl_sstarted_ep=0
772+
_jl_sstopped_ep="${_meta_stopped:-0}"
773+
[ "$_jl_sstopped_ep" = "-" ] && _jl_sstopped_ep=0
763774
local _jl_sfiles="${_meta_total_files:-0}"; [ "$_jl_sfiles" = "-" ] && _jl_sfiles=0
764775
local _jl_shits="${_meta_hits:-0}"; [ "$_jl_shits" = "-" ] && _jl_shits=0
765776
local _jl_selapsed="${_meta_elapsed:-0}"; [ "$_jl_selapsed" = "-" ] && _jl_selapsed=0
766777
local _jl_sworkers="${_meta_workers:-0}"; [ "$_jl_sworkers" = "-" ] && _jl_sworkers=0
767-
printf '\n {"scan_id": "%s", "stage": "%s", "total_files": %s, "hits": %s, "elapsed": %s, "workers": %s, "stopped_hr": "%s", "path": "%s"}' \
768-
"$_jl_stopped_sid" "$_jl_sstage" "$_jl_sfiles" "$_jl_shits" \
769-
"$_jl_selapsed" "$_jl_sworkers" "$_jl_shr" "$_jl_sp"
778+
printf '\n {"scan_id": "%s", "stage": "%s", "started": "%s", "started_epoch": %s, "total_files": %s, "hits": %s, "elapsed": %s, "workers": %s, "stopped_hr": "%s", "stopped_epoch": %s, "path": "%s"}' \
779+
"$_jl_stopped_sid" "$_jl_sstage" \
780+
"$_jl_sstarted_hr" "$_jl_sstarted_ep" \
781+
"$_jl_sfiles" "$_jl_shits" \
782+
"$_jl_selapsed" "$_jl_sworkers" \
783+
"$_jl_shr" "$_jl_sstopped_ep" "$_jl_sp"
770784
fi
771785
done
772786

tests/31-json-report.bats

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,3 +576,102 @@ print(e if e is not None else "MISSING")
576576
[[ "$got" -gt 4000000000 ]]
577577
fi
578578
}
579+
580+
# --- Test 31: active[] carries started + started_epoch ---
581+
# Regression guard for issue #483 parity extension: active[] must expose
582+
# an absolute start timestamp, not only elapsed seconds. Uses synthetic
583+
# scan.meta.* fixture (pattern from tests/39-lifecycle-list.bats:42).
584+
@test "--json-report list active[] entries include started_epoch" {
585+
local sessdir="$LMD_INSTALL/sess"
586+
local sid="260418-2359.77771"
587+
local _started_epoch
588+
_started_epoch=$(date +%s)
589+
local _started_hr
590+
_started_hr=$(date "+%b %d %Y %H:%M:%S %z")
591+
# Fabricate a running scan meta file. Use our own PID for state=running
592+
# liveness check in _lifecycle_detect_state — $$ is guaranteed alive.
593+
cat > "$sessdir/scan.meta.$sid" <<EOF
594+
#LMD_META:v1
595+
pid=$$
596+
ppid=$PPID
597+
started=$_started_epoch
598+
started_hr=$_started_hr
599+
path=/home/testuser
600+
total_files=1000
601+
workers=4
602+
engine=native
603+
hashtype=md5
604+
stages=md5,hex
605+
sig_version=2026041801
606+
options=
607+
state=running
608+
hits=0
609+
progress_pos=100
610+
progress_total=1000
611+
elapsed=30
612+
EOF
613+
run maldet --json-report list
614+
rm -f "$sessdir/scan.meta.$sid"
615+
assert_success
616+
# Active entry must include the scan_id (proves the synthetic meta was
617+
# picked up by the active[] pass), started string, and started_epoch int.
618+
assert_output --partial "\"$sid\""
619+
assert_output --partial '"started"'
620+
assert_output --partial '"started_epoch"'
621+
# started_epoch is an unquoted integer matching the value we wrote.
622+
[[ "$output" =~ \"started_epoch\":[[:space:]]+$_started_epoch ]]
623+
}
624+
625+
# --- Test 32: stopped[] carries started + started_epoch + stopped_epoch ---
626+
# Regression guard for issue #483 parity extension: stopped[] must expose
627+
# machine-readable start AND stop timestamps (stopped_hr stays for backward
628+
# compat). Fabricate a stopped scan with matching checkpoint file so
629+
# _lifecycle_detect_state returns "stopped" and the entry is emitted.
630+
@test "--json-report list stopped[] entries include started_epoch and stopped_epoch" {
631+
local sessdir="$LMD_INSTALL/sess"
632+
local sid="260418-2358.77772"
633+
local _started_epoch _stopped_epoch
634+
_started_epoch=$(( $(date +%s) - 3600 ))
635+
_stopped_epoch=$(( $(date +%s) - 1800 ))
636+
local _started_hr
637+
_started_hr=$(date -d "@$_started_epoch" "+%b %d %Y %H:%M:%S %z")
638+
local _stopped_hr
639+
_stopped_hr=$(date -d "@$_stopped_epoch" "+%b %d %Y %H:%M:%S %z")
640+
# Fabricate stopped meta. pid need not be alive for stopped state.
641+
cat > "$sessdir/scan.meta.$sid" <<EOF
642+
#LMD_META:v1
643+
pid=1
644+
ppid=1
645+
started=$_started_epoch
646+
started_hr=$_started_hr
647+
stopped=$_stopped_epoch
648+
stopped_hr=$_stopped_hr
649+
path=/home/testuser
650+
total_files=1000
651+
workers=4
652+
engine=native
653+
hashtype=md5
654+
stages=md5,hex
655+
sig_version=2026041801
656+
options=
657+
state=stopped
658+
stage=hex
659+
hits=0
660+
progress_pos=500
661+
progress_total=1000
662+
elapsed=1800
663+
EOF
664+
# Checkpoint file is REQUIRED for stopped[] enumeration
665+
# (_lmd_render_json_list gates on its presence at line ~755).
666+
echo "stage=hex" > "$sessdir/scan.checkpoint.$sid"
667+
run maldet --json-report list
668+
rm -f "$sessdir/scan.meta.$sid" "$sessdir/scan.checkpoint.$sid"
669+
assert_success
670+
assert_output --partial "\"$sid\""
671+
assert_output --partial '"started"'
672+
assert_output --partial '"started_epoch"'
673+
assert_output --partial '"stopped_hr"'
674+
assert_output --partial '"stopped_epoch"'
675+
[[ "$output" =~ \"started_epoch\":[[:space:]]+$_started_epoch ]]
676+
[[ "$output" =~ \"stopped_epoch\":[[:space:]]+$_stopped_epoch ]]
677+
}

0 commit comments

Comments
 (0)