Skip to content
API Documentation
BastionGuard™
RootGuardPage Class Reference

#include <RootGuardPage.hpp>

Inheritance diagram for RootGuardPage:

Public Member Functions

 RootGuardPage ()
 ~RootGuardPage () override

Private Member Functions

void on_snapshot (const BastionGuard::RootGuard::Snapshot &snapshot)
void render_incidents (const BastionGuard::RootGuard::Snapshot &snapshot)
void render_events (const BastionGuard::RootGuard::Snapshot &snapshot)
void queue_new_alerts (const BastionGuard::RootGuard::Snapshot &snapshot)
void show_next_alert ()
void close_current_alert ()
void dismiss_alert (const BastionGuard::RootGuard::Event &event)
void dismiss_event (const BastionGuard::RootGuard::Event &event)
void restore_event (const BastionGuard::RootGuard::Event &event)
void remove_event (const BastionGuard::RootGuard::Event &event)
bool on_auto_refresh ()
void on_protection_toggled ()
Gtk::Box * make_rule_editor (const Glib::ustring &title, const Glib::ustring &description, Gtk::ListBox &list, Gtk::Entry &entry)
void add_rule_from_entry (Gtk::Entry &entry, Gtk::ListBox &list)
void add_installed_application_defaults ()
void remove_selected_rule (Gtk::ListBox &list)
void mark_rules_dirty ()
void populate_application_rules (const BastionGuard::RootGuard::ApplicationRules &rules)
std::vector< std::string > collect_rules (const Gtk::ListBox &list) const
void save_application_rules ()

Private Attributes

BastionGuard::RootGuard::Client client_
Gtk::Label title_
Gtk::Label service_value_
Gtk::Label mode_value_
Gtk::Label init_value_
Gtk::Label pid_value_
Gtk::Stack page_stack_
Gtk::StackSwitcher page_switcher_
Gtk::Switch protection_switch_
Gtk::Label protection_label_
Gtk::Popover protection_info_popover_
Gtk::MenuButton protection_info_button_
Gtk::Label system_scope_value_
Gtk::Label home_scope_value_
Gtk::Button start_button_
Gtk::Button stop_button_
Gtk::Button reload_button_
Gtk::Button restart_button_
Gtk::Button refresh_button_
Gtk::Label incidents_title_
Gtk::ScrolledWindow incidents_scroller_
Gtk::ListBox incidents_list_
Gtk::CheckButton show_trusted_
Gtk::ScrolledWindow events_scroller_
Gtk::TextView events_view_
Gtk::Stack rules_stack_
Gtk::StackSwitcher rules_switcher_
Gtk::ListBox trusted_applications_list_
Gtk::Entry trusted_applications_entry_
Gtk::ListBox trusted_executables_list_
Gtk::Entry trusted_executables_entry_
Gtk::ListBox denied_executables_list_
Gtk::Entry denied_executables_entry_
Gtk::Button save_rules_button_
Gtk::Label rules_status_
Gtk::Label status_
sigc::connection client_update_connection_
sigc::connection auto_refresh_connection_
bool updating_switch_ {}
bool initial_snapshot_ {true}
bool rules_dirty_ {}
bool rules_saving_ {}
std::string rules_signature_
std::optional< BastionGuard::RootGuard::Snapshot > last_snapshot_
std::set< std::string > seen_events_
std::set< std::string > dismissed_events_
std::set< std::string > dismissed_paths_
std::set< std::string > notified_paths_
std::queue< BastionGuard::RootGuard::Event > alert_queue_
std::chrono::steady_clock::time_point last_global_burst_alert_shown_ {}
std::unique_ptr< Gtk::Window > current_alert_
std::shared_ptr< std::atomic_bool > alive_ {std::make_shared<std::atomic_bool>(true)}

Detailed Description

Definition at line 21 of file RootGuardPage.hpp.

Constructor & Destructor Documentation

◆ RootGuardPage()

RootGuardPage::RootGuardPage ( )

Definition at line 275 of file RootGuardPage.cpp.

276 : Gtk::Box(Gtk::Orientation::VERTICAL, 8)
277{
278 set_margin(12);
279 set_hexpand(true);
280 set_vexpand(true);
281
282 auto* title_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
283 title_box->add_css_class("secure-headerbar");
284 title_box->set_margin_bottom(4);
285
286 title_.set_text(_("🛡️ RootGuard — Permission and Privilege Protection"));
287 title_.set_margin(8);
288 title_.set_halign(Gtk::Align::CENTER);
289 title_.set_hexpand(true);
290 title_.add_css_class("secure-header-title");
291 title_box->append(title_);
292 append(*title_box);
293
294 auto* summary = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
295 summary->set_homogeneous(true);
296 summary->append(*make_status_item(_("Service"), service_value_));
297 summary->append(*make_status_item(_("Mode"), mode_value_));
298 summary->append(*make_status_item(_("Init system"), init_value_));
299 summary->append(*make_status_item(_("PID"), pid_value_));
300 append(*summary);
301
302 page_stack_.set_hexpand(true);
303 page_stack_.set_vexpand(true);
304 page_stack_.set_hhomogeneous(false);
305 page_stack_.set_vhomogeneous(false);
306
307 page_switcher_.set_stack(page_stack_);
308 page_switcher_.set_halign(Gtk::Align::CENTER);
309 page_switcher_.set_margin_top(2);
310 page_switcher_.set_margin_bottom(2);
311 append(page_switcher_);
312
313 // Overview tab.
314 auto* overview = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 10);
315 overview->set_margin_top(8);
316 overview->set_hexpand(true);
317 overview->set_vexpand(true);
318
319 auto* protection_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
320 protection_box->set_halign(Gtk::Align::CENTER);
321 protection_label_.set_text(_("Immediate permission blocking"));
322 protection_label_.set_tooltip_text(
323 _("Blocks protected system metadata changes before they are committed."));
324 protection_box->append(protection_label_);
325 protection_box->append(protection_switch_);
326
327 protection_info_button_.set_label("ⓘ");
328 protection_info_button_.set_has_frame(false);
329 protection_info_button_.set_tooltip_text(_("Show protection scope"));
330 auto* protection_info = Gtk::make_managed<Gtk::Label>(
331 _("Immediate blocking applies to protected system paths and service definitions. "
332 "User home directories remain audit-only because BastionGuard Anti-Ransomware "
333 "is responsible for enforcement on user data. Routine desktop and browser "
334 "metadata activity can be classified under Application rules without granting "
335 "privilege-transition trust."));
336 protection_info->set_wrap(true);
337 protection_info->set_max_width_chars(58);
338 protection_info->set_margin(14);
339 protection_info->set_halign(Gtk::Align::START);
340 protection_info_popover_.set_autohide(true);
341 protection_info_popover_.set_child(*protection_info);
343 protection_box->append(protection_info_button_);
344 overview->append(*protection_box);
345
346 auto* scope = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
347 scope->set_homogeneous(true);
348 scope->set_margin_start(40);
349 scope->set_margin_end(40);
350 scope->append(*make_status_item(_("Protected system paths"), system_scope_value_));
351 scope->append(*make_status_item(_("User home"), home_scope_value_));
352 overview->append(*scope);
353
354 auto* scope_note = Gtk::make_managed<Gtk::Label>(
355 _("RootGuard records privilege-relevant global metadata. Lower-risk changes remain "
356 "visible as observations; only high-signal incidents generate popups."));
357 scope_note->set_wrap(true);
358 scope_note->set_halign(Gtk::Align::CENTER);
359 scope_note->add_css_class("dim-label");
360 overview->append(*scope_note);
361
362 auto* controls = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
363 controls->set_halign(Gtk::Align::CENTER);
364
365 start_button_.set_label(_("▶ Start"));
366 stop_button_.set_label(_("■ Stop"));
367 reload_button_.set_label(_("↻ Reload policy"));
368 restart_button_.set_label(_("⟳ Restart"));
369 refresh_button_.set_label(_("Refresh"));
370 start_button_.add_css_class("btn-primary");
371 stop_button_.add_css_class("btn-danger");
372 reload_button_.add_css_class("btn-warning");
373
374 controls->append(start_button_);
375 controls->append(stop_button_);
376 controls->append(reload_button_);
377 controls->append(restart_button_);
378 controls->append(refresh_button_);
379 overview->append(*controls);
380
381 auto* overview_spacer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
382 overview_spacer->set_vexpand(true);
383 overview->append(*overview_spacer);
384 page_stack_.add(*overview, "overview", _("Overview"));
385
386 // Incidents tab.
387 auto* incidents_panel = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
388 incidents_panel->set_hexpand(true);
389 incidents_panel->set_vexpand(true);
390 incidents_panel->set_margin_top(8);
391
392 incidents_title_.set_text(_("Security observations and active incidents"));
393 incidents_title_.set_halign(Gtk::Align::START);
394 incidents_title_.add_css_class("BastionGuard-title");
395 incidents_panel->append(incidents_title_);
396
397 incidents_list_.set_selection_mode(Gtk::SelectionMode::NONE);
398 incidents_list_.add_css_class("rootguard-incident-list");
399 incidents_scroller_.set_hexpand(true);
400 incidents_scroller_.set_vexpand(true);
401 incidents_scroller_.set_min_content_height(280);
402 incidents_scroller_.set_policy(Gtk::PolicyType::AUTOMATIC,
403 Gtk::PolicyType::AUTOMATIC);
405 incidents_panel->append(incidents_scroller_);
406 page_stack_.add(*incidents_panel, "incidents", _("Incidents"));
407
408 // Events tab.
409 auto* events_panel = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
410 events_panel->set_hexpand(true);
411 events_panel->set_vexpand(true);
412 events_panel->set_margin_top(8);
413
414 auto* events_header = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
415 auto* events_label = Gtk::make_managed<Gtk::Label>(_("Recent RootGuard events"));
416 events_label->set_halign(Gtk::Align::START);
417 events_label->set_hexpand(true);
418 events_label->add_css_class("BastionGuard-title");
419 show_trusted_.set_label(_("Show trusted events"));
420 show_trusted_.set_active(true);
421 show_trusted_.set_tooltip_text(
422 _("Trusted application activity is shown by default for maximum transparency. "
423 "Disable this option only to focus on audit and blocked events."));
424 events_header->append(*events_label);
425 events_header->append(show_trusted_);
426 events_panel->append(*events_header);
427
428 events_view_.set_editable(false);
429 events_view_.set_cursor_visible(false);
430 events_view_.set_monospace(true);
431 events_view_.set_wrap_mode(Gtk::WrapMode::WORD_CHAR);
432 events_view_.set_left_margin(8);
433 events_view_.set_right_margin(8);
434 events_view_.set_top_margin(8);
435 events_view_.set_bottom_margin(8);
436 events_view_.add_css_class("rootguard-terminal");
437
438 events_scroller_.set_hexpand(true);
439 events_scroller_.set_vexpand(true);
440 events_scroller_.set_min_content_height(280);
441 events_scroller_.set_policy(Gtk::PolicyType::AUTOMATIC,
442 Gtk::PolicyType::AUTOMATIC);
444 events_panel->append(events_scroller_);
445 page_stack_.add(*events_panel, "events", _("Events"));
446
447 // Application rules tab, with nested tabs to keep the page compact.
448 auto* rules_page = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
449 rules_page->set_hexpand(true);
450 rules_page->set_vexpand(true);
451 rules_page->set_margin_top(8);
452
453 auto* rules_intro = Gtk::make_managed<Gtk::Label>(
454 _("Manage executable identities used by RootGuard. Saving validates the policy, "
455 "requests administrator authentication, and restarts RootGuard so the new inode "
456 "identities take effect immediately."));
457 rules_intro->set_wrap(true);
458 rules_intro->set_halign(Gtk::Align::START);
459 rules_page->append(*rules_intro);
460
461 auto* defaults_row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
462 defaults_row->set_halign(Gtk::Align::END);
463 auto* add_defaults = Gtk::make_managed<Gtk::Button>(
464 _("Add installed desktop/browser defaults"));
465 add_defaults->set_tooltip_text(
466 _("Adds only known executables that exist on this computer. Review the list before saving."));
467 defaults_row->append(*add_defaults);
468 rules_page->append(*defaults_row);
469
470 rules_stack_.set_hexpand(true);
471 rules_stack_.set_vexpand(true);
472 rules_stack_.set_hhomogeneous(false);
473 rules_stack_.set_vhomogeneous(false);
474 rules_switcher_.set_stack(rules_stack_);
475 rules_switcher_.set_halign(Gtk::Align::CENTER);
476 rules_page->append(rules_switcher_);
477
478 rules_stack_.add(
480 _("Trusted applications"),
481 _("Routine desktop, file-manager and browser compatibility activity. Matching "
482 "legacy global events can become trusted, but direct privilege-metadata signals "
483 "are never suppressed by this list. This list never grants privilege trust."),
485 "trusted-applications", _("Trusted apps"));
486
487 rules_stack_.add(
489 _("Privilege-trusted executables"),
490 _("Executables allowed as legitimate actors in RootGuard privilege-transition "
491 "checks. Existing files are still subject to ownership and identity validation."),
493 "trusted-executables", _("Privilege trusted"));
494
495 rules_stack_.add(
497 _("Blocked executables"),
498 _("Executables blocked during RootGuard privilege-transition checks. This is not "
499 "a general application-launch blacklist."),
501 "denied-executables", _("Blocked"));
502
503 // The switcher only controls the stack; the stack itself must also be
504 // inserted into the widget hierarchy or its rule editors remain invisible.
505 rules_page->append(rules_stack_);
506
507 auto* save_row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
508 save_row->set_halign(Gtk::Align::END);
509 save_rules_button_.set_label(_("Save rules and restart RootGuard"));
510 save_rules_button_.add_css_class("btn-primary");
511 save_rules_button_.set_sensitive(false);
512 save_row->append(save_rules_button_);
513 rules_page->append(*save_row);
514
515 rules_status_.set_halign(Gtk::Align::START);
516 rules_status_.set_wrap(true);
517 rules_status_.add_css_class("dim-label");
518 rules_status_.set_text(
519 _("Unsaved changes are never applied silently. RootGuard restarts only after a successful policy save."));
520 rules_page->append(rules_status_);
521 page_stack_.add(*rules_page, "application-rules", _("Application rules"));
522
523 append(page_stack_);
524
525 status_.set_halign(Gtk::Align::CENTER);
526 status_.set_wrap(true);
527 status_.set_margin_top(2);
528 append(status_);
529
531 sigc::mem_fun(*this, &RootGuardPage::on_snapshot));
532
533 start_button_.signal_clicked().connect(
534 [this] { status_.set_text(_("Requesting service start…")); client_.start_service(); });
535 stop_button_.signal_clicked().connect(
536 [this] { status_.set_text(_("Requesting service stop…")); client_.stop_service(); });
537 reload_button_.signal_clicked().connect(
538 [this] { status_.set_text(_("Reloading RootGuard policy…")); client_.reload_policy(); });
539 restart_button_.signal_clicked().connect(
540 [this] { status_.set_text(_("Restarting RootGuard…")); client_.restart_service(); });
541 refresh_button_.signal_clicked().connect(
542 [this] { status_.set_text(_("Refreshing RootGuard status…")); client_.refresh(); });
543 show_trusted_.signal_toggled().connect([this] {
544 // Filtering is a local presentation choice. Re-render the most recent
545 // snapshot immediately instead of waiting for the asynchronous client
546 // refresh/next timer tick.
547 if (last_snapshot_) {
549 return;
550 }
552 });
553 protection_switch_.property_active().signal_changed().connect(
554 sigc::mem_fun(*this, &RootGuardPage::on_protection_toggled));
555 save_rules_button_.signal_clicked().connect(
556 sigc::mem_fun(*this, &RootGuardPage::save_application_rules));
557 add_defaults->signal_clicked().connect(
559
560 auto_refresh_connection_ = Glib::signal_timeout().connect_seconds(
561 sigc::mem_fun(*this, &RootGuardPage::on_auto_refresh), 1);
562
563 service_value_.set_text(_("Checking…"));
564 mode_value_.set_text(Glib::ustring("—"));
565 init_value_.set_text(Glib::ustring("—"));
566 pid_value_.set_text(Glib::ustring("—"));
567 system_scope_value_.set_text(_("Checking…"));
568 home_scope_value_.set_text(_("Audit-only"));
569 status_.set_text(_("Loading RootGuard status and recent security events…"));
571}
#define _(s)
sigc::signal< void(const Snapshot &)> & signal_updated() noexcept
Gtk::CheckButton show_trusted_
std::optional< BastionGuard::RootGuard::Snapshot > last_snapshot_
Gtk::Entry trusted_executables_entry_
Gtk::Button refresh_button_
Gtk::Label incidents_title_
void on_snapshot(const BastionGuard::RootGuard::Snapshot &snapshot)
Gtk::ListBox denied_executables_list_
void on_protection_toggled()
void save_application_rules()
Gtk::Label home_scope_value_
Gtk::Entry denied_executables_entry_
Gtk::Label title_
Gtk::Button save_rules_button_
sigc::connection client_update_connection_
Gtk::StackSwitcher rules_switcher_
Gtk::Label mode_value_
Gtk::Button start_button_
Gtk::MenuButton protection_info_button_
Gtk::Button stop_button_
Gtk::Label init_value_
Gtk::Entry trusted_applications_entry_
Gtk::Label service_value_
void render_events(const BastionGuard::RootGuard::Snapshot &snapshot)
Gtk::Stack page_stack_
Gtk::ListBox trusted_applications_list_
Gtk::ListBox trusted_executables_list_
Gtk::Button restart_button_
sigc::connection auto_refresh_connection_
Gtk::ListBox incidents_list_
Gtk::StackSwitcher page_switcher_
Gtk::Button reload_button_
Gtk::Box * make_rule_editor(const Glib::ustring &title, const Glib::ustring &description, Gtk::ListBox &list, Gtk::Entry &entry)
Gtk::Label system_scope_value_
Gtk::Stack rules_stack_
Gtk::Switch protection_switch_
Gtk::ScrolledWindow events_scroller_
Gtk::Popover protection_info_popover_
Gtk::Label pid_value_
Gtk::Label rules_status_
BastionGuard::RootGuard::Client client_
Gtk::TextView events_view_
void add_installed_application_defaults()
Gtk::Label protection_label_
Gtk::Label status_
Gtk::ScrolledWindow incidents_scroller_
Gtk::Box * make_status_item(const Glib::ustring &label, Gtk::Label &value)

References _, add_installed_application_defaults(), auto_refresh_connection_, client_, client_update_connection_, denied_executables_entry_, denied_executables_list_, events_scroller_, events_view_, home_scope_value_, incidents_list_, incidents_scroller_, incidents_title_, init_value_, last_snapshot_, make_rule_editor(), mode_value_, on_auto_refresh(), on_protection_toggled(), on_snapshot(), page_stack_, page_switcher_, pid_value_, protection_info_button_, protection_info_popover_, protection_label_, protection_switch_, BastionGuard::RootGuard::Client::refresh(), refresh_button_, reload_button_, BastionGuard::RootGuard::Client::reload_policy(), render_events(), restart_button_, BastionGuard::RootGuard::Client::restart_service(), rules_stack_, rules_status_, rules_switcher_, save_application_rules(), save_rules_button_, service_value_, show_trusted_, BastionGuard::RootGuard::Client::signal_updated(), start_button_, BastionGuard::RootGuard::Client::start_service(), status_, stop_button_, BastionGuard::RootGuard::Client::stop_service(), system_scope_value_, title_, trusted_applications_entry_, trusted_applications_list_, trusted_executables_entry_, and trusted_executables_list_.

◆ ~RootGuardPage()

RootGuardPage::~RootGuardPage ( )
override

Definition at line 573 of file RootGuardPage.cpp.

574{
575 alive_->store(false, std::memory_order_release);
576 if (client_update_connection_.connected())
577 client_update_connection_.disconnect();
578 if (auto_refresh_connection_.connected())
579 auto_refresh_connection_.disconnect();
580 while (!alert_queue_.empty())
581 alert_queue_.pop();
582 if (current_alert_)
583 current_alert_->hide();
584 current_alert_.reset();
585}
std::shared_ptr< std::atomic_bool > alive_
std::queue< BastionGuard::RootGuard::Event > alert_queue_
std::unique_ptr< Gtk::Window > current_alert_

References alert_queue_, alive_, auto_refresh_connection_, client_update_connection_, and current_alert_.

Member Function Documentation

◆ add_installed_application_defaults()

void RootGuardPage::add_installed_application_defaults ( )
private

Definition at line 670 of file RootGuardPage.cpp.

671{
672 static const std::vector<std::string> candidates {
673 "/usr/bin/gnome-shell",
674 "/usr/bin/nautilus",
675 "/usr/bin/plasmashell",
676 "/usr/bin/kwin_wayland",
677 "/usr/bin/dolphin",
678 "/usr/bin/xfdesktop",
679 "/usr/bin/xfwm4",
680 "/usr/bin/thunar",
681 "/usr/bin/cinnamon",
682 "/usr/bin/nemo",
683 "/usr/bin/mate-panel",
684 "/usr/bin/caja",
685 "/usr/bin/firefox",
686 "/usr/lib/firefox/firefox",
687 "/usr/bin/chromium",
688 "/usr/bin/google-chrome-stable",
689 "/usr/bin/brave",
690 "/usr/bin/vivaldi-stable",
691 };
692
694 std::set<std::string> present(current.begin(), current.end());
695 std::size_t added = 0;
696 for (const auto& candidate : candidates) {
697 std::error_code error;
698 const auto status = std::filesystem::status(candidate, error);
699 if (error || !std::filesystem::is_regular_file(status))
700 continue;
701 const auto permissions = status.permissions();
702 const bool executable =
703 (permissions & std::filesystem::perms::owner_exec) !=
704 std::filesystem::perms::none ||
705 (permissions & std::filesystem::perms::group_exec) !=
706 std::filesystem::perms::none ||
707 (permissions & std::filesystem::perms::others_exec) !=
708 std::filesystem::perms::none;
709 if (!executable)
710 continue;
711
712 const auto canonical = std::filesystem::weakly_canonical(candidate, error);
713 const std::string normalized = error
714 ? std::filesystem::path(candidate).lexically_normal().string()
715 : canonical.string();
716 if (!present.insert(normalized).second)
717 continue;
719 ++added;
720 }
721
722 if (added == 0) {
723 rules_status_.set_text(
724 _("No new installed desktop or browser defaults were found."));
725 return;
726 }
728 rules_status_.set_text(Glib::ustring::compose(
729 _("Added %1 installed desktop/browser executable(s). Review and save to restart RootGuard."),
730 added));
731}
std::vector< std::string > collect_rules(const Gtk::ListBox &list) const
void append_path_row(Gtk::ListBox &list, const std::string &path)

References _, collect_rules(), mark_rules_dirty(), rules_status_, and trusted_applications_list_.

Referenced by RootGuardPage().

◆ add_rule_from_entry()

void RootGuardPage::add_rule_from_entry ( Gtk::Entry & entry,
Gtk::ListBox & list )
private

Definition at line 643 of file RootGuardPage.cpp.

644{
645 const std::string raw = trim_copy(entry.get_text().raw());
646 if (raw.empty()) {
647 rules_status_.set_text(_("Enter an absolute executable path first."));
648 return;
649 }
650
651 std::filesystem::path candidate(raw);
652 if (!candidate.is_absolute()) {
653 rules_status_.set_text(_("Application rules require an absolute path beginning with /."));
654 return;
655 }
656
657 candidate = candidate.lexically_normal();
658 const std::string normalized = candidate.string();
659 const auto current = collect_rules(list);
660 if (std::find(current.begin(), current.end(), normalized) != current.end()) {
661 rules_status_.set_text(_("That executable path is already present in this list."));
662 return;
663 }
664
665 append_path_row(list, normalized);
666 entry.set_text("");
668}
std::string trim_copy(std::string value)

References _, collect_rules(), mark_rules_dirty(), and rules_status_.

Referenced by make_rule_editor().

◆ close_current_alert()

void RootGuardPage::close_current_alert ( )
private

Definition at line 1263 of file RootGuardPage.cpp.

1264{
1265 if (!current_alert_)
1266 return;
1267 auto* window = current_alert_.get();
1268 window->hide();
1269 const auto alive = alive_;
1270 Glib::signal_idle().connect_once([this, window, alive] {
1271 if (!alive->load(std::memory_order_acquire))
1272 return;
1273 if (current_alert_.get() == window)
1274 current_alert_.reset();
1276 });
1277}

References alive_, current_alert_, and show_next_alert().

Referenced by show_next_alert().

◆ collect_rules()

std::vector< std::string > RootGuardPage::collect_rules ( const Gtk::ListBox & list) const
private

Definition at line 752 of file RootGuardPage.cpp.

753{
754 std::vector<std::string> values;
755 for (auto* widget = list.get_first_child(); widget;
756 widget = widget->get_next_sibling()) {
757 const auto* row = dynamic_cast<const Gtk::ListBoxRow*>(widget);
758 if (!row)
759 continue;
760 const auto* label = dynamic_cast<const Gtk::Label*>(row->get_child());
761 if (!label)
762 continue;
763 const std::string value = trim_copy(label->get_text().raw());
764 if (!value.empty())
765 values.push_back(value);
766 }
767 std::sort(values.begin(), values.end());
768 values.erase(std::unique(values.begin(), values.end()), values.end());
769 return values;
770}

Referenced by add_installed_application_defaults(), add_rule_from_entry(), and save_application_rules().

◆ dismiss_alert()

void RootGuardPage::dismiss_alert ( const BastionGuard::RootGuard::Event & event)
private

Definition at line 1279 of file RootGuardPage.cpp.

1280{
1282}
std::set< std::string > dismissed_events_
std::string event_fingerprint(const Event &event)

References dismissed_events_, and BastionGuard::RootGuard::event_fingerprint().

Referenced by show_next_alert().

◆ dismiss_event()

void RootGuardPage::dismiss_event ( const BastionGuard::RootGuard::Event & event)
private

Definition at line 1284 of file RootGuardPage.cpp.

1285{
1287 dismissed_paths_.insert(incident_key(event));
1288 client_.refresh();
1289}
std::set< std::string > dismissed_paths_
std::string incident_key(const BastionGuard::RootGuard::Event &event)

References client_, dismissed_events_, dismissed_paths_, BastionGuard::RootGuard::event_fingerprint(), and BastionGuard::RootGuard::Client::refresh().

Referenced by render_incidents().

◆ make_rule_editor()

Gtk::Box * RootGuardPage::make_rule_editor ( const Glib::ustring & title,
const Glib::ustring & description,
Gtk::ListBox & list,
Gtk::Entry & entry )
private

Definition at line 587 of file RootGuardPage.cpp.

591{
592 auto* editor = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
593 editor->set_hexpand(true);
594 editor->set_vexpand(true);
595 editor->set_margin_top(6);
596
597 auto* heading = Gtk::make_managed<Gtk::Label>(title);
598 heading->set_halign(Gtk::Align::START);
599 heading->add_css_class("BastionGuard-title");
600 editor->append(*heading);
601
602 auto* explanation = Gtk::make_managed<Gtk::Label>(description);
603 explanation->set_halign(Gtk::Align::START);
604 explanation->set_wrap(true);
605 explanation->add_css_class("dim-label");
606 editor->append(*explanation);
607
608 list.set_selection_mode(Gtk::SelectionMode::SINGLE);
609 list.add_css_class("rootguard-rule-list");
610 auto* scroller = Gtk::make_managed<Gtk::ScrolledWindow>();
611 scroller->set_hexpand(true);
612 scroller->set_vexpand(true);
613 scroller->set_min_content_height(210);
614 scroller->set_policy(Gtk::PolicyType::AUTOMATIC,
615 Gtk::PolicyType::AUTOMATIC);
616 scroller->set_child(list);
617 editor->append(*scroller);
618
619 auto* input_row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
620 entry.set_hexpand(true);
621 entry.set_placeholder_text(_("Absolute executable path, for example /usr/bin/firefox"));
622 auto* add = Gtk::make_managed<Gtk::Button>(_("Add"));
623 auto* remove = Gtk::make_managed<Gtk::Button>(_("Remove selected"));
624 add->add_css_class("btn-primary");
625 remove->add_css_class("btn-danger");
626 input_row->append(entry);
627 input_row->append(*add);
628 input_row->append(*remove);
629 editor->append(*input_row);
630
631 add->signal_clicked().connect([this, &entry, &list] {
632 add_rule_from_entry(entry, list);
633 });
634 entry.signal_activate().connect([this, &entry, &list] {
635 add_rule_from_entry(entry, list);
636 });
637 remove->signal_clicked().connect([this, &list] {
639 });
640 return editor;
641}
void add_rule_from_entry(Gtk::Entry &entry, Gtk::ListBox &list)
void remove_selected_rule(Gtk::ListBox &list)

References _, add_rule_from_entry(), and remove_selected_rule().

Referenced by RootGuardPage().

◆ mark_rules_dirty()

void RootGuardPage::mark_rules_dirty ( )
private

Definition at line 744 of file RootGuardPage.cpp.

745{
746 rules_dirty_ = true;
747 rules_status_.set_text(
748 _("Rules have unsaved changes. Saving will validate the policy and restart RootGuard."));
749 save_rules_button_.set_sensitive(!rules_saving_);
750}

References _, rules_dirty_, rules_saving_, rules_status_, and save_rules_button_.

Referenced by add_installed_application_defaults(), add_rule_from_entry(), and remove_selected_rule().

◆ on_auto_refresh()

bool RootGuardPage::on_auto_refresh ( )
private

Definition at line 820 of file RootGuardPage.cpp.

821{
823 return true;
824}

References client_, and BastionGuard::RootGuard::Client::refresh().

Referenced by RootGuardPage().

◆ on_protection_toggled()

void RootGuardPage::on_protection_toggled ( )
private

Definition at line 826 of file RootGuardPage.cpp.

827{
829 return;
830 protection_switch_.set_sensitive(false);
831 const bool enabled = protection_switch_.get_active();
832 status_.set_text(enabled
833 ? _("Enabling immediate blocking for protected system paths; user home remains audit-only…")
834 : _("Switching RootGuard system-path protection to audit mode…"));
836}
void set_immediate_protection(bool enabled)

References _, client_, protection_switch_, BastionGuard::RootGuard::Client::set_immediate_protection(), status_, and updating_switch_.

Referenced by RootGuardPage().

◆ on_snapshot()

void RootGuardPage::on_snapshot ( const BastionGuard::RootGuard::Snapshot & snapshot)
private

Definition at line 838 of file RootGuardPage.cpp.

839{
840 // Keep a local copy so event filtering reacts immediately when the user
841 // toggles trusted-event visibility.
842 last_snapshot_ = snapshot;
843
844 service_value_.set_text(snapshot.service.active ? _("Active") : _("Inactive"));
845 mode_value_.set_text(snapshot.service.immediateProtection
846 ? _("Immediate block")
847 : snapshot.service.mode.empty() ? "unknown" : snapshot.service.mode);
848 init_value_.set_text(snapshot.service.init_system.empty()
849 ? "unknown"
850 : snapshot.service.init_system);
851 pid_value_.set_text(snapshot.service.pid.empty() ? "—" : snapshot.service.pid);
852
854 ? _("Immediate blocking") : _("Audit-only"));
855 home_scope_value_.set_text(snapshot.service.blockUserHome
856 ? _("Blocking enabled by manual policy")
857 : _("Audit-only · Anti-Ransomware enforcement"));
858
859 start_button_.set_sensitive(!snapshot.service.active);
860 stop_button_.set_sensitive(snapshot.service.active);
861 reload_button_.set_sensitive(snapshot.service.active);
862 restart_button_.set_sensitive(snapshot.service.active);
863
864 updating_switch_ = true;
866 updating_switch_ = false;
867 protection_switch_.set_sensitive(snapshot.service.active && !rules_saving_);
868
869 if (rules_saving_ && !snapshot.operation_message.empty()) {
870 rules_saving_ = false;
871 if (snapshot.operation_success) {
872 rules_dirty_ = false;
873 rules_signature_.clear();
874 rules_status_.set_text(
875 _("Application rules saved and RootGuard restarted successfully."));
876 } else {
877 rules_status_.set_text(Glib::ustring::compose(
878 _("Application rules were not applied: %1"),
879 snapshot.operation_message));
880 }
881 }
884
885 if (!snapshot.operation_message.empty()) {
886 status_.set_text(snapshot.operation_success
887 ? Glib::ustring::compose(_("✅ %1"), snapshot.operation_message)
888 : Glib::ustring::compose(_("❌ %1"), snapshot.operation_message));
889 } else if (snapshot.service.active && snapshot.service.immediateProtection) {
890 status_.set_text(snapshot.service.autoBlockServices
891 ? _("RootGuard blocks protected system changes and contains affected services. User home remains audit-only under Anti-Ransomware protection.")
892 : _("RootGuard blocks protected system metadata changes. User home remains audit-only under Anti-Ransomware protection."));
893 } else if (snapshot.service.active) {
894 status_.set_text(
895 _("RootGuard is monitoring filesystem metadata in audit mode. Global privilege events are sampled and destructive permission bursts are aggregated."));
896 } else {
897 status_.set_text(_("RootGuard is not running."));
898 }
899
900 render_incidents(snapshot);
901 render_events(snapshot);
902 queue_new_alerts(snapshot);
903}
void queue_new_alerts(const BastionGuard::RootGuard::Snapshot &snapshot)
std::string rules_signature_
void render_incidents(const BastionGuard::RootGuard::Snapshot &snapshot)
void populate_application_rules(const BastionGuard::RootGuard::ApplicationRules &rules)

References _, BastionGuard::RootGuard::ServiceStatus::active, BastionGuard::RootGuard::Snapshot::application_rules, BastionGuard::RootGuard::ServiceStatus::autoBlockServices, BastionGuard::RootGuard::ServiceStatus::blockUserHome, home_scope_value_, BastionGuard::RootGuard::ServiceStatus::immediateProtection, BastionGuard::RootGuard::ServiceStatus::init_system, init_value_, last_snapshot_, BastionGuard::RootGuard::ServiceStatus::mode, mode_value_, BastionGuard::RootGuard::Snapshot::operation_message, BastionGuard::RootGuard::Snapshot::operation_success, BastionGuard::RootGuard::ServiceStatus::pid, pid_value_, populate_application_rules(), protection_switch_, queue_new_alerts(), reload_button_, render_events(), render_incidents(), restart_button_, rules_dirty_, rules_saving_, rules_signature_, rules_status_, save_rules_button_, BastionGuard::RootGuard::Snapshot::service, service_value_, start_button_, status_, stop_button_, system_scope_value_, and updating_switch_.

Referenced by RootGuardPage().

◆ populate_application_rules()

void RootGuardPage::populate_application_rules ( const BastionGuard::RootGuard::ApplicationRules & rules)
private

Definition at line 772 of file RootGuardPage.cpp.

774{
775 const std::string signature = rules_signature(rules);
776 if (rules_dirty_ || rules_saving_ || signature == rules_signature_)
777 return;
778
779 const auto populate = [](Gtk::ListBox& list,
780 std::vector<std::string> values) {
781 clear_list(list);
782 std::sort(values.begin(), values.end());
783 values.erase(std::unique(values.begin(), values.end()), values.end());
784 for (const auto& path : values)
785 append_path_row(list, path);
786 };
787
791 rules_signature_ = signature;
792}
std::string rules_signature(const BastionGuard::RootGuard::ApplicationRules &rules)
std::vector< std::string > deniedExecutables
std::vector< std::string > trustedExecutables
std::vector< std::string > trustedApplications

References denied_executables_list_, BastionGuard::RootGuard::ApplicationRules::deniedExecutables, rules_dirty_, rules_saving_, rules_signature_, trusted_applications_list_, trusted_executables_list_, BastionGuard::RootGuard::ApplicationRules::trustedApplications, and BastionGuard::RootGuard::ApplicationRules::trustedExecutables.

Referenced by on_snapshot().

◆ queue_new_alerts()

void RootGuardPage::queue_new_alerts ( const BastionGuard::RootGuard::Snapshot & snapshot)
private

Definition at line 1027 of file RootGuardPage.cpp.

1028{
1029 std::vector<BastionGuard::RootGuard::Event> new_events;
1030 for (const auto& event : snapshot.events) {
1031 if (event.verdict == "trusted")
1032 continue;
1033 if (closes_incident(event) && !event.filename.empty()) {
1034 notified_paths_.erase(incident_key(event));
1035 dismissed_paths_.erase(incident_key(event));
1036 continue;
1037 }
1039 continue;
1040
1041 const std::string fingerprint =
1043 if (!seen_events_.insert(fingerprint).second)
1044 continue;
1045
1046 const bool service_failure = event.event == "service-blocked" &&
1047 event.verdict != "blocked";
1048 const bool first_for_path = notified_paths_.insert(incident_key(event)).second;
1049 if (event.event != "service-blocked")
1050 dismissed_paths_.erase(incident_key(event));
1051
1052 if (!initial_snapshot_ &&
1053 dismissed_events_.count(fingerprint) == 0 &&
1054 (first_for_path || service_failure)) {
1055 new_events.push_back(event);
1056 }
1057 }
1058 initial_snapshot_ = false;
1059
1060 std::set<std::string> selected_paths;
1061 std::vector<BastionGuard::RootGuard::Event> selected;
1062 for (auto iterator = new_events.rbegin(); iterator != new_events.rend(); ++iterator) {
1063 const std::string key = incident_key(*iterator) + "\n" +
1064 (iterator->event == "service-blocked" && iterator->verdict != "blocked"
1065 ? "failure" : "incident");
1066 if (selected_paths.insert(key).second)
1067 selected.push_back(*iterator);
1068 }
1069 /*
1070 * Never build a popup backlog. Protected/service incidents take priority and
1071 * may interrupt immediately; a global metadata burst is deliberately much
1072 * quieter because backup/package activity can resemble a burst. The full
1073 * history always remains in Incidents/Events.
1074 */
1075 const auto critical = std::find_if(
1076 selected.begin(), selected.end(), [](const auto& event) {
1077 return event.rule_id != 0 || event.event == "service-blocked";
1078 });
1079 if (!current_alert_ && alert_queue_.empty() && critical != selected.end()) {
1080 alert_queue_.push(*critical);
1081 } else if (!current_alert_ && alert_queue_.empty() && !selected.empty()) {
1082 constexpr auto globalBurstPopupInterval = std::chrono::minutes(15);
1083 const auto now = std::chrono::steady_clock::now();
1084 const auto burst = std::find_if(
1085 selected.begin(), selected.end(), [](const auto& event) {
1086 return event.rule_id == 0 &&
1087 (event.reason_flags & RG_REASON_METADATA_BURST) != 0;
1088 });
1089 const bool cooldownExpired =
1090 last_global_burst_alert_shown_.time_since_epoch().count() == 0 ||
1091 now - last_global_burst_alert_shown_ >= globalBurstPopupInterval;
1092 if (burst != selected.end() && cooldownExpired)
1093 alert_queue_.push(*burst);
1094 }
1095
1096 if (!current_alert_)
1098}
std::set< std::string > notified_paths_
std::set< std::string > seen_events_
std::chrono::steady_clock::time_point last_global_burst_alert_shown_
bool is_actionable_incident(const Event &event) noexcept
bool closes_incident(const BastionGuard::RootGuard::Event &event)

References alert_queue_, current_alert_, dismissed_events_, dismissed_paths_, BastionGuard::RootGuard::event_fingerprint(), BastionGuard::RootGuard::Snapshot::events, initial_snapshot_, BastionGuard::RootGuard::is_actionable_incident(), last_global_burst_alert_shown_, notified_paths_, seen_events_, and show_next_alert().

Referenced by on_snapshot().

◆ remove_event()

void RootGuardPage::remove_event ( const BastionGuard::RootGuard::Event & event)
private

Definition at line 1297 of file RootGuardPage.cpp.

1298{
1299 status_.set_text(
1300 _("Moving the protected file from its original path into quarantine…"));
1301 client_.remove_event(event);
1302}
void remove_event(const Event &event)

References _, client_, BastionGuard::RootGuard::Client::remove_event(), and status_.

Referenced by render_incidents(), and show_next_alert().

◆ remove_selected_rule()

void RootGuardPage::remove_selected_rule ( Gtk::ListBox & list)
private

Definition at line 733 of file RootGuardPage.cpp.

734{
735 auto* row = list.get_selected_row();
736 if (!row) {
737 rules_status_.set_text(_("Select a rule to remove."));
738 return;
739 }
740 list.remove(*row);
742}

References _, mark_rules_dirty(), and rules_status_.

Referenced by make_rule_editor().

◆ render_events()

void RootGuardPage::render_events ( const BastionGuard::RootGuard::Snapshot & snapshot)
private

Definition at line 1005 of file RootGuardPage.cpp.

1006{
1007 std::ostringstream output;
1008 std::size_t visible = 0;
1009 for (auto iterator = snapshot.events.rbegin(); iterator != snapshot.events.rend(); ++iterator) {
1010 if (!show_trusted_.get_active() && iterator->verdict == "trusted")
1011 continue;
1012 output << BastionGuard::RootGuard::format_event(*iterator) << '\n';
1013 if (++visible >= 150)
1014 break;
1015 }
1016 if (visible == 0) {
1017 if (snapshot.events.empty()) {
1018 output << _("No RootGuard events are available yet.") << '\n';
1019 } else {
1020 output << _("Only trusted events are available. Enable “Show trusted events” to display them.")
1021 << '\n';
1022 }
1023 }
1024 events_view_.get_buffer()->set_text(output.str());
1025}
std::string format_event(const Event &event)

References _, BastionGuard::RootGuard::Snapshot::events, events_view_, BastionGuard::RootGuard::format_event(), and show_trusted_.

Referenced by on_snapshot(), and RootGuardPage().

◆ render_incidents()

void RootGuardPage::render_incidents ( const BastionGuard::RootGuard::Snapshot & snapshot)
private

Definition at line 905 of file RootGuardPage.cpp.

906{
908
909 std::vector<BastionGuard::RootGuard::Event> incidents;
910 std::set<std::string> closed_paths;
911 std::set<std::string> displayed_paths;
912 for (auto iterator = snapshot.events.rbegin(); iterator != snapshot.events.rend(); ++iterator) {
913 if (closes_incident(*iterator) && !iterator->filename.empty()) {
914 closed_paths.insert(incident_key(*iterator));
915 continue;
916 }
918 continue;
919 if (closed_paths.count(incident_key(*iterator)) != 0 ||
920 displayed_paths.count(incident_key(*iterator)) != 0 ||
921 dismissed_paths_.count(incident_key(*iterator)) != 0)
922 continue;
923 if (dismissed_events_.count(
925 continue;
926 displayed_paths.insert(incident_key(*iterator));
927 incidents.push_back(*iterator);
928 if (incidents.size() >= 60)
929 break;
930 }
931
932 if (incidents.empty()) {
933 auto* empty = Gtk::make_managed<Gtk::Label>(
934 _("No unresolved observations or blocked changes."));
935 empty->set_margin(16);
936 empty->set_halign(Gtk::Align::CENTER);
937 incidents_list_.append(*empty);
938 return;
939 }
940
941 for (const auto& event : incidents) {
942 auto* row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 8);
943 row->set_margin(8);
944 row->add_css_class("rootguard-incident-row");
945
946 auto* title = Gtk::make_managed<Gtk::Label>(incident_title(event));
947 title->set_halign(Gtk::Align::START);
948 const auto severity = BastionGuard::RootGuard::classify_event(event);
949 if (event.verdict == "blocked")
950 title->add_css_class("rootguard-blocked");
952 title->add_css_class("rootguard-alert");
953 else
954 title->add_css_class("dim-label");
955 row->append(*title);
956
957 auto* detail = Gtk::make_managed<Gtk::Label>(incident_detail(event));
958 detail->set_halign(Gtk::Align::START);
959 detail->set_wrap(true);
960 detail->set_selectable(true);
961 row->append(*detail);
962
963 const bool audit_event = event.verdict == "audit";
964 const bool can_restore = !audit_event && restore_available(event);
965 const bool can_remove = !audit_event && remove_available(event);
966
967 if (!can_restore && !can_remove) {
968 const auto note_text = audit_event
969 ? Glib::ustring(
970 _("Audit observation: RootGuard did not block or alter the operation."))
971 : recovery_note(event);
972 if (!note_text.empty()) {
973 auto* note = Gtk::make_managed<Gtk::Label>(note_text);
974 note->set_halign(Gtk::Align::START);
975 note->set_wrap(true);
976 note->add_css_class("dim-label");
977 row->append(*note);
978 }
979 }
980
981 auto* actions = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
982 actions->set_halign(Gtk::Align::END);
983
984 if (can_restore) {
985 auto* restore = Gtk::make_managed<Gtk::Button>(_("Restore"));
986 restore->add_css_class("btn-primary");
987 restore->signal_clicked().connect([this, event] { restore_event(event); });
988 actions->append(*restore);
989 }
990 if (can_remove) {
991 auto* remove = Gtk::make_managed<Gtk::Button>(_("Quarantine"));
992 remove->add_css_class("btn-danger");
993 remove->signal_clicked().connect([this, event] { remove_event(event); });
994 actions->append(*remove);
995 }
996
997 auto* dismiss = Gtk::make_managed<Gtk::Button>(_("Dismiss"));
998 dismiss->signal_clicked().connect([this, event] { dismiss_event(event); });
999 actions->append(*dismiss);
1000 row->append(*actions);
1001 incidents_list_.append(*row);
1002 }
1003}
void remove_event(const BastionGuard::RootGuard::Event &event)
void restore_event(const BastionGuard::RootGuard::Event &event)
void dismiss_event(const BastionGuard::RootGuard::Event &event)
bool is_security_observation(const Event &event) noexcept
EventSeverity classify_event(const Event &event) noexcept
Glib::ustring incident_detail(const BastionGuard::RootGuard::Event &event)
bool restore_available(const BastionGuard::RootGuard::Event &event)
Glib::ustring recovery_note(const BastionGuard::RootGuard::Event &event)
bool remove_available(const BastionGuard::RootGuard::Event &event)
Glib::ustring incident_title(const BastionGuard::RootGuard::Event &event)

References _, BastionGuard::RootGuard::classify_event(), dismiss_event(), dismissed_events_, dismissed_paths_, BastionGuard::RootGuard::event_fingerprint(), BastionGuard::RootGuard::Snapshot::events, BastionGuard::RootGuard::Incident, incidents_list_, BastionGuard::RootGuard::is_security_observation(), remove_event(), and restore_event().

Referenced by on_snapshot().

◆ restore_event()

void RootGuardPage::restore_event ( const BastionGuard::RootGuard::Event & event)
private

Definition at line 1291 of file RootGuardPage.cpp.

1292{
1293 status_.set_text(_("Restoring protected metadata and unblocking the service…"));
1294 client_.restore_event(event);
1295}
void restore_event(const Event &event)

References _, client_, BastionGuard::RootGuard::Client::restore_event(), and status_.

Referenced by render_incidents(), and show_next_alert().

◆ save_application_rules()

void RootGuardPage::save_application_rules ( )
private

Definition at line 794 of file RootGuardPage.cpp.

795{
796 BastionGuard::RootGuard::ApplicationRules rules;
800
801 std::set<std::string> trusted;
802 trusted.insert(rules.trustedApplications.begin(), rules.trustedApplications.end());
803 trusted.insert(rules.trustedExecutables.begin(), rules.trustedExecutables.end());
804 for (const auto& denied : rules.deniedExecutables) {
805 if (trusted.count(denied) != 0) {
806 rules_status_.set_text(
807 _("The same executable cannot be both trusted and blocked."));
808 return;
809 }
810 }
811
812 rules_saving_ = true;
813 save_rules_button_.set_sensitive(false);
814 rules_status_.set_text(
815 _("Saving application rules and restarting RootGuard… Administrator authentication may be requested."));
816 status_.set_text(_("Saving RootGuard application rules…"));
817 client_.save_application_rules(std::move(rules));
818}
void save_application_rules(ApplicationRules rules)

References _, client_, collect_rules(), denied_executables_list_, BastionGuard::RootGuard::ApplicationRules::deniedExecutables, rules_saving_, rules_status_, BastionGuard::RootGuard::Client::save_application_rules(), save_rules_button_, status_, trusted_applications_list_, trusted_executables_list_, BastionGuard::RootGuard::ApplicationRules::trustedApplications, and BastionGuard::RootGuard::ApplicationRules::trustedExecutables.

Referenced by RootGuardPage().

◆ show_next_alert()

void RootGuardPage::show_next_alert ( )
private

Definition at line 1100 of file RootGuardPage.cpp.

1101{
1102 if (current_alert_ || alert_queue_.empty())
1103 return;
1104
1105 const auto event = alert_queue_.front();
1106 alert_queue_.pop();
1107 if (event.rule_id == 0 &&
1108 (event.reason_flags & RG_REASON_METADATA_BURST) != 0)
1109 last_global_burst_alert_shown_ = std::chrono::steady_clock::now();
1110 current_alert_ = std::make_unique<Gtk::Window>();
1111 auto* win = current_alert_.get();
1112 win->set_title("");
1113 win->set_default_size(720, 520);
1114 win->set_resizable(true);
1115 win->set_modal(false);
1116 win->add_css_class("BastionGuard-popup");
1117 win->add_css_class("BastionGuard-dialog");
1118 if (auto* root = dynamic_cast<Gtk::Window*>(get_root()))
1119 win->set_transient_for(*root);
1120
1121 auto* header = Gtk::make_managed<Gtk::HeaderBar>();
1122 header->set_show_title_buttons(false);
1123 header->set_decoration_layout("");
1124 header->add_css_class("custom-headerbar");
1125 auto* header_title = Gtk::make_managed<Gtk::Label>(
1126 event.event == "service-blocked"
1127 ? (event.verdict == "blocked"
1128 ? _("🛑 System service blocked")
1129 : _("⚠️ System service block failed"))
1130 : (event.reason_flags & RG_REASON_METADATA_BURST) != 0
1131 ? _("⚠️ Unusual permission-change burst")
1132 : event.rule_id == 0
1133 ? _("⚠️ Filesystem metadata change observed")
1134 : event.verdict == "blocked"
1135 ? _("🛑 Protected change blocked")
1136 : event.verdict == "audit"
1137 ? _("⚠️ Protected change observed")
1138 : _("⚠️ Protected change detected"));
1139 header_title->add_css_class("BastionGuard-title");
1140 header->pack_start(*header_title);
1141 win->set_titlebar(*header);
1142
1143 auto* content = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 10);
1144 content->set_margin(18);
1145 win->set_child(*content);
1146
1147 const Glib::ustring message_template =
1148 (event.reason_flags & RG_REASON_METADATA_BURST) != 0
1149 ? _("RootGuard detected many destructive permission/ownership changes in a short kernel window. Latest object:\n<b>%1</b>")
1150 : event.rule_id == 0
1151 ? _("RootGuard observed a filesystem metadata change to:\n<b>%1</b>")
1152 : event.verdict == "blocked"
1153 ? _("RootGuard blocked a change to:\n<b>%1</b>")
1154 : event.verdict == "audit"
1155 ? _("RootGuard observed a change to:\n<b>%1</b>")
1156 : _("RootGuard detected a change to:\n<b>%1</b>");
1157 auto* message = Gtk::make_managed<Gtk::Label>(
1158 Glib::ustring::compose(
1159 message_template,
1160 Glib::Markup::escape_text(event.filename.empty() ? "--" : event.filename)));
1161 message->set_use_markup(true);
1162 message->set_wrap(true);
1163 message->set_selectable(true);
1164 message->set_halign(Gtk::Align::CENTER);
1165 content->append(*message);
1166
1167 auto* detail = Gtk::make_managed<Gtk::Label>(incident_detail(event));
1168 detail->set_wrap(true);
1169 detail->set_selectable(true);
1170 detail->set_halign(Gtk::Align::START);
1171 detail->set_valign(Gtk::Align::START);
1172 detail->set_xalign(0.0F);
1173
1174 auto* detail_scroller = Gtk::make_managed<Gtk::ScrolledWindow>();
1175 detail_scroller->set_hexpand(true);
1176 detail_scroller->set_vexpand(true);
1177 detail_scroller->set_min_content_height(220);
1178 detail_scroller->set_policy(Gtk::PolicyType::AUTOMATIC,
1179 Gtk::PolicyType::AUTOMATIC);
1180 detail_scroller->set_child(*detail);
1181 content->append(*detail_scroller);
1182
1183 if (!has_exact_path(event)) {
1184 auto* path_notice = Gtk::make_managed<Gtk::Label>(
1185 _("Transparency notice: the kernel event contained only a basename. RootGuard "
1186 "shows the filesystem identity and disables path-based actions rather than "
1187 "guessing an unsafe path."));
1188 path_notice->set_wrap(true);
1189 path_notice->set_halign(Gtk::Align::CENTER);
1190 path_notice->add_css_class("dim-label");
1191 content->append(*path_notice);
1192 }
1193
1195 auto* service_notice = Gtk::make_managed<Gtk::Label>(
1196 event.event == "service-blocked"
1197 ? (event.verdict == "blocked"
1198 ? _("The affected system service has been stopped immediately. On systemd it is also runtime-masked.")
1199 : _("RootGuard could not stop the affected service. Review the init-system logs immediately."))
1200 : event.verdict == "blocked"
1201 ? _("RootGuard requested immediate service containment. A follow-up event will confirm the init-system action.")
1202 : _("Audit mode never stops or masks services. This event is informational only."));
1203 service_notice->set_wrap(true);
1204 service_notice->add_css_class("rootguard-blocked");
1205 service_notice->set_halign(Gtk::Align::CENTER);
1206 content->append(*service_notice);
1207 }
1208
1209 const bool audit_event = event.verdict == "audit";
1210 const bool can_restore = !audit_event && restore_available(event);
1211 const bool can_remove = !audit_event && remove_available(event);
1212 const auto note_text = audit_event
1213 ? Glib::ustring(
1214 _("Audit mode recorded this event without blocking or altering the operation. This notification is acknowledgement-only."))
1215 : recovery_note(event);
1216 if (!note_text.empty() && !can_restore && !can_remove) {
1217 auto* note = Gtk::make_managed<Gtk::Label>(note_text);
1218 note->set_wrap(true);
1219 note->set_halign(Gtk::Align::CENTER);
1220 note->add_css_class("dim-label");
1221 content->append(*note);
1222 }
1223
1224 auto* buttons = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 10);
1225 buttons->set_halign(Gtk::Align::CENTER);
1226
1227 auto* acknowledge = Gtk::make_managed<Gtk::Button>(
1228 event.verdict == "blocked" ? _("Keep blocked") : _("Acknowledge"));
1229 buttons->append(*acknowledge);
1230
1231 if (can_restore) {
1232 auto* restore = Gtk::make_managed<Gtk::Button>(_("Restore"));
1233 restore->add_css_class("btn-primary");
1234 restore->signal_clicked().connect([this, event] {
1235 restore_event(event);
1237 });
1238 buttons->append(*restore);
1239 }
1240 if (can_remove) {
1241 auto* remove = Gtk::make_managed<Gtk::Button>(_("Quarantine"));
1242 remove->add_css_class("btn-danger");
1243 remove->signal_clicked().connect([this, event] {
1244 remove_event(event);
1246 });
1247 buttons->append(*remove);
1248 }
1249 content->append(*buttons);
1250
1251 acknowledge->signal_clicked().connect([this, event] {
1252 dismiss_alert(event);
1254 });
1255 win->signal_close_request().connect([this, event]() -> bool {
1256 dismiss_alert(event);
1258 return true;
1259 }, false);
1260 win->present();
1261}
void dismiss_alert(const BastionGuard::RootGuard::Event &event)
bool is_system_service_path(const std::string &path) noexcept
bool has_exact_path(const BastionGuard::RootGuard::Event &event)
@ RG_REASON_METADATA_BURST

References _, alert_queue_, close_current_alert(), current_alert_, dismiss_alert(), BastionGuard::RootGuard::is_system_service_path(), last_global_burst_alert_shown_, remove_event(), restore_event(), and RG_REASON_METADATA_BURST.

Referenced by close_current_alert(), and queue_new_alerts().

Member Data Documentation

◆ alert_queue_

std::queue<BastionGuard::RootGuard::Event> RootGuardPage::alert_queue_
private

Definition at line 109 of file RootGuardPage.hpp.

Referenced by queue_new_alerts(), show_next_alert(), and ~RootGuardPage().

◆ alive_

std::shared_ptr<std::atomic_bool> RootGuardPage::alive_ {std::make_shared<std::atomic_bool>(true)}
private

Definition at line 112 of file RootGuardPage.hpp.

Referenced by close_current_alert(), and ~RootGuardPage().

◆ auto_refresh_connection_

sigc::connection RootGuardPage::auto_refresh_connection_
private

Definition at line 98 of file RootGuardPage.hpp.

Referenced by RootGuardPage(), and ~RootGuardPage().

◆ client_

◆ client_update_connection_

sigc::connection RootGuardPage::client_update_connection_
private

Definition at line 97 of file RootGuardPage.hpp.

Referenced by RootGuardPage(), and ~RootGuardPage().

◆ current_alert_

std::unique_ptr<Gtk::Window> RootGuardPage::current_alert_
private

◆ denied_executables_entry_

Gtk::Entry RootGuardPage::denied_executables_entry_
private

Definition at line 91 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ denied_executables_list_

Gtk::ListBox RootGuardPage::denied_executables_list_
private

◆ dismissed_events_

std::set<std::string> RootGuardPage::dismissed_events_
private

◆ dismissed_paths_

std::set<std::string> RootGuardPage::dismissed_paths_
private

Definition at line 107 of file RootGuardPage.hpp.

Referenced by dismiss_event(), queue_new_alerts(), and render_incidents().

◆ events_scroller_

Gtk::ScrolledWindow RootGuardPage::events_scroller_
private

Definition at line 81 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ events_view_

Gtk::TextView RootGuardPage::events_view_
private

Definition at line 82 of file RootGuardPage.hpp.

Referenced by render_events(), and RootGuardPage().

◆ home_scope_value_

Gtk::Label RootGuardPage::home_scope_value_
private

Definition at line 69 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ incidents_list_

Gtk::ListBox RootGuardPage::incidents_list_
private

Definition at line 78 of file RootGuardPage.hpp.

Referenced by render_incidents(), and RootGuardPage().

◆ incidents_scroller_

Gtk::ScrolledWindow RootGuardPage::incidents_scroller_
private

Definition at line 77 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ incidents_title_

Gtk::Label RootGuardPage::incidents_title_
private

Definition at line 76 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ init_value_

Gtk::Label RootGuardPage::init_value_
private

Definition at line 58 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ initial_snapshot_

bool RootGuardPage::initial_snapshot_ {true}
private

Definition at line 100 of file RootGuardPage.hpp.

Referenced by queue_new_alerts().

◆ last_global_burst_alert_shown_

std::chrono::steady_clock::time_point RootGuardPage::last_global_burst_alert_shown_ {}
private

Definition at line 110 of file RootGuardPage.hpp.

Referenced by queue_new_alerts(), and show_next_alert().

◆ last_snapshot_

std::optional<BastionGuard::RootGuard::Snapshot> RootGuardPage::last_snapshot_
private

Definition at line 104 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ mode_value_

Gtk::Label RootGuardPage::mode_value_
private

Definition at line 57 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ notified_paths_

std::set<std::string> RootGuardPage::notified_paths_
private

Definition at line 108 of file RootGuardPage.hpp.

Referenced by queue_new_alerts().

◆ page_stack_

Gtk::Stack RootGuardPage::page_stack_
private

Definition at line 61 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ page_switcher_

Gtk::StackSwitcher RootGuardPage::page_switcher_
private

Definition at line 62 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ pid_value_

Gtk::Label RootGuardPage::pid_value_
private

Definition at line 59 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ protection_info_button_

Gtk::MenuButton RootGuardPage::protection_info_button_
private

Definition at line 67 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ protection_info_popover_

Gtk::Popover RootGuardPage::protection_info_popover_
private

Definition at line 66 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ protection_label_

Gtk::Label RootGuardPage::protection_label_
private

Definition at line 65 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ protection_switch_

Gtk::Switch RootGuardPage::protection_switch_
private

Definition at line 64 of file RootGuardPage.hpp.

Referenced by on_protection_toggled(), on_snapshot(), and RootGuardPage().

◆ refresh_button_

Gtk::Button RootGuardPage::refresh_button_
private

Definition at line 74 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ reload_button_

Gtk::Button RootGuardPage::reload_button_
private

Definition at line 72 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ restart_button_

Gtk::Button RootGuardPage::restart_button_
private

Definition at line 73 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ rules_dirty_

bool RootGuardPage::rules_dirty_ {}
private

Definition at line 101 of file RootGuardPage.hpp.

Referenced by mark_rules_dirty(), on_snapshot(), and populate_application_rules().

◆ rules_saving_

bool RootGuardPage::rules_saving_ {}
private

◆ rules_signature_

std::string RootGuardPage::rules_signature_
private

Definition at line 103 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and populate_application_rules().

◆ rules_stack_

Gtk::Stack RootGuardPage::rules_stack_
private

Definition at line 84 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ rules_status_

◆ rules_switcher_

Gtk::StackSwitcher RootGuardPage::rules_switcher_
private

Definition at line 85 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ save_rules_button_

Gtk::Button RootGuardPage::save_rules_button_
private

◆ seen_events_

std::set<std::string> RootGuardPage::seen_events_
private

Definition at line 105 of file RootGuardPage.hpp.

Referenced by queue_new_alerts().

◆ service_value_

Gtk::Label RootGuardPage::service_value_
private

Definition at line 56 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ show_trusted_

Gtk::CheckButton RootGuardPage::show_trusted_
private

Definition at line 80 of file RootGuardPage.hpp.

Referenced by render_events(), and RootGuardPage().

◆ start_button_

Gtk::Button RootGuardPage::start_button_
private

Definition at line 70 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ status_

Gtk::Label RootGuardPage::status_
private

◆ stop_button_

Gtk::Button RootGuardPage::stop_button_
private

Definition at line 71 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ system_scope_value_

Gtk::Label RootGuardPage::system_scope_value_
private

Definition at line 68 of file RootGuardPage.hpp.

Referenced by on_snapshot(), and RootGuardPage().

◆ title_

Gtk::Label RootGuardPage::title_
private

Definition at line 55 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ trusted_applications_entry_

Gtk::Entry RootGuardPage::trusted_applications_entry_
private

Definition at line 87 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ trusted_applications_list_

Gtk::ListBox RootGuardPage::trusted_applications_list_
private

◆ trusted_executables_entry_

Gtk::Entry RootGuardPage::trusted_executables_entry_
private

Definition at line 89 of file RootGuardPage.hpp.

Referenced by RootGuardPage().

◆ trusted_executables_list_

Gtk::ListBox RootGuardPage::trusted_executables_list_
private

◆ updating_switch_

bool RootGuardPage::updating_switch_ {}
private

Definition at line 99 of file RootGuardPage.hpp.

Referenced by on_protection_toggled(), and on_snapshot().


The documentation for this class was generated from the following files: