strip sensitive information from logs
This commit is contained in:
@@ -14,12 +14,18 @@
|
||||
#include "base64.h"
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <winnt.h>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <system_error>
|
||||
#include <cstdlib>
|
||||
|
||||
// Forward-declare OutputDebugStringA without pulling in <windows.h> (which
|
||||
// drags winsock conflicts as noted in ANSLicense.h). Used by ANSCENTER_DebugLog.
|
||||
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
|
||||
|
||||
#define MAX_CUSTOMER_NAME_LEN 100
|
||||
#define FEATURE_1 0x1
|
||||
#define FEATURE_2 0x2
|
||||
@@ -30,6 +36,135 @@
|
||||
#define FEATURE_7 0x40
|
||||
#define FEATURE_8 0x80
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Log-prefix scrubber.
|
||||
//
|
||||
// We don't want noisy fully-qualified per-machine paths in log lines:
|
||||
// "model loaded from C:\ProgramData\Jh7O7nUe7vS\Models\EngineModels\X"
|
||||
// becomes
|
||||
// "model loaded from Models\EngineModels\X"
|
||||
//
|
||||
// The prefix list is built once on first use and combines:
|
||||
// - hardcoded ANSCENTER ProgramData roots (edit kStaticPrefixes below),
|
||||
// - the OS temp directory resolved at runtime via std::filesystem,
|
||||
// - per-OS conventional temp/env-var paths (TEMP/TMP on Windows;
|
||||
// /tmp/, /var/tmp/, TMPDIR on POSIX).
|
||||
//
|
||||
// Each prefix is normalised to lowercase, has a trailing separator forced,
|
||||
// and is added in BOTH `\` and `/` slash flavours so the matcher works on
|
||||
// either form. Prefixes are sorted longest-first so a more specific prefix
|
||||
// (e.g. C:\Users\Bob\AppData\Local\Temp\) wins over a shorter overlapping one.
|
||||
//
|
||||
// Case-insensitive, handles both `\` and `/` separators. Single pass, O(N*P)
|
||||
// where N is message length and P is prefix count (~5–10 in practice). Called
|
||||
// from every SPDLogger::Log* path and from ANSCENTER_DebugLog (the choke point
|
||||
// used by ANS_DBG / ANSLIB_DBG).
|
||||
// ----------------------------------------------------------------------------
|
||||
namespace {
|
||||
|
||||
inline std::string to_lower_ascii(const std::string& s) {
|
||||
std::string out(s.size(), '\0');
|
||||
for (size_t i = 0; i < s.size(); ++i) {
|
||||
out[i] = static_cast<char>(std::tolower(static_cast<unsigned char>(s[i])));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Append both `\` and `/` slash flavours of `raw` to `dst`, lower-cased
|
||||
// and with a trailing separator forced. Empty / pathologically short
|
||||
// entries are skipped.
|
||||
inline void push_prefix_variants(std::vector<std::string>& dst, const std::string& raw) {
|
||||
if (raw.size() < 2) return;
|
||||
std::string normalised = raw;
|
||||
char last = normalised.back();
|
||||
if (last != '\\' && last != '/') normalised.push_back('\\');
|
||||
std::string lowerBs = to_lower_ascii(normalised);
|
||||
std::string lowerFs = lowerBs;
|
||||
for (char& c : lowerFs) if (c == '\\') c = '/';
|
||||
dst.push_back(lowerBs);
|
||||
if (lowerFs != lowerBs) dst.push_back(lowerFs);
|
||||
}
|
||||
|
||||
// Build the prefix table once. Function-local static is initialised
|
||||
// exactly once per process (C++11 magic-static rule, thread-safe).
|
||||
const std::vector<std::string>& GetLogPrefixesLower() {
|
||||
static const std::vector<std::string> kPrefixesLower = []() {
|
||||
std::vector<std::string> v;
|
||||
|
||||
// 1. Hardcoded ANSCENTER ProgramData roots — edit this list to
|
||||
// add or remove well-known noise prefixes that aren't tied to
|
||||
// the OS temp/user dir.
|
||||
static const char* kStaticPrefixes[] = {
|
||||
"C:\\ProgramData\\ANSCENTER\\",
|
||||
"C:\\ProgramData\\Jh7O7nUe7vS\\",
|
||||
nullptr,
|
||||
};
|
||||
for (int i = 0; kStaticPrefixes[i]; ++i) {
|
||||
push_prefix_variants(v, kStaticPrefixes[i]);
|
||||
}
|
||||
|
||||
// 2. OS temp directory (Windows: usually %LOCALAPPDATA%\Temp;
|
||||
// Linux: usually /tmp; macOS: /private/var/folders/...).
|
||||
try {
|
||||
push_prefix_variants(v, std::filesystem::temp_directory_path().string());
|
||||
} catch (...) { /* ignore — fallbacks below cover most cases */ }
|
||||
|
||||
// 3. Per-OS conventional fallbacks in case temp_directory_path()
|
||||
// differs from what the env vars say or returns nothing useful.
|
||||
#ifdef _WIN32
|
||||
if (const char* p = std::getenv("TEMP")) push_prefix_variants(v, p);
|
||||
if (const char* p = std::getenv("TMP")) push_prefix_variants(v, p);
|
||||
push_prefix_variants(v, "C:\\Windows\\Temp\\"); // SYSTEM/services temp
|
||||
#else
|
||||
if (const char* p = std::getenv("TMPDIR")) push_prefix_variants(v, p);
|
||||
push_prefix_variants(v, "/tmp/");
|
||||
push_prefix_variants(v, "/var/tmp/");
|
||||
#endif
|
||||
|
||||
// 4. De-duplicate (env vars + filesystem API often agree).
|
||||
std::sort(v.begin(), v.end());
|
||||
v.erase(std::unique(v.begin(), v.end()), v.end());
|
||||
|
||||
// 5. Sort longest-first so specific prefixes win over shorter
|
||||
// overlapping ones (e.g. AppData\Local\Temp\ before AppData\).
|
||||
std::sort(v.begin(), v.end(),
|
||||
[](const std::string& a, const std::string& b) {
|
||||
return a.size() > b.size();
|
||||
});
|
||||
|
||||
return v;
|
||||
}();
|
||||
return kPrefixesLower;
|
||||
}
|
||||
|
||||
inline std::string strip_log_prefixes(const std::string& input) {
|
||||
if (input.empty()) return input;
|
||||
|
||||
const auto& prefixes = GetLogPrefixesLower();
|
||||
std::string lower = to_lower_ascii(input);
|
||||
|
||||
std::string out;
|
||||
out.reserve(input.size());
|
||||
size_t i = 0;
|
||||
while (i < input.size()) {
|
||||
bool matched = false;
|
||||
for (const auto& p : prefixes) {
|
||||
if (i + p.size() <= input.size() &&
|
||||
lower.compare(i, p.size(), p) == 0) {
|
||||
i += p.size();
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
out.push_back(input[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
template <typename T> T GetData(const boost::property_tree::ptree& pt, const std::string& key)
|
||||
@@ -242,13 +377,14 @@ namespace ANSCENTER
|
||||
//__FUNCTION__
|
||||
void SPDLogger::LogTrace(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
|
||||
std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
|
||||
std::string scrubbed = strip_log_prefixes(message);
|
||||
|
||||
try {
|
||||
auto logger = spdlog::get("anslogger_console");
|
||||
if (logger) logger->trace("{}: {}", msg, message);
|
||||
if (logger) logger->trace("{}: {}", msg, scrubbed);
|
||||
|
||||
auto eventLogger = spdlog::get("anslogger_event");
|
||||
if (eventLogger) eventLogger->trace("{}: {}", msg, message);
|
||||
if (eventLogger) eventLogger->trace("{}: {}", msg, scrubbed);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "SPDLogger::LogError - Exception: " << e.what() << std::endl;
|
||||
@@ -256,14 +392,15 @@ namespace ANSCENTER
|
||||
}
|
||||
void SPDLogger::LogInfo(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
|
||||
std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
|
||||
std::string scrubbed = strip_log_prefixes(message);
|
||||
|
||||
try {
|
||||
auto logger = spdlog::get("anslogger_console");
|
||||
if (logger) {
|
||||
logger->info("{}: {}", msg, message);
|
||||
logger->info("{}: {}", msg, scrubbed);
|
||||
}
|
||||
auto eventLogger = spdlog::get("anslogger_event");
|
||||
if (eventLogger) eventLogger->info("{}: {}", msg, message);
|
||||
if (eventLogger) eventLogger->info("{}: {}", msg, scrubbed);
|
||||
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
@@ -272,11 +409,12 @@ namespace ANSCENTER
|
||||
}
|
||||
void SPDLogger::LogWarn(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
|
||||
std::string messageSource = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
|
||||
std::string scrubbed = strip_log_prefixes(message);
|
||||
|
||||
try {
|
||||
auto logger = spdlog::get("anslogger_console");
|
||||
if (logger) {
|
||||
logger->warn("{}: {}", messageSource, message);
|
||||
logger->warn("{}: {}", messageSource, scrubbed);
|
||||
}
|
||||
else {
|
||||
std::cerr << "SPDLogger::LogWarn - Logger not initialized: anslogger_console" << std::endl;
|
||||
@@ -288,11 +426,12 @@ namespace ANSCENTER
|
||||
}
|
||||
void SPDLogger::LogDebug(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
|
||||
std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
|
||||
std::string scrubbed = strip_log_prefixes(message);
|
||||
|
||||
try {
|
||||
auto logger = spdlog::get("anslogger_console");
|
||||
if (logger) {
|
||||
logger->debug("{}: {}", msg, message);
|
||||
logger->debug("{}: {}", msg, scrubbed);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
@@ -301,13 +440,14 @@ namespace ANSCENTER
|
||||
}
|
||||
void SPDLogger::LogError(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
|
||||
std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
|
||||
std::string scrubbed = strip_log_prefixes(message);
|
||||
|
||||
try {
|
||||
auto logger = spdlog::get("anslogger_console");
|
||||
if (logger) logger->error("{}: {}", msg, message);
|
||||
if (logger) logger->error("{}: {}", msg, scrubbed);
|
||||
|
||||
auto eventLogger = spdlog::get("anslogger_event");
|
||||
if (eventLogger) eventLogger->error("{}: {}", msg, message);
|
||||
if (eventLogger) eventLogger->error("{}: {}", msg, scrubbed);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "SPDLogger::LogError - Exception: " << e.what() << std::endl;
|
||||
@@ -315,13 +455,14 @@ namespace ANSCENTER
|
||||
}
|
||||
void SPDLogger::LogFatal(const std::string& source, const std::string& message, const std::string& fileSource, int lineSource) {
|
||||
std::string msg = "[" + path_to_filename(fileSource) + "] [" + std::to_string(lineSource) + "] " + source;
|
||||
std::string scrubbed = strip_log_prefixes(message);
|
||||
|
||||
try {
|
||||
auto logger = spdlog::get("anslogger_console");
|
||||
if (logger) logger->critical("{}: {}", msg, message);
|
||||
if (logger) logger->critical("{}: {}", msg, scrubbed);
|
||||
|
||||
auto eventLogger = spdlog::get("anslogger_event");
|
||||
if (eventLogger) eventLogger->critical("{}: {}", msg, message);
|
||||
if (eventLogger) eventLogger->critical("{}: {}", msg, scrubbed);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "SPDLogger::LogFatal - Exception: " << e.what() << std::endl;
|
||||
@@ -352,6 +493,19 @@ extern "C" {
|
||||
src ? src : "", msg ? msg : "", file ? file : "", line);
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
// DebugView emit path used by ANS_DBG and ANSLIB_DBG. Single choke point
|
||||
// so the prefix scrubber lives in one place; macros just hand off the
|
||||
// already-formatted "[Tag] body\n" buffer here.
|
||||
ANSLICENSE_API void ANSCENTER_DebugLog(const char* buf) {
|
||||
if (!buf) return;
|
||||
try {
|
||||
std::string scrubbed = strip_log_prefixes(buf);
|
||||
OutputDebugStringA(scrubbed.c_str());
|
||||
std::fputs(scrubbed.c_str(), stderr);
|
||||
std::fflush(stderr);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ANSCENTER
|
||||
|
||||
Reference in New Issue
Block a user