Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ LogLens also tracks parser coverage telemetry for unsupported or malformed lines
- `top_unknown_patterns`

Common unsupported-pattern buckets include `sshd_connection_closed_preauth`,
`sshd_timeout_or_disconnection`, `sshd_negotiation_failure`, and
`pam_unix_session_closed`. These buckets keep non-finding evidence reviewable
without counting it as detector evidence.
`sshd_timeout_or_disconnection`, `sshd_negotiation_failure`,
`pam_faillock_account_locked`, and `pam_unix_session_closed`. These buckets keep
non-finding evidence reviewable without counting it as detector evidence.

For the parser behavior contract, supported modes, and fixture map, see [`docs/parser-contract.md`](./docs/parser-contract.md).

Expand Down
6 changes: 3 additions & 3 deletions docs/parser-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ This is the main trust boundary: unsupported input should remain inspectable, ev

Stable unsupported-pattern buckets currently exercised by the fixture corpus include
`sshd_connection_closed_preauth`, `sshd_timeout_or_disconnection`,
`sshd_negotiation_failure`, and `pam_unix_session_closed`. They are parser
telemetry and warnings only; detector signal mappings decide which parsed events
can contribute to findings.
`sshd_negotiation_failure`, `pam_faillock_account_locked`, and
`pam_unix_session_closed`. They are parser telemetry and warnings only; detector
signal mappings decide which parsed events can contribute to findings.

## Detection signal boundary

Expand Down
4 changes: 4 additions & 0 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,10 @@ bool parse_pam_faillock_message(std::string_view message, Event& event) {
}

std::string classify_unknown_pam_faillock_pattern(std::string_view message) {
if (message.starts_with("Account temporarily locked for user ")) {
return "pam_faillock_account_locked";
}

if (message.starts_with("User ") && message.find("successfully authenticated") != std::string_view::npos) {
return "pam_faillock_authsucc";
}
Expand Down
2 changes: 2 additions & 0 deletions tests/test_cli.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>

namespace {

void expect(bool condition, const std::string& message) {
if (!condition) {
std::cerr << "test_cli assertion failed: " << message << '\n';
throw std::runtime_error(message);
}
}
Expand Down
21 changes: 11 additions & 10 deletions tests/test_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -605,12 +605,12 @@ void test_syslog_auth_family_fixture_file() {
expect(result.events[11].username == "erin", "expected pam_unix session-opened username");

expect(result.quality.top_unknown_patterns.size() == 5, "expected five syslog auth-family buckets");
expect(result.quality.top_unknown_patterns[0].pattern == "pam_faillock_authsucc",
expect(result.quality.top_unknown_patterns[0].pattern == "pam_faillock_account_locked",
"expected pam_faillock account-locked telemetry bucket");
expect(result.quality.top_unknown_patterns[0].count == 1, "expected one pam_faillock account-locked line");
expect(result.quality.top_unknown_patterns[1].pattern == "pam_faillock_authsucc",
"expected pam_faillock authsucc telemetry bucket");
expect(result.quality.top_unknown_patterns[0].count == 1, "expected one pam_faillock authsucc line");
expect(result.quality.top_unknown_patterns[1].pattern == "pam_faillock_other",
"expected pam_faillock other telemetry bucket");
expect(result.quality.top_unknown_patterns[1].count == 1, "expected one pam_faillock other line");
expect(result.quality.top_unknown_patterns[1].count == 1, "expected one pam_faillock authsucc line");
expect(result.quality.top_unknown_patterns[2].pattern == "pam_sss_authinfo_unavail",
"expected pam_sss authinfo-unavail telemetry bucket");
expect(result.quality.top_unknown_patterns[2].count == 1, "expected one pam_sss authinfo-unavail line");
Expand Down Expand Up @@ -671,12 +671,13 @@ void test_journalctl_auth_family_fixture_file() {
"expected journalctl pam_unix session-opened auth-family event");

expect(result.quality.top_unknown_patterns.size() == 5, "expected five journalctl auth-family buckets");
expect(result.quality.top_unknown_patterns[0].pattern == "pam_faillock_authsucc",
expect(result.quality.top_unknown_patterns[0].pattern == "pam_faillock_account_locked",
"expected journalctl pam_faillock account-locked telemetry bucket");
expect(result.quality.top_unknown_patterns[0].count == 1,
"expected one journalctl pam_faillock account-locked line");
expect(result.quality.top_unknown_patterns[1].pattern == "pam_faillock_authsucc",
"expected journalctl pam_faillock authsucc telemetry bucket");
expect(result.quality.top_unknown_patterns[0].count == 1, "expected one journalctl pam_faillock authsucc line");
expect(result.quality.top_unknown_patterns[1].pattern == "pam_faillock_other",
"expected journalctl pam_faillock other telemetry bucket");
expect(result.quality.top_unknown_patterns[1].count == 1, "expected one journalctl pam_faillock other line");
expect(result.quality.top_unknown_patterns[1].count == 1, "expected one journalctl pam_faillock authsucc line");
expect(result.quality.top_unknown_patterns[2].pattern == "pam_sss_authinfo_unavail",
"expected journalctl pam_sss authinfo-unavail telemetry bucket");
expect(result.quality.top_unknown_patterns[2].count == 1, "expected one journalctl pam_sss authinfo-unavail line");
Expand Down
Loading