Files
MosisService/docs/SANDBOX_MILESTONE_2.md

489 lines
14 KiB
Markdown

# 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<std::string, PermissionInfo> 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 <string>
#include <vector>
#include <unordered_set>
#include <chrono>
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<std::string>& GetDeclaredPermissions() const;
// Get all granted permissions
std::vector<std::string> GetGrantedPermissions() 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;
};
// 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 <lua.hpp>
#include <algorithm>
namespace mosis {
// Permission database
static const std::unordered_map<std::string, PermissionInfo> 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<std::chrono::milliseconds>(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<std::string>& PermissionGate::GetDeclaredPermissions() const {
return m_context.permissions;
}
std::vector<std::string> PermissionGate::GetGrantedPermissions() const {
std::vector<std::string> 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<LuaSandbox*>(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