diff --git a/src/cli/main.cpp b/src/cli/main.cpp index 16718754..3b082cb0 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -298,7 +298,7 @@ void print_usage() { fmt::print(" --env Apply environment overrides from govern.json\n"); fmt::print("\nSecurity Options:\n"); fmt::print(" --sandbox-level Security: restricted|standard|elevated|unrestricted\n"); - fmt::print(" (default: standard - safe for enterprise)\n"); + fmt::print(" (default: unrestricted)\n"); fmt::print(" --timeout Execution timeout per block (default: 30)\n"); fmt::print(" --memory-limit Memory limit per block (default: 512)\n"); fmt::print(" --allow-network Enable network access (default: disabled)\n"); diff --git a/src/interpreter/naab_val.cpp b/src/interpreter/naab_val.cpp index 4f89f046..34432579 100644 --- a/src/interpreter/naab_val.cpp +++ b/src/interpreter/naab_val.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include namespace naab { namespace interpreter { @@ -504,7 +506,13 @@ bool NaabVal::toBool() const { int NaabVal::toInt() const { if (isInt()) return asInt(); - if (isDouble()) return static_cast(asDouble()); + if (isDouble()) { + double d = asDouble(); + if (std::isnan(d)) return 0; + if (d >= static_cast(INT_MAX)) return INT_MAX; + if (d <= static_cast(INT_MIN)) return INT_MIN; + return static_cast(d); + } if (isBool()) return asBool() ? 1 : 0; if (isHeap()) return asHeap()->shared_value->toInt(); return 0; diff --git a/src/runtime/block_search_index.cpp b/src/runtime/block_search_index.cpp index 24405879..1a0156c3 100644 --- a/src/runtime/block_search_index.cpp +++ b/src/runtime/block_search_index.cpp @@ -168,6 +168,7 @@ class BlockSearchIndex::Impl { } if (ec) { fmt::print("[ERROR] Failed to open blocks directory: {}\n", blocks_path); + sqlite3_exec(db_, "ROLLBACK;", nullptr, nullptr, nullptr); return 0; } diff --git a/src/runtime/bounded_read.cpp b/src/runtime/bounded_read.cpp index d586f54f..60f61bba 100644 --- a/src/runtime/bounded_read.cpp +++ b/src/runtime/bounded_read.cpp @@ -22,10 +22,10 @@ std::optional readFileBounded(const std::string& path, // This eliminates the lstat-then-ifstream TOCTOU race. int fd = ::open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC); if (fd < 0) return std::nullopt; + struct FdGuard { int f; ~FdGuard() { if (f >= 0) ::close(f); } } guard{fd}; struct stat st; if (::fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) { - ::close(fd); return std::nullopt; } @@ -41,7 +41,6 @@ std::optional readFileBounded(const std::string& path, } content.append(buf, chunk); } - ::close(fd); if (n < 0) return std::nullopt; return content; #else diff --git a/src/runtime/governance_reports.cpp b/src/runtime/governance_reports.cpp index fe3a2750..03523f84 100644 --- a/src/runtime/governance_reports.cpp +++ b/src/runtime/governance_reports.cpp @@ -60,7 +60,7 @@ void GovernanceEngine::logAuditEvent(const std::string& event_type, entry["event"] = event_type; entry["rule"] = rule_name; entry["message"] = message; - if (!file.empty()) entry["file"] = file; + if (!file.empty()) entry["file"] = error::ErrorSanitizer::sanitizeFilePaths(file); if (line > 0) entry["line"] = line; // Include config rationale in audit entries when available diff --git a/src/runtime/resource_limits.cpp b/src/runtime/resource_limits.cpp index e9501a88..166061ec 100644 --- a/src/runtime/resource_limits.cpp +++ b/src/runtime/resource_limits.cpp @@ -41,6 +41,8 @@ static std::atomic g_win_timer_generation{0}; #else // V-RT-007: cancel flag for the POSIX timer thread (set by clearTimeout()). std::atomic ResourceLimiter::posix_timer_cancel_{false}; +// Generation counter for cancellable POSIX timer threads (matches Windows pattern). +static std::atomic g_posix_timer_generation{0}; #endif void ResourceLimiter::installSignalHandlers() { @@ -95,14 +97,15 @@ void ResourceLimiter::setExecutionTimeout(unsigned int seconds) { // concurrent call to setExecutionTimeout() has its own independent timer. pthread_t tid = pthread_self(); posix_timer_cancel_.store(false, std::memory_order_relaxed); - std::thread([seconds, tid]() { + uint64_t my_gen = ++g_posix_timer_generation; + std::thread([seconds, tid, my_gen]() { using clock = std::chrono::steady_clock; auto deadline = clock::now() + std::chrono::seconds(seconds); while (clock::now() < deadline) { - if (ResourceLimiter::posix_timer_cancel_.load(std::memory_order_relaxed)) return; + if (g_posix_timer_generation.load(std::memory_order_relaxed) != my_gen) return; std::this_thread::sleep_for(std::chrono::milliseconds(50)); } - if (!ResourceLimiter::posix_timer_cancel_.load(std::memory_order_relaxed)) { + if (g_posix_timer_generation.load(std::memory_order_relaxed) == my_gen) { // Set global_shutdown_ first so isTimeoutTriggered() returns true // even if the signal is not delivered immediately (e.g. tight loops // on Android/Termux where SIGALRM may stay pending). @@ -139,6 +142,9 @@ void ResourceLimiter::setExecutionTimeout(unsigned int seconds) { void ResourceLimiter::clearTimeout() { #ifndef _WIN32 // V-RT-007: cancel the posix timer thread and any residual system alarm. + // Bump generation counter to invalidate any in-flight timer thread + // (matches Windows pattern — stale timer sees mismatched generation and exits). + ++g_posix_timer_generation; posix_timer_cancel_.store(true, std::memory_order_relaxed); alarm(0); #else diff --git a/src/stdlib/agent_impl.cpp b/src/stdlib/agent_impl.cpp index 3808d129..6ad530f2 100644 --- a/src/stdlib/agent_impl.cpp +++ b/src/stdlib/agent_impl.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1495,7 +1496,9 @@ static NaabVal agentSend(std::vector& args) { if (config->retry.jitter && delay > 0) { int jitter_range = delay / 4; if (jitter_range > 0) { - delay += (std::rand() % (2 * jitter_range + 1)) - jitter_range; + static thread_local std::mt19937 rng(std::random_device{}()); + std::uniform_int_distribution dist(-jitter_range, jitter_range); + delay += dist(rng); } } if (delay > 0) { diff --git a/src/stdlib/json_impl.cpp b/src/stdlib/json_impl.cpp index 4ebda556..926ff623 100644 --- a/src/stdlib/json_impl.cpp +++ b/src/stdlib/json_impl.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include using json = nlohmann::json; @@ -108,7 +110,12 @@ interpreter::NaabVal jsonToValue(const json& j) { } else if (j.is_boolean()) { return interpreter::NaabVal::makeBool(j.get()); } else if (j.is_number_integer()) { - return interpreter::NaabVal::makeInt(j.get()); + auto val = j.get(); + if (val >= INT_MIN && val <= INT_MAX) { + return interpreter::NaabVal::makeInt(static_cast(val)); + } else { + return interpreter::NaabVal::makeDouble(static_cast(val)); + } } else if (j.is_number_float()) { return interpreter::NaabVal::makeDouble(j.get()); } else if (j.is_string()) {