# Milestone 4: Safe Path & Require

**Status**: Complete ✓
**Goal**: Secure file access within app sandbox.

---

## Overview

This milestone implements path validation to prevent directory traversal attacks and a safe `require()` function that loads Lua modules only from the app's scripts directory.

### Key Deliverables

1. **PathSandbox class** - Path validation and canonicalization
2. **SafeRequire function** - Secure module loader
3. **Module caching** - Registry-based cache for loaded modules

---

## 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             # (existing)
├── audit_log.cpp           # (existing)
├── rate_limiter.h          # (existing)
├── rate_limiter.cpp        # (existing)
├── path_sandbox.h          # NEW - Path validation
└── path_sandbox.cpp        # NEW - Implementation
```

---

## Implementation Details

### 1. PathSandbox Class

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

#include <string>
#include <filesystem>

namespace mosis {

class PathSandbox {
public:
    explicit PathSandbox(const std::string& app_path);

    // Validate a path is within the sandbox
    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);

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

} // namespace mosis
```

### 2. SafeRequire Function

```cpp
// path_sandbox.cpp

// Safe require implementation for Lua
int SafeRequire(lua_State* L);

// Register safe require as global
void RegisterSafeRequire(lua_State* L, PathSandbox* sandbox);
```

### 3. Module Name Validation

Valid module names contain only:
- Alphanumeric characters (a-z, A-Z, 0-9)
- Underscores (_)
- Dots (.) for submodules

Examples:
- `utils` → loads `scripts/utils.lua`
- `ui.button` → loads `scripts/ui/button.lua`

---

## Test Cases

### Test 1: Rejects Directory Traversal

```cpp
bool Test_PathRejectsTraversal(std::string& error_msg) {
    mosis::PathSandbox sandbox("D:/test/app");

    EXPECT_TRUE(mosis::PathSandbox::ContainsTraversal("../etc/passwd"));
    EXPECT_TRUE(mosis::PathSandbox::ContainsTraversal("foo/../../../bar"));
    EXPECT_TRUE(mosis::PathSandbox::ContainsTraversal("..\\windows\\system32"));

    std::string canonical;
    EXPECT_FALSE(sandbox.ValidatePath("../etc/passwd", canonical));
    EXPECT_FALSE(sandbox.ValidatePath("data/../../../etc/passwd", canonical));

    return true;
}
```

### Test 2: Rejects Absolute Paths

```cpp
bool Test_PathRejectsAbsolute(std::string& error_msg) {
    EXPECT_TRUE(mosis::PathSandbox::IsAbsolutePath("/etc/passwd"));
    EXPECT_TRUE(mosis::PathSandbox::IsAbsolutePath("C:\\Windows\\System32"));
    EXPECT_TRUE(mosis::PathSandbox::IsAbsolutePath("D:/test/file.txt"));

    EXPECT_FALSE(mosis::PathSandbox::IsAbsolutePath("scripts/utils.lua"));
    EXPECT_FALSE(mosis::PathSandbox::IsAbsolutePath("./data/file.txt"));

    return true;
}
```

### Test 3: Accepts Valid Paths

```cpp
bool Test_PathAcceptsValid(std::string& error_msg) {
    mosis::PathSandbox sandbox("D:/test/app");

    std::string canonical;
    EXPECT_TRUE(sandbox.ValidatePath("scripts/utils.lua", canonical));
    EXPECT_TRUE(sandbox.ValidatePath("data/config.json", canonical));
    EXPECT_TRUE(sandbox.ValidatePath("./scripts/ui/button.lua", canonical));

    return true;
}
```

### Test 4: Safe Require Loads Modules

```cpp
bool Test_SafeRequireLoads(std::string& error_msg) {
    // Create sandbox with test scripts directory
    SandboxContext ctx = TestContext();
    ctx.app_path = GetScriptsDir();  // Use scripts/ as app path

    LuaSandbox sandbox(ctx);

    // Should be able to require a test module
    std::string script = R"(
        local m = require("test_module")
        return m.value == 42
    )";

    EXPECT_TRUE(sandbox.LoadString(script, "require_test"));

    return true;
}
```

### Test 5: Safe Require Caches Modules

```cpp
bool Test_SafeRequireCaches(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.app_path = GetScriptsDir();

    LuaSandbox sandbox(ctx);

    std::string script = R"(
        local m1 = require("test_module")
        local m2 = require("test_module")
        return m1 == m2  -- Should be same table (cached)
    )";

    EXPECT_TRUE(sandbox.LoadString(script, "cache_test"));

    return true;
}
```

### Test 6: Safe Require Rejects Invalid Names

```cpp
bool Test_SafeRequireRejectsInvalid(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.app_path = GetScriptsDir();

    LuaSandbox sandbox(ctx);

    // Should reject path traversal in module name
    EXPECT_FALSE(sandbox.LoadString("require('../evil')", "evil_require"));

    // Should reject absolute paths
    EXPECT_FALSE(sandbox.LoadString("require('/etc/passwd')", "abs_require"));

    // Should reject special characters
    EXPECT_FALSE(sandbox.LoadString("require('foo;bar')", "special_require"));

    return true;
}
```

---

## Acceptance Criteria

All tests pass:

- [x] `Test_PathRejectsTraversal` - Block .. traversal
- [x] `Test_PathRejectsAbsolute` - Block absolute paths
- [x] `Test_PathAcceptsValid` - Allow valid relative paths
- [x] `Test_ModuleNameValidation` - Validate module names
- [x] `Test_ModuleToPath` - Convert module names to paths
- [x] `Test_SafeRequireLoads` - Load modules from scripts/
- [x] `Test_SafeRequireCaches` - Cache loaded modules
- [x] `Test_SafeRequireRejectsInvalid` - Reject malicious require calls

---

## Next Steps

After Milestone 4 passes:
1. Milestone 5: Timer & Callback System
2. Milestone 6: JSON & Crypto APIs
