@@ -570,6 +570,45 @@ void test_sudo_signals_include_command_and_session_opened() {
570570 " did not expect sudo session-opened to count as terminal auth failure" );
571571}
572572
573+ void test_privilege_auth_failures_are_non_counting_signals () {
574+ const auto events = parse_events (
575+ make_syslog_config (),
576+ " Mar 10 08:21:40 example-host sudo: alice : 1 incorrect password attempt ; TTY=pts/0 ; "
577+ " PWD=/home/user/project ; USER=root ; COMMAND=/usr/bin/id\n "
578+ " Mar 10 08:21:45 example-host sudo: bob : user NOT in sudoers ; TTY=pts/1 ; "
579+ " PWD=/home/user/project ; USER=root ; COMMAND=/usr/bin/id\n "
580+ " Mar 10 08:21:50 example-host su[1243]: FAILED SU (to root) carol on pts/2\n " );
581+ expect (events.size () == 3 , " expected sudo and su auth failures to parse" );
582+ expect (events[0 ].event_type == loglens::EventType::SudoAuthFailure,
583+ " expected sudo password failure event" );
584+ expect (events[1 ].event_type == loglens::EventType::SudoPolicyDenied,
585+ " expected sudo policy denied event" );
586+ expect (events[2 ].event_type == loglens::EventType::SuAuthFailure,
587+ " expected su auth failure event" );
588+
589+ const auto signals = loglens::build_auth_signals (events, loglens::DetectorConfig{}.auth_signal_mappings );
590+ expect (signals.size () == 3 , " expected privilege auth failures to be visible as signals" );
591+ expect (count_signals (signals, loglens::AuthSignalKind::SudoAuthFailure) == 1 ,
592+ " expected one sudo auth failure signal" );
593+ expect (count_signals (signals, loglens::AuthSignalKind::SudoPolicyDenied) == 1 ,
594+ " expected one sudo policy denied signal" );
595+ expect (count_signals (signals, loglens::AuthSignalKind::SuAuthFailure) == 1 ,
596+ " expected one su auth failure signal" );
597+
598+ for (const auto & signal : signals) {
599+ expect (!signal.counts_as_attempt_evidence ,
600+ " did not expect privilege auth signals to count as auth attempt evidence" );
601+ expect (!signal.counts_as_terminal_auth_failure ,
602+ " did not expect privilege auth signals to count as terminal auth failures" );
603+ expect (!signal.counts_as_sudo_burst_evidence ,
604+ " did not expect privilege auth signals to count toward sudo burst evidence" );
605+ }
606+
607+ const loglens::Detector detector;
608+ const auto findings = detector.analyze (events);
609+ expect (findings.empty (), " expected privilege auth signals to stay out of detector findings by default" );
610+ }
611+
573612void test_sudo_burst_behavior_is_preserved_with_signal_layer () {
574613 const auto events = build_sudo_burst_preservation_events ();
575614 const loglens::Detector detector;
@@ -743,6 +782,7 @@ int main() {
743782 test_failed_publickey_contributes_to_bruteforce_by_default ();
744783 test_accepted_publickey_success_stays_out_of_failure_signals ();
745784 test_sudo_signals_include_command_and_session_opened ();
785+ test_privilege_auth_failures_are_non_counting_signals ();
746786 test_sudo_burst_behavior_is_preserved_with_signal_layer ();
747787 test_unsupported_pam_session_close_remains_telemetry_only ();
748788 test_pam_auth_failure_does_not_trigger_bruteforce_by_default ();
0 commit comments