# Milestone 2: Permission System **Status**: ✅ Complete **Goal**: Gate API access based on app permissions. --- ## Overview This milestone implements the permission system that controls which APIs an app can access. It defines permission categories (Normal, Dangerous, Signature) and provides mechanisms for checking permissions at runtime. ### Key Deliverables 1. **PermissionGate class** - Permission checking and enforcement 2. **Permission categories** - Normal/Dangerous/Signature classification 3. **User gesture tracking** - Detect recent user interactions 4. **Manifest parsing** - Read app permissions from manifest.json --- ## File Structure ``` src/main/cpp/sandbox/ ├── lua_sandbox.h # (existing) ├── lua_sandbox.cpp # (existing) ├── permission_gate.h # NEW - Permission checking └── permission_gate.cpp # NEW - Implementation sandbox-test/ ├── scripts/ │ ├── test_permission_normal.lua # NEW │ ├── test_permission_dangerous.lua # NEW │ └── test_permission_signature.lua # NEW └── src/ └── main.cpp # Add new tests ``` --- ## Implementation Details ### 1. Permission Categories ```cpp // permission_gate.h enum class PermissionCategory { Normal, // Auto-granted (e.g., vibrate, internet) Dangerous, // Requires user consent (e.g., camera, location) Signature // System apps only (e.g., system settings, install apps) }; ``` ### 2. Permission Definitions ```cpp // permission_gate.cpp static const std::unordered_map PERMISSIONS = { // Normal permissions (auto-granted) {"internet", {PermissionCategory::Normal, "Access the internet"}}, {"vibrate", {PermissionCategory::Normal, "Vibrate the device"}}, {"wake_lock", {PermissionCategory::Normal, "Keep device awake"}}, {"notifications", {PermissionCategory::Normal, "Show notifications"}}, // Dangerous permissions (require user consent) {"camera", {PermissionCategory::Dangerous, "Access the camera"}}, {"microphone", {PermissionCategory::Dangerous, "Record audio"}}, {"location.fine", {PermissionCategory::Dangerous, "Access precise location"}}, {"location.coarse", {PermissionCategory::Dangerous, "Access approximate location"}}, {"contacts.read", {PermissionCategory::Dangerous, "Read contacts"}}, {"contacts.write", {PermissionCategory::Dangerous, "Modify contacts"}}, {"storage.external", {PermissionCategory::Dangerous, "Access external storage"}}, {"sensors.motion", {PermissionCategory::Dangerous, "Access motion sensors"}}, {"bluetooth", {PermissionCategory::Dangerous, "Use Bluetooth"}}, {"calendar.read", {PermissionCategory::Dangerous, "Read calendar"}}, {"calendar.write", {PermissionCategory::Dangerous, "Modify calendar"}}, // Signature permissions (system apps only) {"system.settings", {PermissionCategory::Signature, "Modify system settings"}}, {"system.install", {PermissionCategory::Signature, "Install apps"}}, {"system.uninstall", {PermissionCategory::Signature, "Uninstall apps"}}, {"system.admin", {PermissionCategory::Signature, "Device administrator"}}, }; ``` ### 3. PermissionGate Class ```cpp // permission_gate.h #pragma once #include #include #include #include struct lua_State; namespace mosis { struct SandboxContext; // Forward declaration enum class PermissionCategory { Normal, Dangerous, Signature }; 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); // 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& GetDeclaredPermissions() const; // Get all granted permissions std::vector GetGrantedPermissions() const; private: const SandboxContext& m_context; std::unordered_set 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; }; // Lua helper - throws error if permission denied int RequirePermission(lua_State* L, const char* permission); } // namespace mosis ``` ### 4. Implementation ```cpp // permission_gate.cpp #include "permission_gate.h" #include "lua_sandbox.h" #include #include namespace mosis { // Permission database static const std::unordered_map PERMISSIONS = { // Normal {"internet", {PermissionCategory::Normal, "Access the internet"}}, {"vibrate", {PermissionCategory::Normal, "Vibrate the device"}}, {"wake_lock", {PermissionCategory::Normal, "Keep device awake"}}, {"notifications", {PermissionCategory::Normal, "Show notifications"}}, // Dangerous {"camera", {PermissionCategory::Dangerous, "Access the camera"}}, {"microphone", {PermissionCategory::Dangerous, "Record audio"}}, {"location.fine", {PermissionCategory::Dangerous, "Access precise location"}}, {"location.coarse", {PermissionCategory::Dangerous, "Access approximate location"}}, {"contacts.read", {PermissionCategory::Dangerous, "Read contacts"}}, {"contacts.write", {PermissionCategory::Dangerous, "Modify contacts"}}, {"storage.external", {PermissionCategory::Dangerous, "Access external storage"}}, {"sensors.motion", {PermissionCategory::Dangerous, "Access motion sensors"}}, {"bluetooth", {PermissionCategory::Dangerous, "Use Bluetooth"}}, // Signature {"system.settings", {PermissionCategory::Signature, "Modify system settings"}}, {"system.install", {PermissionCategory::Signature, "Install apps"}}, {"system.admin", {PermissionCategory::Signature, "Device administrator"}}, }; PermissionGate::PermissionGate(const SandboxContext& context) : m_context(context) , m_last_gesture(std::chrono::steady_clock::time_point::min()) { } PermissionCategory PermissionGate::GetCategory(const std::string& permission) { auto it = PERMISSIONS.find(permission); if (it != PERMISSIONS.end()) { return it->second.category; } // Unknown permissions default to Dangerous return PermissionCategory::Dangerous; } bool PermissionGate::HasPermission(const std::string& permission) const { auto category = GetCategory(permission); switch (category) { case PermissionCategory::Normal: return CheckNormalPermission(permission); case PermissionCategory::Dangerous: return CheckDangerousPermission(permission); case PermissionCategory::Signature: return CheckSignaturePermission(permission); } return false; } bool PermissionGate::Check(lua_State* L, const std::string& permission) { if (!HasPermission(permission)) { luaL_error(L, "permission denied: %s", permission.c_str()); return false; } return true; } bool PermissionGate::CheckNormalPermission(const std::string& permission) const { // Normal permissions are auto-granted if declared in manifest const auto& declared = m_context.permissions; return std::find(declared.begin(), declared.end(), permission) != declared.end(); } bool PermissionGate::CheckDangerousPermission(const std::string& permission) const { // Must be declared in manifest const auto& declared = m_context.permissions; if (std::find(declared.begin(), declared.end(), permission) == declared.end()) { return false; } // Must be granted at runtime (or be a system app) if (m_context.is_system_app) { return true; } return m_runtime_grants.count(permission) > 0; } bool PermissionGate::CheckSignaturePermission(const std::string& permission) const { // Only system apps get signature permissions if (!m_context.is_system_app) { return false; } // Must still be declared const auto& declared = m_context.permissions; return std::find(declared.begin(), declared.end(), permission) != declared.end(); } void PermissionGate::RecordUserGesture() { m_last_gesture = std::chrono::steady_clock::now(); } bool PermissionGate::HasRecentUserGesture(int ms) const { auto now = std::chrono::steady_clock::now(); auto elapsed = std::chrono::duration_cast(now - m_last_gesture); return elapsed.count() < ms; } void PermissionGate::GrantPermission(const std::string& permission) { m_runtime_grants.insert(permission); } void PermissionGate::RevokePermission(const std::string& permission) { m_runtime_grants.erase(permission); } const std::vector& PermissionGate::GetDeclaredPermissions() const { return m_context.permissions; } std::vector PermissionGate::GetGrantedPermissions() const { std::vector granted; for (const auto& perm : m_context.permissions) { if (HasPermission(perm)) { granted.push_back(perm); } } return granted; } int RequirePermission(lua_State* L, const char* permission) { // Get sandbox from registry lua_getfield(L, LUA_REGISTRYINDEX, "__mosis_sandbox"); auto* sandbox = static_cast(lua_touserdata(L, -1)); lua_pop(L, 1); if (!sandbox) { return luaL_error(L, "sandbox not initialized"); } // TODO: Get PermissionGate from sandbox once integrated // For now, this is a placeholder return 0; } } // namespace mosis ``` --- ## Test Cases ### Test 1: Normal Permission Auto-Granted **Script**: `scripts/test_permission_normal.lua` ```lua -- Test that normal permissions are auto-granted when declared -- This is called from C++ which sets up context with "internet" permission -- If we get here, permission check passed print("PASS: Normal permission granted") ``` **C++ Test**: ```cpp bool Test_NormalPermissionAutoGranted(std::string& error_msg) { SandboxContext ctx = TestContext(); ctx.permissions = {"internet"}; // Declare normal permission LuaSandbox sandbox(ctx); PermissionGate gate(ctx); // Normal permissions should be auto-granted EXPECT_TRUE(gate.HasPermission("internet")); return true; } ``` ### Test 2: Dangerous Permission Requires Grant **C++ Test**: ```cpp bool Test_DangerousPermissionRequiresGrant(std::string& error_msg) { SandboxContext ctx = TestContext(); ctx.permissions = {"camera"}; // Declare dangerous permission PermissionGate gate(ctx); // Not granted yet EXPECT_FALSE(gate.HasPermission("camera")); // Grant at runtime gate.GrantPermission("camera"); // Now should have it EXPECT_TRUE(gate.HasPermission("camera")); // Revoke gate.RevokePermission("camera"); EXPECT_FALSE(gate.HasPermission("camera")); return true; } ``` ### Test 3: Signature Permission System Only **C++ Test**: ```cpp bool Test_SignaturePermissionSystemOnly(std::string& error_msg) { // Non-system app SandboxContext ctx = TestContext(); ctx.permissions = {"system.settings"}; ctx.is_system_app = false; PermissionGate gate(ctx); EXPECT_FALSE(gate.HasPermission("system.settings")); // System app SandboxContext sys_ctx = TestContext(); sys_ctx.permissions = {"system.settings"}; sys_ctx.is_system_app = true; PermissionGate sys_gate(sys_ctx); EXPECT_TRUE(sys_gate.HasPermission("system.settings")); return true; } ``` ### Test 4: User Gesture Tracking **C++ Test**: ```cpp bool Test_UserGestureRequired(std::string& error_msg) { SandboxContext ctx = TestContext(); PermissionGate gate(ctx); // No recent gesture EXPECT_FALSE(gate.HasRecentUserGesture(5000)); // Record gesture gate.RecordUserGesture(); // Should have recent gesture EXPECT_TRUE(gate.HasRecentUserGesture(5000)); return true; } ``` ### Test 5: Undeclared Permission Denied **C++ Test**: ```cpp bool Test_UndeclaredPermissionDenied(std::string& error_msg) { SandboxContext ctx = TestContext(); ctx.permissions = {}; // No permissions declared PermissionGate gate(ctx); // Even normal permissions need to be declared EXPECT_FALSE(gate.HasPermission("internet")); return true; } ``` --- ## Build & Test Commands ### Build ```bash cd sandbox-test cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake cmake --build build --config Debug ``` ### Run All Tests ```bash ./build/Debug/sandbox-test.exe ``` ### Run Permission Tests Only ```bash ./build/Debug/sandbox-test.exe --test Permission ``` --- ## Acceptance Criteria All tests must pass: - [x] `Test_NormalPermissionAutoGranted` - Normal perms auto-granted when declared - [x] `Test_DangerousPermissionRequiresGrant` - Dangerous perms need runtime grant - [x] `Test_SignaturePermissionSystemOnly` - Signature perms only for system apps - [x] `Test_UserGestureTracking` - User gesture tracking works - [x] `Test_UndeclaredPermissionDenied` - Undeclared perms always denied - [x] `Test_SystemAppGetsDangerousAuto` - System apps get dangerous perms auto - [x] `Test_PermissionCategoryCheck` - Permission categories are correct --- ## Integration Notes After Milestone 2: 1. Add `PermissionGate` to `LuaSandbox` class 2. Call `RequirePermission()` before sensitive operations 3. Wire up user gesture recording from touch events --- ## Next Steps After Milestone 2 passes: 1. Milestone 3: Audit Logging & Rate Limiting 2. Use permission gate in all subsequent API implementations