# Milestone 3: Audit Logging & Rate Limiting

**Status**: Complete ✓
**Goal**: Track security events and prevent API abuse.

---

## Overview

This milestone adds security event logging and rate limiting to prevent abuse. The audit log tracks permission checks, sandbox violations, and resource usage. The rate limiter uses a token bucket algorithm to limit API call frequency.

### Key Deliverables

1. **AuditLog class** - Security event logging with ring buffer
2. **RateLimiter class** - Token bucket rate limiting
3. **Thread safety** - Concurrent access support

---

## File Structure

```
src/main/cpp/sandbox/
├── lua_sandbox.h           # (existing)
├── lua_sandbox.cpp         # (existing)
├── permission_gate.h       # (existing)
├── permission_gate.cpp     # (existing)
├── audit_log.h             # NEW - Audit logging
├── audit_log.cpp           # NEW - Implementation
├── rate_limiter.h          # NEW - Rate limiting
└── rate_limiter.cpp        # NEW - Implementation
```

---

## Implementation Details

### 1. AuditEvent Enum

```cpp
// audit_log.h
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
};
```

### 2. AuditLog Class

```cpp
// audit_log.h
#pragma once

#include <string>
#include <vector>
#include <mutex>
#include <chrono>

namespace mosis {

enum class AuditEvent { /* ... */ };

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
    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 CountEvents(AuditEvent event, const std::string& app_id = "") const;

    // Clear
    void Clear();

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;
};

// Global audit log (singleton pattern)
AuditLog& GetAuditLog();

} // namespace mosis
```

### 3. RateLimiter Class (Token Bucket)

```cpp
// rate_limiter.h
#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
    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 current token count
    double GetTokens(const std::string& app_id, const std::string& operation) const;

    // Reset an app's tokens (e.g., on app restart)
    void ResetApp(const std::string& app_id);

private:
    struct Bucket {
        double tokens;
        std::chrono::steady_clock::time_point last_refill;
    };

    void Refill(Bucket& bucket, const RateLimitConfig& config) const;
    Bucket& GetBucket(const std::string& app_id, const std::string& operation);

    mutable std::mutex m_mutex;
    std::unordered_map<std::string, RateLimitConfig> m_configs;
    std::unordered_map<std::string, Bucket> m_buckets;  // Key: app_id:operation
};

// Global rate limiter
RateLimiter& GetRateLimiter();

} // namespace mosis
```

### 4. Default Rate Limits

```cpp
// rate_limiter.cpp
RateLimiter::RateLimiter() {
    // Network
    SetLimit("network.request", {10.0, 100.0});   // 10/sec, burst 100
    SetLimit("network.websocket", {2.0, 10.0});   // 2/sec, burst 10

    // Storage
    SetLimit("storage.read", {100.0, 500.0});     // 100/sec, burst 500
    SetLimit("storage.write", {20.0, 100.0});     // 20/sec, burst 100
    SetLimit("database.query", {50.0, 200.0});    // 50/sec, burst 200

    // Hardware
    SetLimit("camera.capture", {30.0, 30.0});     // 30 fps max
    SetLimit("microphone.record", {1.0, 1.0});    // 1 session at a time
    SetLimit("location.request", {1.0, 5.0});     // 1/sec, burst 5

    // Timers
    SetLimit("timer.create", {10.0, 100.0});      // 10/sec, burst 100
}
```

---

## Test Cases

### Test 1: Audit Log Basic

```cpp
bool Test_AuditLogBasic(std::string& error_msg) {
    mosis::AuditLog log(1000);

    log.Log(mosis::AuditEvent::AppStart, "test.app", "App started");
    log.Log(mosis::AuditEvent::PermissionCheck, "test.app", "camera", true);
    log.Log(mosis::AuditEvent::PermissionDenied, "test.app", "microphone", false);

    auto entries = log.GetEntries(10);
    EXPECT_TRUE(entries.size() == 3);

    auto app_entries = log.GetEntriesForApp("test.app", 10);
    EXPECT_TRUE(app_entries.size() == 3);

    return true;
}
```

### Test 2: Audit Log Ring Buffer

```cpp
bool Test_AuditLogRingBuffer(std::string& error_msg) {
    mosis::AuditLog log(100);  // Small buffer

    // Log more than capacity
    for (int i = 0; i < 200; i++) {
        log.Log(mosis::AuditEvent::Custom, "test.app", std::to_string(i));
    }

    // Should only have latest 100
    auto entries = log.GetEntries(200);
    EXPECT_TRUE(entries.size() == 100);

    // Total logged should be 200
    EXPECT_TRUE(log.GetTotalEntries() == 200);

    return true;
}
```

### Test 3: Rate Limiter Basic

```cpp
bool Test_RateLimiterBasic(std::string& error_msg) {
    mosis::RateLimiter limiter;

    // Should succeed initially (has tokens)
    EXPECT_TRUE(limiter.Check("test.app", "network.request"));

    return true;
}
```

### Test 4: Rate Limiter Exhaustion

```cpp
bool Test_RateLimiterExhaustion(std::string& error_msg) {
    mosis::RateLimiter limiter;
    limiter.SetLimit("test.op", {0.0, 5.0});  // 5 tokens, no refill

    // Use all tokens
    for (int i = 0; i < 5; i++) {
        EXPECT_TRUE(limiter.Check("test.app", "test.op"));
    }

    // Should be denied now
    EXPECT_FALSE(limiter.Check("test.app", "test.op"));

    return true;
}
```

### Test 5: Rate Limiter Refill

```cpp
bool Test_RateLimiterRefill(std::string& error_msg) {
    mosis::RateLimiter limiter;
    limiter.SetLimit("test.op", {1000.0, 1.0});  // 1000/sec, max 1

    // Use the token
    EXPECT_TRUE(limiter.Check("test.app", "test.op"));
    EXPECT_FALSE(limiter.Check("test.app", "test.op"));

    // Wait a bit for refill (1ms = 1 token at 1000/sec)
    std::this_thread::sleep_for(std::chrono::milliseconds(2));

    // Should have token again
    EXPECT_TRUE(limiter.Check("test.app", "test.op"));

    return true;
}
```

### Test 6: App Isolation

```cpp
bool Test_RateLimiterAppIsolation(std::string& error_msg) {
    mosis::RateLimiter limiter;
    limiter.SetLimit("test.op", {0.0, 1.0});  // 1 token, no refill

    // App 1 uses its token
    EXPECT_TRUE(limiter.Check("app1", "test.op"));
    EXPECT_FALSE(limiter.Check("app1", "test.op"));

    // App 2 should still have its token
    EXPECT_TRUE(limiter.Check("app2", "test.op"));

    return true;
}
```

---

## Acceptance Criteria

All tests must pass:

- [x] `Test_AuditLogBasic` - Logging and retrieval work
- [x] `Test_AuditLogRingBuffer` - Ring buffer wraps correctly
- [x] `Test_AuditLogThreadSafe` - Thread safety verified
- [x] `Test_RateLimiterBasic` - Basic rate limiting works
- [x] `Test_RateLimiterExhaustion` - Tokens exhaust correctly
- [x] `Test_RateLimiterRefill` - Tokens refill over time
- [x] `Test_RateLimiterAppIsolation` - Apps have separate buckets
- [x] `Test_RateLimiterReset` - App reset clears buckets
- [x] `Test_RateLimiterNoConfig` - Unconfigured ops allowed

---

## Next Steps

After Milestone 3 passes:
1. Integrate AuditLog into LuaSandbox and PermissionGate
2. Integrate RateLimiter into API implementations
3. Milestone 4: Safe Path & Require
