add JSON and crypto APIs with sandbox protection (milestone 6 complete)
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
#include "rate_limiter.h"
|
||||
#include "path_sandbox.h"
|
||||
#include "timer_manager.h"
|
||||
#include "json_api.h"
|
||||
#include "crypto_api.h"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
@@ -874,6 +876,204 @@ bool Test_MinIntervalEnforced(std::string& error_msg) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// MILESTONE 6: JSON & CRYPTO API TESTS
|
||||
//=============================================================================
|
||||
|
||||
bool Test_JsonDecodeValid(std::string& error_msg) {
|
||||
SandboxContext ctx = TestContext();
|
||||
LuaSandbox sandbox(ctx);
|
||||
mosis::RegisterJsonAPI(sandbox.GetState());
|
||||
|
||||
std::string script = R"(
|
||||
local obj = json.decode('{"name":"test","value":42,"arr":[1,2,3]}')
|
||||
assert(obj.name == "test", "name should be test")
|
||||
assert(obj.value == 42, "value should be 42")
|
||||
assert(#obj.arr == 3, "arr should have 3 elements")
|
||||
assert(obj.arr[1] == 1, "first element should be 1")
|
||||
)";
|
||||
|
||||
if (!sandbox.LoadString(script, "decode_test")) {
|
||||
error_msg = "JSON decode failed: " + sandbox.GetLastError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Test_JsonDecodeRejectsDeep(std::string& error_msg) {
|
||||
SandboxContext ctx = TestContext();
|
||||
LuaSandbox sandbox(ctx);
|
||||
|
||||
mosis::JsonLimits limits;
|
||||
limits.max_depth = 5;
|
||||
mosis::RegisterJsonAPI(sandbox.GetState(), limits);
|
||||
|
||||
// Create deeply nested JSON (10 levels)
|
||||
std::string script = R"(
|
||||
local deep_json = '[[[[[[[[[[1]]]]]]]]]]'
|
||||
local result, err = json.decode(deep_json)
|
||||
assert(result == nil, 'should fail on deep nesting')
|
||||
assert(err and err:find('depth'), 'error should mention depth')
|
||||
)";
|
||||
|
||||
if (!sandbox.LoadString(script, "deep_test")) {
|
||||
error_msg = "Test failed: " + sandbox.GetLastError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Test_JsonEncodeValid(std::string& error_msg) {
|
||||
SandboxContext ctx = TestContext();
|
||||
LuaSandbox sandbox(ctx);
|
||||
mosis::RegisterJsonAPI(sandbox.GetState());
|
||||
|
||||
std::string script = R"(
|
||||
local str = json.encode({name = "test", value = 42})
|
||||
assert(type(str) == "string", "should return string")
|
||||
-- Decode back to verify round-trip
|
||||
local obj = json.decode(str)
|
||||
assert(obj.name == "test", "round-trip name")
|
||||
assert(obj.value == 42, "round-trip value")
|
||||
)";
|
||||
|
||||
if (!sandbox.LoadString(script, "encode_test")) {
|
||||
error_msg = "JSON encode failed: " + sandbox.GetLastError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Test_JsonEncodeDetectsCycles(std::string& error_msg) {
|
||||
SandboxContext ctx = TestContext();
|
||||
LuaSandbox sandbox(ctx);
|
||||
mosis::RegisterJsonAPI(sandbox.GetState());
|
||||
|
||||
std::string script = R"(
|
||||
local t = {a = 1}
|
||||
t.self = t -- Create cycle
|
||||
local result, err = json.encode(t)
|
||||
assert(result == nil, 'should fail on cycle')
|
||||
assert(err and (err:find('cycle') or err:find('circular')), 'should mention cycle')
|
||||
)";
|
||||
|
||||
if (!sandbox.LoadString(script, "cycle_test")) {
|
||||
error_msg = "Test failed: " + sandbox.GetLastError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Test_JsonRejectsTooLarge(std::string& error_msg) {
|
||||
SandboxContext ctx = TestContext();
|
||||
LuaSandbox sandbox(ctx);
|
||||
|
||||
mosis::JsonLimits limits;
|
||||
limits.max_array_size = 10;
|
||||
mosis::RegisterJsonAPI(sandbox.GetState(), limits);
|
||||
|
||||
std::string script = R"(
|
||||
-- Try to decode array with 20 elements
|
||||
local result, err = json.decode('[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]')
|
||||
assert(result == nil, 'should fail on large array')
|
||||
assert(err and (err:find('size') or err:find('limit')), 'should mention size limit')
|
||||
)";
|
||||
|
||||
if (!sandbox.LoadString(script, "size_test")) {
|
||||
error_msg = "Test failed: " + sandbox.GetLastError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Test_CryptoRandomBytes(std::string& error_msg) {
|
||||
SandboxContext ctx = TestContext();
|
||||
LuaSandbox sandbox(ctx);
|
||||
mosis::RegisterCryptoAPI(sandbox.GetState());
|
||||
|
||||
std::string script = R"(
|
||||
local bytes = crypto.randomBytes(16)
|
||||
assert(#bytes == 16, 'should be 16 bytes')
|
||||
|
||||
-- Should be different each time
|
||||
local bytes2 = crypto.randomBytes(16)
|
||||
assert(bytes ~= bytes2, 'should be random')
|
||||
)";
|
||||
|
||||
if (!sandbox.LoadString(script, "random_test")) {
|
||||
error_msg = "Random bytes test failed: " + sandbox.GetLastError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Test_CryptoHashSHA256(std::string& error_msg) {
|
||||
SandboxContext ctx = TestContext();
|
||||
LuaSandbox sandbox(ctx);
|
||||
mosis::RegisterCryptoAPI(sandbox.GetState());
|
||||
|
||||
std::string script = R"(
|
||||
local hash = crypto.hash("sha256", "hello")
|
||||
-- Known SHA256 of "hello"
|
||||
local expected = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
|
||||
assert(hash == expected, 'SHA256 mismatch: got ' .. hash)
|
||||
)";
|
||||
|
||||
if (!sandbox.LoadString(script, "hash_test")) {
|
||||
error_msg = "Hash test failed: " + sandbox.GetLastError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Test_CryptoHMAC(std::string& error_msg) {
|
||||
SandboxContext ctx = TestContext();
|
||||
LuaSandbox sandbox(ctx);
|
||||
mosis::RegisterCryptoAPI(sandbox.GetState());
|
||||
|
||||
std::string script = R"(
|
||||
local hmac = crypto.hmac("sha256", "key", "message")
|
||||
-- Known HMAC-SHA256 of "message" with key "key"
|
||||
local expected = "6e9ef29b75fffc5b7abae527d58fdadb2fe42e7219011976917343065f58ed4a"
|
||||
assert(hmac == expected, 'HMAC mismatch: got ' .. hmac)
|
||||
)";
|
||||
|
||||
if (!sandbox.LoadString(script, "hmac_test")) {
|
||||
error_msg = "HMAC test failed: " + sandbox.GetLastError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Test_SecureMathRandom(std::string& error_msg) {
|
||||
SandboxContext ctx = TestContext();
|
||||
LuaSandbox sandbox(ctx);
|
||||
|
||||
mosis::SecureRandom rng;
|
||||
mosis::RegisterSecureMathRandom(sandbox.GetState(), &rng);
|
||||
|
||||
std::string script = R"(
|
||||
-- math.randomseed should be removed
|
||||
assert(math.randomseed == nil, 'randomseed should be removed')
|
||||
|
||||
-- math.random should work
|
||||
local r1 = math.random()
|
||||
assert(r1 >= 0 and r1 < 1, 'random() should return [0,1)')
|
||||
|
||||
local r2 = math.random(10)
|
||||
assert(r2 >= 1 and r2 <= 10, 'random(n) should return [1,n]')
|
||||
|
||||
local r3 = math.random(5, 15)
|
||||
assert(r3 >= 5 and r3 <= 15, 'random(m,n) should return [m,n]')
|
||||
)";
|
||||
|
||||
if (!sandbox.LoadString(script, "math_random_test")) {
|
||||
error_msg = "Math.random test failed: " + sandbox.GetLastError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// MAIN
|
||||
//=============================================================================
|
||||
@@ -965,6 +1165,17 @@ int main(int argc, char* argv[]) {
|
||||
harness.AddTest("ClearAppTimersCleanup", Test_ClearAppTimersCleanup);
|
||||
harness.AddTest("MinIntervalEnforced", Test_MinIntervalEnforced);
|
||||
|
||||
// Milestone 6: JSON & Crypto APIs
|
||||
harness.AddTest("JsonDecodeValid", Test_JsonDecodeValid);
|
||||
harness.AddTest("JsonDecodeRejectsDeep", Test_JsonDecodeRejectsDeep);
|
||||
harness.AddTest("JsonEncodeValid", Test_JsonEncodeValid);
|
||||
harness.AddTest("JsonEncodeDetectsCycles", Test_JsonEncodeDetectsCycles);
|
||||
harness.AddTest("JsonRejectsTooLarge", Test_JsonRejectsTooLarge);
|
||||
harness.AddTest("CryptoRandomBytes", Test_CryptoRandomBytes);
|
||||
harness.AddTest("CryptoHashSHA256", Test_CryptoHashSHA256);
|
||||
harness.AddTest("CryptoHMAC", Test_CryptoHMAC);
|
||||
harness.AddTest("SecureMathRandom", Test_SecureMathRandom);
|
||||
|
||||
// Run tests
|
||||
auto results = harness.Run(filter);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user