feat(validator): M81-4 per-module health derivation (Day 1+2)#381
Merged
feat(validator): M81-4 per-module health derivation (Day 1+2)#381
Conversation
Implements the module health derivation from HEALTH_METRIC_DERIVATION_v1.81.md. Each module evaluated on 4 axes: config, structural, runtime, effective. Added: - module_health.go: evaluateModuleHealth() + per-module evaluators (BotGuard, DDoS, Portscan, LoginMon, Blacklist) - Config readers: readConfigBool() with .local override chain - BotGuard dual-family structural check (Rule 9 per-family aggregation) - StatusIdle enum + evaluateOverallStatus emits idle when all modules idle - SchemaVersionCurrent = "1.81.0" + schema_version in JSON output - ModuleHealthMap, ConfigState, StructuralState, EffectiveState types - BlacklistHealth with manual/feeds/geoban sub-states Gap fixes applied: - StatusIdle added to overall status enum (was missing) - Geoban DB missing = degraded (was stale — per M81-3 contract) - Feeds = loaded when configured (not idle — per shared counter rule) - BotGuard IPv4+IPv6 dual-family evaluation Tests: 32/32 PASS on lab4 (17 new + 15 existing, 0 regressions). Live output verified on lab4 with correct per-module states. Known gaps (Day 2): DDoS counter evidence, blacklist element counting, portscan effective axis stub. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Day 2 evidence fidelity: effective axis now uses real kernel data. DDoS: - GetCounter() method on RulesetDocument reads named counter packets - evaluateDDoS() checks 5 enforcement counters (ct_ssh/ct_http/ct_mail/ syn_rate/syn_prefix_drop). Any > 0 = ENFORCING. All zero = IDLE. - Live verified on lab4: DDoS now reports "enforcing" from real kernel counter evidence (input_syn_rate_exceeded > 0) Portscan: - Effective axis explicitly set to IDLE with code documentation: no dedicated counter exists, effective evidence incomplete per M81-3 portscan contract. Real enforcement evidence requires kernel log parsing (M81-7 Day 6 scope). - TestPortscanEffectiveAlwaysIdle pinned as regression guard Blacklist: - countSetElements() remains stubbed (nft -j list ruleset does not include set elements — per-set queries needed, future enhancement) - Documentation updated in stub explaining the limitation Overall status: - Now correctly PROTECTED on lab4 (DDoS enforcing = active module) - Was IDLE on Day 1 (no active modules detected) Tests: 35/35 PASS on lab4 (3 new + 32 existing, 0 regressions). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Contributor
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
Implements the M81-6 JSON_SCHEMA_SPEC_v1.81.md as a separate projection
layer between internal state and JSON output.
Added:
- health_output.go: HealthOutput, ModulesJSON, ModuleJSON, BlacklistJSON,
ConsistencyJSON, FindingJSON — the frozen schema types
- health_mapper.go: MapToHealthOutput() — single mapping point from
ValidationResult to HealthOutput. Enforces:
- disabled modules: config only, no other fields
- kernel-only modules: no runtime field
- daemon-dependent modules: runtime lowercase
- no nulls
- vocabulary-approved values only
- health_mapper_test.go: 10 schema compliance tests including:
- schema version check
- all 4 status values mapped
- disabled omits axes
- kernel-only omits runtime
- daemon-dependent includes runtime
- no-nulls golden test
- blacklist composite mapping
- consistency stub
- findings mapping
Changed:
- cli.go: ToJSON() now uses MapToHealthOutput() (frozen schema).
ToJSONLegacy() preserves raw serialization for rebuild safety code.
StatusString() + ExitCode() handle IDLE (exit 0).
Backward compat: .status and .chain_counts.total_chains still present
in the frozen schema — rebuild safety code in cmd_firewall.sh unaffected.
Tests: 45/45 PASS on lab4 (10 new + 35 existing, 0 regressions).
Live verified: lab4 emits frozen schema with all M81-6 constraints met.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b3655f0 to
4f67338
Compare
Implements CLI_OUTPUT_SPEC_v1.81.md vocabulary enforcement (partial). Fixed: - cmd_status.sh: "healthy" → "protected" in health_word mapping (4 sites) - cmd_status.sh: "threats_blocked_24h" → "enforcement_events_24h" in JSON output (per M81-5 banned terms + M81-6 frozen schema alignment) - cmd_list.sh: "protecting your IPs" → "in the whitelist" (1 site) Added: - tests/test_banned_phrases.sh: M81-5 regression scanner Scans cli/*.sh for 7 banned phrase patterns. Informational for v1.81 (24 remaining findings in health subsystem). Full enforcement is v1.82. Remaining (v1.82 scope): - "OK" in health context (19 sites, mostly cmd_health_analysis.sh + cmd_health_core.sh — requires health subsystem rewrite) - "healthy" in 2 deeper health paths - "working" in cmd_queue.sh (queue context, low severity) Findings: 27 → 24 (3 fixed, 24 deferred to v1.82 CLI phase) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolves 4 critical failures from SPEC_CONFORMANCE_AUDIT_v1.81.md.
CF-1: service_state.nftband now emits uppercase ("RUNNING"|"STOPPED"|
"ERROR") matching JSON_SCHEMA_SPEC §5. Module runtime fields remain
lowercase per spec. Fix: removed normalizeRuntime() call for
service_state, using raw RuntimeState enum string directly.
CF-2: geoban DB missing now emits "stale" (in allowed blacklist sub-state
enum) instead of "degraded" (not in enum). Also emits VAL-GEOBAN-001
finding (SeverityWarn) for visibility. New finding code registered in
types.go. Module findings collected via moduleFindings slice and
appended to result.Findings.
CF-3: JSON_SCHEMA_SPEC_v1.81.md updated to mark families and module_truth
as "legacy-only (ToJSONLegacy), not part of M81-6 primary schema."
The M81-6 HealthOutput replaces module_truth with the richer modules
block. No code change — spec alignment only.
CF-4: countSetElements() stub documented as v1.81 known limitation with
explicit consequence description and v1.82 fix target. BotGuard
ENFORCING/OBSERVING and blacklist PRIMED states are unreachable
from the validator until per-set queries are implemented.
All tests pass on lab4. Live output verified: uppercase service_state,
geoban="stale" with VAL-GEOBAN-001 finding, no schema violations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bumps VERSION 1.80.1 -> 1.81.0. Full CHANGELOG with: M81-4/5/6 implementation, portscan classic fix, CF-1..4 audit fixes, 12 M81 spec documents, known limitations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bf9e46b to
2d33872
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements M81-4 (HEALTH_METRIC_DERIVATION_v1.81.md) in the Go validator.
Per-module 4-axis health evaluation: config, structural, runtime, effective.
Day 1 — Module health evaluator
module_health.go: evaluateModuleHealth() + per-module evaluators(BotGuard, DDoS, Portscan, LoginMon, Blacklist)
Day 2 — Evidence fidelity
(5 enforcement counters, any > 0 = ENFORCING)
Live output (lab4)
{ "schema_version": "1.81.0", "status": "protected", "modules": { "botguard": { "config": "disabled" }, "ddos": { "config": "enabled", "structural": "present", "effective": "enforcing" }, "portscan": { "config": "enabled", "structural": "present", "effective": "idle" }, "loginmon": { "config": "enabled", "structural": "present", "runtime": "RUNNING", "effective": "idle" }, "blacklist": { "manual": {"state":"idle"}, "feeds": {"state":"disabled"}, "geoban": {"state":"degraded"} } } }Tests
35/35 PASS on lab4. 20 new M81-4 tests + 15 existing B80 tests.
Scope
internal/validator/only. No shell. No CLI. No runtime.🤖 Generated with Claude Code