implement Milestone 8: SQLite Database with injection prevention

This commit is contained in:
2026-01-18 15:18:47 +01:00
parent 2bb083fd7d
commit a94e0d5d63
7 changed files with 1396 additions and 1 deletions

View File

@@ -7,6 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Find dependencies via vcpkg
find_package(Lua REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
find_package(unofficial-sqlite3 CONFIG REQUIRED)
# Sandbox library (the code being tested)
add_library(mosis-sandbox STATIC
@@ -19,6 +20,7 @@ add_library(mosis-sandbox STATIC
../src/main/cpp/sandbox/json_api.cpp
../src/main/cpp/sandbox/crypto_api.cpp
../src/main/cpp/sandbox/virtual_fs.cpp
../src/main/cpp/sandbox/database_manager.cpp
)
target_include_directories(mosis-sandbox PUBLIC
../src/main/cpp/sandbox
@@ -27,6 +29,7 @@ target_include_directories(mosis-sandbox PUBLIC
target_link_libraries(mosis-sandbox PUBLIC
${LUA_LIBRARIES}
nlohmann_json::nlohmann_json
unofficial::sqlite3::sqlite3
)
# Windows BCrypt for crypto API
if(WIN32)

View File

@@ -10,6 +10,7 @@
#include "json_api.h"
#include "crypto_api.h"
#include "virtual_fs.h"
#include "database_manager.h"
#include <filesystem>
#include <fstream>
#include <sstream>
@@ -1259,6 +1260,215 @@ bool Test_VirtualFSMaxFileSize(std::string& error_msg) {
return true;
}
//=============================================================================
// MILESTONE 8: SQLITE DATABASE TESTS
//=============================================================================
bool Test_DatabaseCreatesTables(std::string& error_msg) {
std::string test_root = "test_db_app";
mosis::DatabaseManager manager("test.app", test_root);
std::string err;
auto db = manager.Open("test", err);
EXPECT_TRUE(db != nullptr);
// Create table
EXPECT_TRUE(db->Execute(
"CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT)",
{}, err));
// Insert
EXPECT_TRUE(db->Execute(
"INSERT INTO items (name) VALUES (?)",
{std::string("Test Item")}, err));
// Query
auto rows = db->Query("SELECT * FROM items", {}, err);
EXPECT_TRUE(rows.has_value());
EXPECT_TRUE(rows->size() == 1);
db->Close();
manager.CloseAll();
// Cleanup
std::filesystem::remove_all(test_root);
return true;
}
bool Test_DatabasePreparedStatements(std::string& error_msg) {
std::string test_root = "test_db_app";
mosis::DatabaseManager manager("test.app", test_root);
std::string err;
auto db = manager.Open("test", err);
EXPECT_TRUE(db != nullptr);
db->Execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)", {}, err);
db->Execute("INSERT INTO users (name) VALUES (?)", {std::string("Alice")}, err);
// Attempt SQL injection via parameter - should be safely escaped
std::string malicious = "'; DROP TABLE users; --";
auto rows = db->Query("SELECT * FROM users WHERE name = ?", {malicious}, err);
// Query should succeed (finding nothing) and table should still exist
EXPECT_TRUE(rows.has_value());
EXPECT_TRUE(rows->size() == 0);
// Table should still exist
auto check = db->Query("SELECT * FROM users", {}, err);
EXPECT_TRUE(check.has_value());
EXPECT_TRUE(check->size() == 1);
db->Close();
std::filesystem::remove_all(test_root);
return true;
}
bool Test_DatabaseBlocksAttach(std::string& error_msg) {
std::string test_root = "test_db_app";
mosis::DatabaseManager manager("test.app", test_root);
std::string err;
auto db = manager.Open("test", err);
EXPECT_TRUE(db != nullptr);
// Try to attach another database - should fail
EXPECT_FALSE(db->Execute("ATTACH DATABASE '/etc/passwd' AS evil", {}, err));
EXPECT_TRUE(err.find("authorized") != std::string::npos ||
err.find("denied") != std::string::npos ||
err.find("not authorized") != std::string::npos);
db->Close();
std::filesystem::remove_all(test_root);
return true;
}
bool Test_DatabaseBlocksDangerousPragma(std::string& error_msg) {
std::string test_root = "test_db_app";
mosis::DatabaseManager manager("test.app", test_root);
std::string err;
auto db = manager.Open("test", err);
EXPECT_TRUE(db != nullptr);
// Try dangerous PRAGMAs - should fail
EXPECT_FALSE(db->Execute("PRAGMA journal_mode = OFF", {}, err));
err.clear();
EXPECT_FALSE(db->Execute("PRAGMA synchronous = OFF", {}, err));
// Safe PRAGMAs should work
err.clear();
auto rows = db->Query("PRAGMA table_info(sqlite_master)", {}, err);
EXPECT_TRUE(rows.has_value());
db->Close();
std::filesystem::remove_all(test_root);
return true;
}
bool Test_DatabaseMultiple(std::string& error_msg) {
std::string test_root = "test_db_app";
mosis::DatabaseManager manager("test.app", test_root);
std::string err;
auto db1 = manager.Open("db1", err);
auto db2 = manager.Open("db2", err);
EXPECT_TRUE(db1 != nullptr);
EXPECT_TRUE(db2 != nullptr);
EXPECT_TRUE(manager.GetOpenDatabaseCount() == 2);
// They should be independent
db1->Execute("CREATE TABLE t1 (x INTEGER)", {}, err);
db2->Execute("CREATE TABLE t2 (y INTEGER)", {}, err);
// t1 shouldn't exist in db2
auto rows = db2->Query("SELECT * FROM t1", {}, err);
EXPECT_FALSE(rows.has_value()); // Should fail - table doesn't exist
manager.CloseAll();
std::filesystem::remove_all(test_root);
return true;
}
bool Test_DatabaseLuaIntegration(std::string& error_msg) {
SandboxContext ctx = TestContext();
LuaSandbox sandbox(ctx);
std::string test_root = "test_db_lua";
mosis::DatabaseManager manager("test.app", test_root);
mosis::RegisterDatabaseAPI(sandbox.GetState(), &manager);
std::string script = R"lua(
-- Just test if database global exists
if not database then
error("database global not found")
end
if not database.open then
error("database.open not found")
end
)lua";
bool ok = sandbox.LoadString(script, "db_test");
manager.CloseAll();
std::filesystem::remove_all(test_root);
if (!ok) {
error_msg = "Lua test failed: " + sandbox.GetLastError();
return false;
}
return true;
}
bool Test_DatabaseInvalidNames(std::string& error_msg) {
std::string test_root = "test_db_app";
mosis::DatabaseManager manager("test.app", test_root);
std::string err;
// Path traversal
auto db1 = manager.Open("../evil", err);
EXPECT_TRUE(db1 == nullptr);
// Absolute path component
err.clear();
auto db2 = manager.Open("/etc/passwd", err);
EXPECT_TRUE(db2 == nullptr);
// Special characters
err.clear();
auto db3 = manager.Open("test;drop", err);
EXPECT_TRUE(db3 == nullptr);
std::filesystem::remove_all(test_root);
return true;
}
bool Test_DatabaseLastInsertAndChanges(std::string& error_msg) {
std::string test_root = "test_db_app";
mosis::DatabaseManager manager("test.app", test_root);
std::string err;
auto db = manager.Open("test", err);
EXPECT_TRUE(db != nullptr);
db->Execute("CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)", {}, err);
db->Execute("INSERT INTO items (name) VALUES (?)", {std::string("Item 1")}, err);
EXPECT_TRUE(db->GetLastInsertRowId() == 1);
EXPECT_TRUE(db->GetChanges() == 1);
db->Execute("INSERT INTO items (name) VALUES (?)", {std::string("Item 2")}, err);
EXPECT_TRUE(db->GetLastInsertRowId() == 2);
db->Execute("UPDATE items SET name = ?", {std::string("Updated")}, err);
EXPECT_TRUE(db->GetChanges() == 2); // Updated 2 rows
db->Close();
std::filesystem::remove_all(test_root);
return true;
}
//=============================================================================
// MAIN
//=============================================================================
@@ -1371,6 +1581,16 @@ int main(int argc, char* argv[]) {
harness.AddTest("VirtualFSLuaIntegration", Test_VirtualFSLuaIntegration);
harness.AddTest("VirtualFSMaxFileSize", Test_VirtualFSMaxFileSize);
// Milestone 8: SQLite Database
harness.AddTest("DatabaseCreatesTables", Test_DatabaseCreatesTables);
harness.AddTest("DatabasePreparedStatements", Test_DatabasePreparedStatements);
harness.AddTest("DatabaseBlocksAttach", Test_DatabaseBlocksAttach);
harness.AddTest("DatabaseBlocksDangerousPragma", Test_DatabaseBlocksDangerousPragma);
harness.AddTest("DatabaseMultiple", Test_DatabaseMultiple);
harness.AddTest("DatabaseLuaIntegration", Test_DatabaseLuaIntegration);
harness.AddTest("DatabaseInvalidNames", Test_DatabaseInvalidNames);
harness.AddTest("DatabaseLastInsertAndChanges", Test_DatabaseLastInsertAndChanges);
// Run tests
auto results = harness.Run(filter);

9
sandbox-test/vcpkg.json Normal file
View File

@@ -0,0 +1,9 @@
{
"name": "sandbox-test",
"version-string": "0.1.0",
"dependencies": [
"lua",
"nlohmann-json",
"sqlite3"
]
}