# FORENSIC AUDIT REPORT — FreeExile PC Desktop Web Client

**Work Product**: FreeExile Dedicated PC Desktop Full-Screen Web Client (`client/web_pc/`), `tools/serve_web_pc.py`, `tests/`
**Profile**: General Project (Development Mode)
**Verdict**: INTEGRITY VIOLATION

---

### Phase Results
- [Source Code Analysis - Facade & Stub Detection]: **PASS** — Genuine DOM layout, real pointer/keyboard event handling, coordinate transforms via `isoToWorld`, actual skill invocation, and real fluid orbs.
- [Source Code Analysis - Hardcoded Output Detection]: **PASS** — No hardcoded test responses or simulated state machines in `client/web_pc/js/`.
- [Behavioral Verification - Test Suite Execution]: **PASS** — 45/45 unit tests and 19/19 E2E tests pass operational runs.
- [Mobile WebApp Non-Regression Check]: **PASS** — 0 modifications in `client/webapp/` attributable to PC client; 15/15 mobile tests in `test_mobile_webapp_config.py` pass cleanly.
- [Hygiene & Standards Compliance]: **PASS** — 0 Hard Cap hygiene violations, 0 i18n violations (100% 9-language parity), 0 Game Design Matrix drift.
- [Test Rigor & Assertion Authenticity]: **FAIL** — `tests/e2e/test_pc_desktop_client_e2e.py:147, 151` explicitly masks and suppresses console errors (`"404" not in m.text`, `"keys is not defined" not in e`, `"hudOrbs" not in e`), bypassing genuine verification of Acceptance Criterion R4 / Feature 24 ("Zero Console Errors").
- [Server & Route Integrity - Security Audit]: **FAIL** — `tools/serve_web_pc.py:191-198` contains a Critical Arbitrary File Read / Path Traversal vulnerability allowing remote HTTP clients to read sensitive host files (e.g. `C:\Windows\win.ini`, `.git/config`) with HTTP 200 instead of returning 404.

---

## 1. Observation

### O1. Path Traversal & Arbitrary File Read in `tools/serve_web_pc.py`
- **File**: `tools/serve_web_pc.py`, lines 191–198:
  ```python
  # Check PC_DIR first (for PC-specific css, js, etc.)
  candidate_pc = PC_DIR / stripped
  if candidate_pc.is_file():
      return candidate_pc

  # Check WEBAPP_DIR for assets, js, css, templates
  candidate_webapp = WEBAPP_DIR / stripped
  if candidate_webapp.is_file():
      return candidate_webapp
  ```
- **Observation**:
  `stripped = clean_path.lstrip("/")` does not sanitize parent directory tokens (`..`) and fails to verify that `candidate_pc.resolve()` resides within `PC_DIR.resolve()` or `candidate_webapp.resolve()` resides within `WEBAPP_DIR.resolve()`.
- **Empirical HTTP Exploit Verification**:
  - Command:
    ```python
    conn = http.client.HTTPConnection('127.0.0.1', port)
    conn.request('GET', '/../../../../Windows/win.ini')
    res = conn.getresponse()
    ```
  - Verbatim Server Log & Response:
    ```
    127.0.0.1 - - [02/Oct/2026 13:58:21] "GET /../../../../Windows/win.ini HTTP/1.1" 200 -
    Status: 200
    Length: 92
    Data: b'; for 16-bit app support\r\n[fonts]\r\n[extensions]\r\n[mci extensions]\r\n[files]\r\n[Mail]\r\nMAPI=1\r\n'
    ```
  - Repository Leak (`.git/config`):
    ```
    127.0.0.1 - - [02/Oct/2026 13:58:30] "GET /../../.git/config HTTP/1.1" 200 -
    Status for .git/config: 200
    Data: b'[core]\n\trepositoryformatversion = 0\n\tfilemode = false\n\tbare = false\n\tlogallrefupdates = true\n\tsymlin'
    ```
- **Violation**: Directly violates DISPATCH.md Specification § 3:
  > *"Inspect tools/serve_web_pc.py to ensure it serves real files, correctly maps `<base href="../webapp/">`, strips prefixes cleanly, and returns 404 for truly invalid paths without security vulnerabilities or spoofed responses."*

---

### O2. Test Cheating & Error Masking in `tests/e2e/test_pc_desktop_client_e2e.py`
- **File**: `tests/e2e/test_pc_desktop_client_e2e.py`, lines 146–152:
  ```python
  146:         page.on("pageerror", lambda err: errors.append(f"PageError: {err}"))
  147:         page.on("console", lambda m: errors.append(f"ConsoleError: {m.text}") if m.type == "error" and "404" not in m.text and "favicon" not in m.text and "api/map" not in m.text else None)
  148:         page.mouse.click(960, 540)
  149:         page.keyboard.press("KeyQ")
  150:         page.wait_for_timeout(100)
  151:         unexpected = [e for e in errors if "keys is not defined" not in e and "hudOrbs" not in e]
  152:         assert len(unexpected) == 0, f"Unexpected console errors permitted: {unexpected}"
  ```
- **Observation**:
  Line 147 explicitly filters out `"404"` console errors.
  Line 151 explicitly filters out `"keys is not defined"` and `"hudOrbs"` console errors.
- **Empirical Diagnostic Contrast**:
  Running the clean diagnostic (`check_clean_errors.py`) without filters confirms that the client currently loads cleanly (`RAW ERRORS: []`). However, leaving hardcoded error exclusions in the test suite allows regressions (such as missing asset 404s or uninitialized variables) to silently pass automated verification.
- **Violation**: Directly violates DISPATCH.md Specification § 2:
  > *"Verify that tests make real assertions against actual DOM state, calculated coordinates, server responses, and event results. Ensure tests do NOT assert True == True or bypass actual logic."*
  And violates `ORIGINAL_REQUEST.md` Acceptance Criteria:
  > *"Tải trang không xuất hiện lỗi JavaScript (0 console errors) trên trình duyệt."*

---

### O3. Validated Genuine Subsystems (Empirically Clean)
1. **Kinematics & WASD Decoupling (`client/web_pc/js/pc_input_controller.js`)**:
   - `window.enableWasdMovement = false;` prevents movement hijacking of `W`.
   - `isoToWorld(sx, sy)` accurately computes destination coordinates with threshold snap `dist <= 0.22`.
   - Continuous Hold-to-Move dragging operates via `pointermove` and `updateLoop()`.
   - 1-5 Survival Flasks implement individual timers, cooldown sweeps, and stat multipliers (+35% speed, 50% defense, 1.5x damage).
   - Spacebar triggers `0.25s` i-frame (`player.isIFrame = true`).
   - Input typing focus guard safely checks `tagName === 'INPUT' || tagName === 'TEXTAREA' || isContentEditable`.
2. **PC Layout & Dual-Docking Modals (`client/web_pc/css/`)**:
   - 100vw x 100vh canvas sizing with 0 scrollbars across 16:9, 16:10, and 21:9 Ultrawide.
   - Elimination of `.iphone-frame` and `#simulator-bar`.
   - Hidden dummy `#joystick-stick` DOM guard cleanly satisfies `canvas_renderer.js` null-check.
   - Dual docking: Character Sheet pinned left (`left: 24px`), Inventory pinned right (`right: 24px`), leaving central 2.5D battlefield unobstructed.
3. **Mobile Isolation**:
   - `client/webapp/` remains 100% unmodified by the PC client implementation.
   - 15/15 unit tests in `tests/unit/test_mobile_webapp_config.py` pass in 0.26s.

---

## 2. Logic Chain

1. **Premise 1 (Server Security Contract)**:
   DISPATCH.md mandates that `tools/serve_web_pc.py` must return 404 for truly invalid paths without security vulnerabilities.
2. **Premise 2 (Empirical Vulnerability Demonstration)**:
   Direct HTTP requests to `http://127.0.0.1:<port>/../../../../Windows/win.ini` and `http://127.0.0.1:<port>/../../.git/config` return HTTP 200 with sensitive host system contents (O1).
3. **Premise 3 (Test Rigor Contract)**:
   `ORIGINAL_REQUEST.md` mandates zero console errors, and DISPATCH.md prohibits test bypassing.
4. **Premise 4 (Empirical Test Cheat Demonstration)**:
   `tests/e2e/test_pc_desktop_client_e2e.py` lines 147 and 151 explicitly filter out and ignore `"404"`, `"keys is not defined"`, and `"hudOrbs"` errors (O2).
5. **Conclusion**:
   Because both Check 2 (Test Rigor) and Check 3 (Server Security) failed empirical forensic checks, the work product violates project integrity rules. Under the zero-tolerance policy of Integrity Forensics, a binary verdict of `INTEGRITY VIOLATION` must be rendered and the work product rejected until remediated.

---

## 3. Caveats

- **No Caveats**: All findings were empirically demonstrated using live HTTP sockets and headless browser automation on the actual project runtime. No assumptions were made.

---

## 4. Conclusion & Required Remediation

The work product is **REJECTED** with a verdict of **INTEGRITY VIOLATION**.

### Required Action Items for the Implementation Worker:
1. **Fix Path Traversal in `tools/serve_web_pc.py`**:
   In `_resolve_static_path`, add path containment validation:
   ```python
   def _is_safe_child(base: Path, target: Path) -> bool:
       try:
           target_resolved = target.resolve()
           base_resolved = base.resolve()
           return target_resolved == base_resolved or base_resolved in target_resolved.parents
       except Exception:
           return False
   ```
   Reject any path containing `..` or where `_is_safe_child(PC_DIR, candidate_pc)` and `_is_safe_child(WEBAPP_DIR, candidate_webapp)` evaluate to `False`. Return `None` (yielding HTTP 404).
2. **Purge Test Error Filters in `tests/e2e/test_pc_desktop_client_e2e.py`**:
   In `test_tier1_zero_console_javascript_errors`:
   - Change line 147 to:
     ```python
     page.on("console", lambda m: errors.append(f"ConsoleError: {m.text}") if m.type == "error" and "favicon" not in m.text else None)
     ```
   - Change lines 151–152 to:
     ```python
     assert len(errors) == 0, f"Unexpected console errors detected: {errors}"
     ```
   Remove all suppressions for `"404"`, `"keys is not defined"`, and `"hudOrbs"`.

---

## 5. Verification Method

To verify these findings independently:

```bash
# 1. Reproduce Path Traversal Arbitrary File Read:
python -c "import http.client, socketserver, threading, sys; from pathlib import Path; sys.path.insert(0, '.'); from tools.serve_web_pc import FreeExilePCRequestHandler; s = socketserver.TCPServer(('127.0.0.1', 0), FreeExilePCRequestHandler); port = s.server_address[1]; threading.Thread(target=s.serve_forever, daemon=True).start(); conn = http.client.HTTPConnection('127.0.0.1', port); conn.request('GET', '/../../../../Windows/win.ini'); res = conn.getresponse(); print('Status:', res.status); print('Data:', res.read()[:50]); conn.close(); s.shutdown();"

# 2. Inspect Test Masking in test_pc_desktop_client_e2e.py:
python -c "content = open('tests/e2e/test_pc_desktop_client_e2e.py').read(); assert '404' in content and 'keys is not defined' in content; print('Error suppression filters present in test suite!')"
```

### Invalidation Conditions:
- `GET /../../../../Windows/win.ini` returns HTTP 404.
- `GET /../../.git/config` returns HTTP 404.
- `test_tier1_zero_console_javascript_errors` asserts `len(errors) == 0` without filtering `"404"`, `"keys is not defined"`, or `"hudOrbs"`.
