extract shared mosis-core library from sandbox APIs

Create core/ directory with platform-agnostic sandbox components:
- Timer manager, JSON API, Crypto API, Virtual FS
- Lua sandbox, Permission gate, Audit log, Rate limiter
- Platform abstraction interfaces (IAssetInterface, IFilesystemInterface)
- Platform-agnostic logger with Android/Desktop implementations

Update designer to link against mosis-core library instead of
including sandbox sources directly.

This is the foundation for unifying the Android service and
desktop designer to share the same codebase.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-19 11:57:10 +01:00
parent f41eda6f62
commit 33841516f1
34 changed files with 7134 additions and 13 deletions

View File

@@ -0,0 +1,23 @@
// app_api.h - Lua API bindings for app management
// Milestone 10: Device-Side App Management
#pragma once
#include <string>
struct lua_State;
namespace mosis {
class AppManager;
class UpdateService;
// Register Lua APIs for app management
// - mosis.apps.* - System apps only (App Store, Settings)
// - mosis.app.* - All apps (info about current app)
void RegisterAppAPIs(lua_State* L,
AppManager* app_manager,
UpdateService* update_service,
const std::string& current_app_id,
bool is_system_app);
} // namespace mosis

View File

@@ -0,0 +1,167 @@
// app_manager.h - App installation and management
// Milestone 10: Device-Side App Management
#pragma once
#include <string>
#include <vector>
#include <map>
#include <optional>
#include <functional>
#include <mutex>
#include <chrono>
#include <cstdint>
namespace mosis {
// Forward declarations
class LuaSandboxManager;
// Information about an installed app
struct InstalledApp {
std::string package_id;
std::string name;
std::string version_name;
int version_code = 0;
std::string install_path;
std::vector<std::string> permissions;
std::chrono::system_clock::time_point installed_at;
std::chrono::system_clock::time_point updated_at;
int64_t package_size = 0;
int64_t data_size = 0;
bool is_system_app = false;
std::string entry_point;
std::string icon_path;
std::string developer_name;
};
// Progress stages during installation
struct InstallProgress {
enum class Stage {
Downloading,
Verifying,
Extracting,
Registering,
Complete,
Failed
};
Stage stage = Stage::Downloading;
float progress = 0.0f; // 0.0 - 1.0
std::string error;
static const char* StageName(Stage s) {
switch (s) {
case Stage::Downloading: return "downloading";
case Stage::Verifying: return "verifying";
case Stage::Extracting: return "extracting";
case Stage::Registering: return "registering";
case Stage::Complete: return "complete";
case Stage::Failed: return "failed";
default: return "unknown";
}
}
};
using ProgressCallback = std::function<void(const InstallProgress&)>;
// Manifest parsed from package
struct AppManifest {
std::string id;
std::string name;
std::string version;
int version_code = 0;
std::string entry;
std::string icon;
std::string description;
std::string developer_name;
std::string developer_email;
std::vector<std::string> permissions;
int min_api_version = 1;
};
class AppManager {
public:
explicit AppManager(const std::string& data_root);
~AppManager();
// Prevent copying
AppManager(const AppManager&) = delete;
AppManager& operator=(const AppManager&) = delete;
// Installation from URL
bool Install(const std::string& package_url,
const std::string& signature,
ProgressCallback callback);
// Installation from local file
bool InstallFromFile(const std::string& package_path,
ProgressCallback callback);
// Uninstallation
bool Uninstall(const std::string& package_id, bool keep_data = false);
// Updates
bool Update(const std::string& package_id,
const std::string& package_url,
const std::string& signature,
ProgressCallback callback);
// Query installed apps
std::vector<InstalledApp> GetInstalledApps() const;
std::optional<InstalledApp> GetApp(const std::string& package_id) const;
bool IsInstalled(const std::string& package_id) const;
// Data management
int64_t GetAppDataSize(const std::string& package_id) const;
bool ClearAppData(const std::string& package_id);
bool ClearAppCache(const std::string& package_id);
bool BackupAppData(const std::string& package_id);
bool RestoreAppData(const std::string& package_id);
// App launching
bool LaunchApp(const std::string& package_id);
bool StopApp(const std::string& package_id);
bool IsAppRunning(const std::string& package_id) const;
// Integration with sandbox manager
void SetSandboxManager(LuaSandboxManager* manager);
// Get paths
std::string GetDataRoot() const { return m_data_root; }
std::string GetAppPath(const std::string& package_id) const;
std::string GetAppDataPath(const std::string& package_id) const;
std::string GetAppCachePath(const std::string& package_id) const;
// System apps registration
void RegisterSystemApp(const InstalledApp& app);
private:
// Package verification
bool VerifyPackage(const std::string& path);
bool VerifySignature(const std::string& path, const std::string& signature);
// Package operations
std::optional<AppManifest> ExtractManifest(const std::string& package_path);
bool ExtractPackage(const std::string& package_path, const std::string& dest_path);
// Download helper
bool DownloadFile(const std::string& url, const std::string& dest_path,
std::function<void(float)> progress_callback);
// Registry persistence
void LoadInstalledApps();
void SaveInstalledApps();
// Directory size calculation
int64_t CalculateDirectorySize(const std::string& path) const;
// Generate unique ID
std::string GenerateUUID() const;
std::string m_data_root;
LuaSandboxManager* m_sandbox_manager = nullptr;
mutable std::mutex m_mutex;
std::map<std::string, InstalledApp> m_installed_apps;
};
} // namespace mosis

View File

@@ -0,0 +1,57 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
namespace mosis {
/**
* Platform-agnostic interface for loading assets.
* Android implements this using AAssetManager.
* Desktop implements this using filesystem operations.
*/
class IAssetInterface {
public:
virtual ~IAssetInterface() = default;
/**
* Read entire file contents as bytes.
* @param path Relative path to asset (e.g., "apps/home/home.rml")
* @return File contents, or empty vector if not found
*/
virtual std::vector<uint8_t> ReadFile(const std::string& path) = 0;
/**
* Read entire file contents as string.
* @param path Relative path to asset
* @return File contents, or empty string if not found
*/
virtual std::string ReadFileString(const std::string& path) = 0;
/**
* Check if an asset exists.
* @param path Relative path to asset
* @return true if asset exists
*/
virtual bool Exists(const std::string& path) = 0;
/**
* List files in a directory.
* @param path Relative path to directory
* @return List of filenames (not full paths)
*/
virtual std::vector<std::string> ListDirectory(const std::string& path) = 0;
/**
* Get the absolute path for an asset (if applicable).
* On Android this may return empty as assets are in APK.
* @param path Relative path to asset
* @return Absolute path or empty string
*/
virtual std::string GetAbsolutePath(const std::string& path) = 0;
};
using AssetInterfacePtr = std::shared_ptr<IAssetInterface>;
} // namespace mosis

View File

@@ -0,0 +1,98 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <functional>
namespace mosis {
/**
* Platform-agnostic interface for filesystem operations.
* Used for app data storage, not assets.
*/
class IFilesystemInterface {
public:
virtual ~IFilesystemInterface() = default;
/**
* Get the base data directory for apps.
* Android: /data/data/com.omixlab.mosis/files/
* Desktop: ./data/ or configurable
*/
virtual std::string GetDataRoot() = 0;
/**
* Get the apps installation directory.
* Contains installed app packages.
*/
virtual std::string GetAppsDirectory() = 0;
/**
* Get app-specific data directory.
* @param app_id Application ID (e.g., "com.example.app")
*/
virtual std::string GetAppDataDirectory(const std::string& app_id) = 0;
/**
* Get app-specific cache directory.
*/
virtual std::string GetAppCacheDirectory(const std::string& app_id) = 0;
/**
* Create directory if it doesn't exist.
* @return true on success
*/
virtual bool CreateDirectory(const std::string& path) = 0;
/**
* Check if path exists.
*/
virtual bool Exists(const std::string& path) = 0;
/**
* Check if path is a directory.
*/
virtual bool IsDirectory(const std::string& path) = 0;
/**
* Remove file or directory.
* @param recursive If true, remove directory contents
*/
virtual bool Remove(const std::string& path, bool recursive = false) = 0;
/**
* Read file contents.
*/
virtual std::vector<uint8_t> ReadFile(const std::string& path) = 0;
/**
* Write file contents.
*/
virtual bool WriteFile(const std::string& path, const std::vector<uint8_t>& data) = 0;
/**
* List directory contents.
*/
virtual std::vector<std::string> ListDirectory(const std::string& path) = 0;
/**
* Get file size.
* @return Size in bytes, or -1 if not found
*/
virtual int64_t GetFileSize(const std::string& path) = 0;
/**
* Copy file.
*/
virtual bool CopyFile(const std::string& src, const std::string& dst) = 0;
/**
* Move/rename file.
*/
virtual bool MoveFile(const std::string& src, const std::string& dst) = 0;
};
using FilesystemInterfacePtr = std::shared_ptr<IFilesystemInterface>;
} // namespace mosis

View File

@@ -0,0 +1,94 @@
#pragma once
#include <string>
#include <vector>
#include <mutex>
#include <chrono>
namespace mosis {
enum class AuditEvent {
// Lifecycle
AppStart,
AppStop,
// Permissions
PermissionCheck,
PermissionGranted,
PermissionDenied,
// Network
NetworkRequest,
NetworkBlocked,
// Storage
FileAccess,
FileBlocked,
DatabaseAccess,
// Hardware
CameraAccess,
MicrophoneAccess,
LocationAccess,
// Security
SandboxViolation,
ResourceLimitHit,
RateLimitHit,
// Other
Custom
};
struct AuditEntry {
std::chrono::system_clock::time_point timestamp;
AuditEvent event;
std::string app_id;
std::string details;
bool success;
};
class AuditLog {
public:
explicit AuditLog(size_t max_entries = 10000);
// Log an event
void Log(AuditEvent event, const std::string& app_id,
const std::string& details = "", bool success = true);
// Query entries (returns most recent first)
std::vector<AuditEntry> GetEntries(size_t count = 100) const;
std::vector<AuditEntry> GetEntriesForApp(const std::string& app_id,
size_t count = 100) const;
std::vector<AuditEntry> GetEntriesByEvent(AuditEvent event,
size_t count = 100) const;
// Statistics
size_t GetTotalEntries() const;
size_t GetStoredEntries() const;
size_t CountEvents(AuditEvent event, const std::string& app_id = "") const;
// Clear all entries
void Clear();
// Convert event to string for logging
static const char* EventToString(AuditEvent event);
private:
mutable std::mutex m_mutex;
std::vector<AuditEntry> m_entries;
size_t m_max_entries;
size_t m_write_index = 0;
size_t m_total_logged = 0;
bool m_wrapped = false;
};
// Global audit log (singleton)
AuditLog& GetAuditLog();
} // namespace mosis
// Convenience alias
using AuditLog = mosis::AuditLog;
using AuditEvent = mosis::AuditEvent;
using AuditEntry = mosis::AuditEntry;

View File

@@ -0,0 +1,52 @@
#pragma once
#include <string>
#include <cstdint>
#include <random>
#include <mutex>
struct lua_State;
namespace mosis {
// Per-app cryptographically secure RNG
class SecureRandom {
public:
SecureRandom();
// Get random bytes as binary string
std::string GetBytes(size_t count);
// Get random integer in range [min, max]
int64_t GetInt(int64_t min, int64_t max);
// Get random double in range [0.0, 1.0)
double GetDouble();
private:
std::random_device m_rd;
std::mt19937_64 m_gen;
std::mutex m_mutex;
};
// Hash algorithms supported
enum class HashAlgorithm {
SHA256,
SHA512,
SHA1,
MD5
};
// Compute hash of data
std::string ComputeHash(HashAlgorithm algo, const std::string& data);
// Compute HMAC of data with key
std::string ComputeHMAC(HashAlgorithm algo, const std::string& key, const std::string& data);
// Register crypto.* APIs as globals
void RegisterCryptoAPI(lua_State* L);
// Register secure math.random replacement (removes math.randomseed)
void RegisterSecureMathRandom(lua_State* L, SecureRandom* rng);
} // namespace mosis

View File

@@ -0,0 +1,88 @@
#pragma once
#include <string>
#include <vector>
#include <variant>
#include <optional>
#include <memory>
#include <unordered_map>
struct sqlite3;
struct lua_State;
namespace mosis {
// SQL value types
using SqlValue = std::variant<std::nullptr_t, int64_t, double, std::string, std::vector<uint8_t>>;
using SqlRow = std::vector<SqlValue>;
using SqlResult = std::vector<SqlRow>;
struct DatabaseLimits {
size_t max_database_size = 50 * 1024 * 1024; // 50 MB per database
int max_databases_per_app = 5; // Max open databases
int max_query_time_ms = 5000; // 5 second query timeout
int max_result_rows = 10000; // Max rows returned
};
class DatabaseHandle;
class DatabaseManager {
public:
DatabaseManager(const std::string& app_id,
const std::string& app_root,
const DatabaseLimits& limits = DatabaseLimits{});
~DatabaseManager();
// Database operations
std::shared_ptr<DatabaseHandle> Open(const std::string& name, std::string& error);
void CloseAll();
// Stats
size_t GetOpenDatabaseCount() const;
private:
std::string m_app_id;
std::string m_app_root;
DatabaseLimits m_limits;
std::unordered_map<std::string, std::shared_ptr<DatabaseHandle>> m_databases;
std::string ResolvePath(const std::string& name);
bool ValidateName(const std::string& name, std::string& error);
};
class DatabaseHandle {
public:
DatabaseHandle(sqlite3* db, const std::string& path, const DatabaseLimits& limits);
~DatabaseHandle();
// Execute (INSERT, UPDATE, DELETE, CREATE, etc.)
bool Execute(const std::string& sql, const std::vector<SqlValue>& params, std::string& error);
// Query (SELECT)
std::optional<SqlResult> Query(const std::string& sql, const std::vector<SqlValue>& params,
std::string& error);
// Get last insert rowid
int64_t GetLastInsertRowId() const;
// Get affected rows
int GetChanges() const;
bool IsOpen() const { return m_db != nullptr; }
void Close();
private:
sqlite3* m_db;
std::string m_path;
DatabaseLimits m_limits;
static int Authorizer(void* user_data, int action, const char* arg1,
const char* arg2, const char* arg3, const char* arg4);
bool BindParameters(void* stmt, const std::vector<SqlValue>& params, std::string& error);
};
// Register database.* APIs as globals
void RegisterDatabaseAPI(lua_State* L, DatabaseManager* manager);
} // namespace mosis

View File

@@ -0,0 +1,55 @@
#pragma once
#include <string>
#include <vector>
#include <optional>
#include <cstdint>
namespace mosis {
struct ParsedUrl {
std::string scheme; // "https"
std::string host; // "api.example.com" or "192.0.2.1"
uint16_t port; // 443
std::string path; // "/api/data"
std::string query; // "?key=value"
bool is_ip_address; // true if host is IP literal
};
class HttpValidator {
public:
HttpValidator();
// Set allowed domains (from app manifest)
void SetAllowedDomains(const std::vector<std::string>& domains);
// Clear domain restrictions (for testing)
void ClearDomainRestrictions();
// Validate URL
// Returns parsed URL on success, sets error on failure
std::optional<ParsedUrl> Validate(const std::string& url, std::string& error);
private:
std::vector<std::string> m_allowed_domains;
bool m_domain_restrictions_enabled;
// IP address validation
bool IsIPv4Address(const std::string& host);
bool IsIPv6Address(const std::string& host);
bool IsPrivateIPv4(const std::string& ip);
bool IsPrivateIPv6(const std::string& ip);
bool IsLocalhostIP(const std::string& host);
bool IsMetadataIP(const std::string& host);
bool IsBlockedIP(const std::string& host);
// Domain validation
bool IsDomainAllowed(const std::string& host);
bool IsLocalhostName(const std::string& host);
bool IsMetadataHostname(const std::string& host);
// URL parsing
std::optional<ParsedUrl> ParseUrl(const std::string& url);
};
} // namespace mosis

View File

@@ -0,0 +1,22 @@
#pragma once
#include <string>
#include <cstddef>
struct lua_State;
namespace mosis {
// Configuration limits for JSON operations
struct JsonLimits {
int max_depth = 32; // Maximum nesting depth
size_t max_string_length = 1 * 1024 * 1024; // 1 MB per string
size_t max_output_size = 10 * 1024 * 1024; // 10 MB total output
size_t max_array_size = 100000; // Max elements in array
size_t max_object_size = 10000; // Max keys in object
};
// Register json.encode() and json.decode() as globals
void RegisterJsonAPI(lua_State* L, const JsonLimits& limits = JsonLimits{});
} // namespace mosis

View File

@@ -0,0 +1,101 @@
#pragma once
#include <string>
#include <vector>
#include <cstdint>
// Forward declare lua_State to avoid including lua.h in header
struct lua_State;
struct lua_Debug;
namespace mosis {
// Resource limits for sandbox
struct SandboxLimits {
size_t memory_bytes = 16 * 1024 * 1024; // 16 MB default
size_t max_string_size = 1 * 1024 * 1024; // 1 MB max string
size_t max_table_entries = 100000; // Prevent hash DoS
uint64_t instructions_per_call = 1000000; // ~10ms execution
int stack_depth = 200; // Recursion limit
};
// Context for sandbox (app identity, permissions, etc.)
struct SandboxContext {
std::string app_id;
std::string app_path;
std::vector<std::string> permissions;
bool is_system_app = false;
};
// Isolated Lua execution environment
class LuaSandbox {
public:
explicit LuaSandbox(const SandboxContext& context,
const SandboxLimits& limits = {});
~LuaSandbox();
// Non-copyable, non-movable
LuaSandbox(const LuaSandbox&) = delete;
LuaSandbox& operator=(const LuaSandbox&) = delete;
LuaSandbox(LuaSandbox&&) = delete;
LuaSandbox& operator=(LuaSandbox&&) = delete;
// Load and execute Lua code (text only, bytecode rejected)
bool LoadString(const std::string& code, const std::string& chunk_name = "chunk");
bool LoadFile(const std::string& path);
// State access
lua_State* GetState() const { return m_L; }
const std::string& GetLastError() const { return m_last_error; }
// Resource usage
size_t GetMemoryUsed() const { return m_memory_used; }
uint64_t GetInstructionsUsed() const { return m_instructions_used; }
// Context access
const SandboxContext& GetContext() const { return m_context; }
const SandboxLimits& GetLimits() const { return m_limits; }
const std::string& app_id() const { return m_context.app_id; }
// Reset instruction counter (call before each event handler)
void ResetInstructionCount();
// Check if sandbox is in valid state
bool IsValid() const { return m_L != nullptr; }
private:
// Setup functions
void SetupSandbox();
void RemoveDangerousGlobals();
void ProtectBuiltinTables();
void SetupInstructionHook();
void SetupSafeGlobals();
void SetupSafeRequire();
// Allocator callback (static for C compatibility)
static void* SandboxAlloc(void* ud, void* ptr, size_t osize, size_t nsize);
// Instruction hook callback (static for C compatibility)
static void InstructionHook(lua_State* L, lua_Debug* ar);
// Safe print function
static int SafePrint(lua_State* L);
// Safe require function
static int SafeRequire(lua_State* L);
lua_State* m_L = nullptr;
SandboxContext m_context;
SandboxLimits m_limits;
size_t m_memory_used = 0;
uint64_t m_instructions_used = 0;
std::string m_last_error;
};
} // namespace mosis
// Convenience alias for tests
using SandboxContext = mosis::SandboxContext;
using SandboxLimits = mosis::SandboxLimits;
using LuaSandbox = mosis::LuaSandbox;

View File

@@ -0,0 +1,76 @@
#pragma once
#include <string>
#include <vector>
#include <map>
#include <mutex>
#include <atomic>
#include "http_validator.h"
struct lua_State;
namespace mosis {
struct HttpRequest {
std::string url;
std::string method = "GET";
std::map<std::string, std::string> headers;
std::string body;
int timeout_ms = 30000;
};
struct HttpResponse {
int status_code = 0;
std::map<std::string, std::string> headers;
std::string body;
std::string error;
};
struct NetworkLimits {
size_t max_request_body = 10 * 1024 * 1024; // 10 MB
size_t max_response_body = 50 * 1024 * 1024; // 50 MB
int max_timeout_ms = 60000; // 60 seconds
int max_concurrent_requests = 6;
int default_timeout_ms = 30000;
};
class NetworkManager {
public:
NetworkManager(const std::string& app_id, const NetworkLimits& limits = NetworkLimits{});
~NetworkManager();
// Configure domain restrictions
void SetAllowedDomains(const std::vector<std::string>& domains);
void ClearDomainRestrictions();
// Synchronous request
// In test mode, validates but doesn't actually make network calls
HttpResponse Request(const HttpRequest& request, std::string& error);
// Stats
int GetActiveRequestCount() const;
// Access validator for testing
HttpValidator& GetValidator() { return m_validator; }
const HttpValidator& GetValidator() const { return m_validator; }
// For testing: set mock mode (no actual network calls)
void SetMockMode(bool enabled) { m_mock_mode = enabled; }
bool IsMockMode() const { return m_mock_mode; }
private:
std::string m_app_id;
NetworkLimits m_limits;
HttpValidator m_validator;
std::atomic<int> m_active_requests{0};
std::mutex m_mutex;
bool m_mock_mode = true; // Default to mock mode for tests
// Validate request before sending
bool ValidateRequest(const HttpRequest& request, std::string& error);
};
// Register network.* APIs as globals
void RegisterNetworkAPI(lua_State* L, NetworkManager* manager);
} // namespace mosis

View File

@@ -0,0 +1,52 @@
#pragma once
#include <string>
#include <filesystem>
struct lua_State;
namespace mosis {
class PathSandbox {
public:
explicit PathSandbox(const std::string& app_path);
// Validate a path is within the sandbox
// Returns true if valid, sets out_canonical to the resolved path
bool ValidatePath(const std::string& path, std::string& out_canonical);
// Check if path contains traversal attempts (..)
static bool ContainsTraversal(const std::string& path);
// Check if path is absolute
static bool IsAbsolutePath(const std::string& path);
// Normalize path separators and remove redundant ./ components
static std::string NormalizePath(const std::string& path);
// Validate module name for require() - alphanumeric, underscore, dots only
static bool IsValidModuleName(const std::string& name);
// Convert module name to relative path (e.g., "ui.button" -> "scripts/ui/button.lua")
static std::string ModuleToPath(const std::string& module_name);
// Get the app's base path
const std::string& GetAppPath() const { return m_app_path; }
// Resolve a relative path to full path within sandbox
std::string ResolvePath(const std::string& relative_path);
private:
std::string m_app_path;
};
// Safe require implementation for Lua
// Loads modules only from app_path/scripts/<module>.lua
// Caches modules in registry
int SafeRequire(lua_State* L);
// Register safe require as global "require"
// The PathSandbox pointer is stored in registry for use by SafeRequire
void RegisterSafeRequire(lua_State* L, PathSandbox* sandbox);
} // namespace mosis

View File

@@ -0,0 +1,73 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <chrono>
struct lua_State;
namespace mosis {
struct SandboxContext; // Forward declaration
enum class PermissionCategory {
Normal, // Auto-granted when declared (e.g., internet, vibrate)
Dangerous, // Requires user consent (e.g., camera, location)
Signature // System apps only (e.g., system.settings)
};
struct PermissionInfo {
PermissionCategory category;
std::string description;
};
class PermissionGate {
public:
explicit PermissionGate(const SandboxContext& context);
// Check if app has permission (throws Lua error if not)
bool Check(lua_State* L, const std::string& permission);
// Check without throwing (returns false if denied)
bool HasPermission(const std::string& permission) const;
// Get permission category
static PermissionCategory GetCategory(const std::string& permission);
// Get permission info (returns nullptr if unknown)
static const PermissionInfo* GetPermissionInfo(const std::string& permission);
// User gesture tracking
void RecordUserGesture();
bool HasRecentUserGesture(int ms = 5000) const;
// Runtime permission grant (called after user consent)
void GrantPermission(const std::string& permission);
void RevokePermission(const std::string& permission);
// Get all declared permissions
const std::vector<std::string>& GetDeclaredPermissions() const;
// Get all granted permissions
std::vector<std::string> GetGrantedPermissions() const;
// Check if permission is declared in manifest
bool IsDeclared(const std::string& permission) const;
private:
const SandboxContext& m_context;
std::unordered_set<std::string> m_runtime_grants; // Runtime-granted dangerous perms
std::chrono::steady_clock::time_point m_last_gesture;
bool CheckNormalPermission(const std::string& permission) const;
bool CheckDangerousPermission(const std::string& permission) const;
bool CheckSignaturePermission(const std::string& permission) const;
};
} // namespace mosis
// Convenience alias
using PermissionGate = mosis::PermissionGate;
using PermissionCategory = mosis::PermissionCategory;

View File

@@ -0,0 +1,68 @@
#pragma once
#include <string>
#include <unordered_map>
#include <mutex>
#include <chrono>
namespace mosis {
struct RateLimitConfig {
double tokens_per_second; // Refill rate
double max_tokens; // Bucket capacity
};
class RateLimiter {
public:
// Default limits for common operations
RateLimiter();
// Check if operation is allowed (consumes token if yes)
bool Check(const std::string& app_id, const std::string& operation);
// Check without consuming token
bool CanProceed(const std::string& app_id, const std::string& operation) const;
// Configure limits for an operation
void SetLimit(const std::string& operation, const RateLimitConfig& config);
// Get config for an operation
const RateLimitConfig* GetLimit(const std::string& operation) const;
// Get current token count for app+operation
double GetTokens(const std::string& app_id, const std::string& operation) const;
// Reset all buckets for an app (e.g., on app restart)
void ResetApp(const std::string& app_id);
// Clear all buckets
void ClearAll();
private:
struct Bucket {
double tokens;
std::chrono::steady_clock::time_point last_refill;
};
// Refill bucket based on elapsed time
void Refill(Bucket& bucket, const RateLimitConfig& config) const;
// Get or create bucket for app+operation
Bucket& GetBucket(const std::string& app_id, const std::string& operation);
// Get bucket key
static std::string MakeKey(const std::string& app_id, const std::string& operation);
mutable std::mutex m_mutex;
std::unordered_map<std::string, RateLimitConfig> m_configs;
mutable std::unordered_map<std::string, Bucket> m_buckets;
};
// Global rate limiter (singleton)
RateLimiter& GetRateLimiter();
} // namespace mosis
// Convenience alias
using RateLimiter = mosis::RateLimiter;
using RateLimitConfig = mosis::RateLimitConfig;

View File

@@ -0,0 +1,87 @@
#pragma once
#include <string>
#include <functional>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <chrono>
#include <mutex>
#include <cstdint>
struct lua_State;
namespace mosis {
using TimerId = uint64_t;
using TimePoint = std::chrono::steady_clock::time_point;
using Duration = std::chrono::milliseconds;
struct Timer {
TimerId id;
std::string app_id;
TimePoint fire_time;
Duration interval; // 0 for setTimeout, >0 for setInterval
int callback_ref; // Lua registry reference
lua_State* L; // Lua state that owns the callback
bool cancelled = false;
bool is_interval = false;
};
class TimerManager {
public:
TimerManager();
~TimerManager();
// Non-copyable
TimerManager(const TimerManager&) = delete;
TimerManager& operator=(const TimerManager&) = delete;
// Create timers (returns timer ID, 0 on failure)
TimerId SetTimeout(lua_State* L, const std::string& app_id,
int callback_ref, int delay_ms);
TimerId SetInterval(lua_State* L, const std::string& app_id,
int callback_ref, int interval_ms);
// Cancel timers
bool ClearTimer(const std::string& app_id, TimerId id);
// Cancel all timers for an app (call on app stop)
void ClearAppTimers(const std::string& app_id);
// Process timers (call from main loop)
// Returns number of timers fired
int ProcessTimers();
// Get timer count for an app
size_t GetTimerCount(const std::string& app_id) const;
// Configuration
static constexpr size_t MAX_TIMERS_PER_APP = 100;
static constexpr int MIN_INTERVAL_MS = 10;
static constexpr int MIN_TIMEOUT_MS = 0;
private:
TimerId m_next_id = 1;
// All timers (we use a vector and sort/search as needed)
std::vector<Timer> m_timers;
// Track timer count per app
std::unordered_map<std::string, size_t> m_app_timer_counts;
// Track which timer IDs belong to which app (for fast cancellation)
std::unordered_map<std::string, std::unordered_set<TimerId>> m_app_timer_ids;
mutable std::mutex m_mutex;
void FireTimer(Timer& timer);
void RemoveTimer(TimerId id);
void RescheduleInterval(Timer& timer);
};
// Lua API registration
// Registers: setTimeout, clearTimeout, setInterval, clearInterval
void RegisterTimerAPI(lua_State* L, TimerManager* manager, const std::string& app_id);
} // namespace mosis

View File

@@ -0,0 +1,77 @@
#pragma once
#include <string>
#include <vector>
#include <cstdint>
#include <optional>
#include <functional>
struct lua_State;
namespace mosis {
struct FileStat {
size_t size;
int64_t modified; // Unix timestamp
bool is_dir;
};
struct VirtualFSLimits {
size_t max_quota_bytes = 50 * 1024 * 1024; // 50 MB per app
size_t max_file_size = 10 * 1024 * 1024; // 10 MB per file
int max_path_depth = 10; // Max directory depth
size_t max_path_length = 256; // Max path string length
};
class VirtualFS {
public:
VirtualFS(const std::string& app_id,
const std::string& app_root,
const VirtualFSLimits& limits = VirtualFSLimits{});
~VirtualFS();
// Path operations
bool ValidatePath(const std::string& virtual_path, std::string& error);
std::string ResolvePath(const std::string& virtual_path);
// File operations
std::optional<std::string> Read(const std::string& path, std::string& error);
bool Write(const std::string& path, const std::string& data, std::string& error);
bool Append(const std::string& path, const std::string& data, std::string& error);
bool Delete(const std::string& path, std::string& error);
bool Exists(const std::string& path);
std::optional<std::vector<std::string>> List(const std::string& path, std::string& error);
bool MakeDir(const std::string& path, std::string& error);
std::optional<FileStat> Stat(const std::string& path, std::string& error);
// Quota management
size_t GetUsedBytes() const { return m_used_bytes; }
size_t GetQuotaBytes() const { return m_limits.max_quota_bytes; }
void RecalculateUsage();
// Cleanup
void ClearTemp();
void ClearAll(); // For testing
// Permission check callback (set by sandbox)
std::function<bool(const std::string&)> CheckPermission;
private:
std::string m_app_id;
std::string m_app_root;
VirtualFSLimits m_limits;
size_t m_used_bytes = 0;
bool EnsureParentDir(const std::string& path);
void UpdateUsage(int64_t delta);
bool CheckQuota(size_t additional_bytes, std::string& error);
int GetPathDepth(const std::string& path);
bool IsValidPathChar(char c);
void DeleteDirectoryRecursive(const std::string& path);
size_t CalculateDirectorySize(const std::string& path);
};
// Register fs.* APIs as globals
void RegisterVirtualFS(lua_State* L, VirtualFS* vfs);
} // namespace mosis

View File

@@ -0,0 +1,40 @@
#pragma once
#include <string>
#include <cstdio>
#include <cstdarg>
class Logger
{
public:
static void Log(const std::string& message);
// Printf-style logging
static void LogF(const char* level, const char* fmt, ...) {
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
Log(std::string("[") + level + "] " + buffer);
}
};
// Undefine conflicting syslog macros if present
#ifdef LOG_DEBUG
#undef LOG_DEBUG
#endif
#ifdef LOG_INFO
#undef LOG_INFO
#endif
#ifdef LOG_WARN
#undef LOG_WARN
#endif
#ifdef LOG_ERROR
#undef LOG_ERROR
#endif
// Logging macros for convenience (printf-style)
#define LOG_DEBUG(fmt, ...) Logger::LogF("DEBUG", fmt __VA_OPT__(,) __VA_ARGS__)
#define LOG_INFO(fmt, ...) Logger::LogF("INFO", fmt __VA_OPT__(,) __VA_ARGS__)
#define LOG_WARN(fmt, ...) Logger::LogF("WARN", fmt __VA_OPT__(,) __VA_ARGS__)
#define LOG_ERROR(fmt, ...) Logger::LogF("ERROR", fmt __VA_OPT__(,) __VA_ARGS__)