From fb1aace89d73b0c78e29ef93ab17dd42fd811e32 Mon Sep 17 00:00:00 2001
From: Egor Ivkov <e.o.ivkov@gmail.com>
Date: Mon, 1 Apr 2024 17:53:11 +0300
Subject: [PATCH] feat: add integrity_violation audit log event

(cherry picked from commit 46dfe8c102103adad168daa0f33a31cc0d46e160)
---
 src/audit.rs           |  2 +-
 src/lib.rs             | 16 ++++++++++++++++
 test/int/test_audit.py | 25 +++++++++++++++++++++++++
 3 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/src/audit.rs b/src/audit.rs
index 9b8952af79..028b5e193b 100644
--- a/src/audit.rs
+++ b/src/audit.rs
@@ -1,4 +1,4 @@
-use crate::traft::{LogicalClock, RaftSpaceAccess};
+use crate::traft::LogicalClock;
 use once_cell::sync::OnceCell;
 use std::ffi::{CStr, CString};
 use tarantool::{error::TarantoolError, log::SayLevel};
diff --git a/src/lib.rs b/src/lib.rs
index e331d5bb1d..1218f03bce 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -553,6 +553,22 @@ fn init_common(
     tlog::set_core_logger_is_initialized(true);
 
     if let Err(e) = tarantool::set_cfg(cfg) {
+        let tnt_err = ::tarantool::error::TarantoolError::last();
+        let err_ty = tnt_err.error_type();
+        if err_ty == "XlogError" {
+            if let Some(config) = &config.instance.audit {
+                // Init log with stab values for raft_id and gen.
+                // We need to log the 'integrity_violation' event and
+                // there is nowhere to take raft state from at the moment.
+                audit::init(config, 0, 0);
+                crate::audit!(
+                    message: "integrity violation detected",
+                    title: "integrity_violation",
+                    severity: High,
+                    error: format!("{err_ty}: {}", tnt_err.message()),
+                )
+            }
+        }
         tlog::set_core_logger_is_initialized(false);
         return Err(Error::other(format!("core initialization failed: {e}")));
     }
diff --git a/test/int/test_audit.py b/test/int/test_audit.py
index 67a322f34f..3c4fb3a324 100644
--- a/test/int/test_audit.py
+++ b/test/int/test_audit.py
@@ -90,6 +90,31 @@ def test_startup(instance: Instance):
     assert event["initiator"] == "admin"
 
 
+def test_integrity_violation(instance: Instance):
+    # Instance was up for some time
+    instance.start()
+    instance.terminate()
+
+    # Then data files were modified and instance restarted
+    snap_name = f"{instance.data_dir}/00000000000000000000.snap"
+    with open(snap_name, "w") as snap:
+        snap.write("abc")
+
+    # Instance should fail to start
+    instance.fail_to_start()
+
+    # Instance should  detect integrity violation
+    events = list(AuditFile(instance.audit_flag_value).events())
+    event = take_until_title(iter(events), "integrity_violation")
+    assert event is not None
+    assert event["message"] == "integrity violation detected"
+    assert event["severity"] == "high"
+    assert (
+        event["error"]
+        == "XlogError: Unexpected end of file, run with 'force_recovery = true'"
+    )
+
+
 def test_recover_database(instance: Instance):
     instance.start()
     instance.wait_online()
-- 
GitLab