chore: initial commit of Server Configs

This commit is contained in:
sapient
2026-03-22 00:54:28 -07:00
commit 5fc35b2f76
226 changed files with 7906 additions and 0 deletions

149
httpserver/AUDIT.md Normal file
View File

@@ -0,0 +1,149 @@
# Code Audit Report — httpserver stack
**Date:** 2026-03-05
**Scope:** `/opt/stacks/httpserver` (compose, data/, chat server, services loader, static assets)
---
## Executive summary
- **Security:** Several issues: sensitive file exposure, client-side XSS risk in chat, missing escaping in services HTML, no chat rate limiting or abuse controls.
- **Correctness:** One protocol mismatch (gs.html chat history never populates).
- **Maintainability:** Minor issues (comment vs script name, dependency pinning).
---
## 1. Security
### 1.1 Sensitive file served as static content (High)
**File:** `data/banned.json`
**Issue:** The entire `data/` directory is mounted as the Apache document root (`./data:/var/www/html`). So `banned.json` is publicly reachable at `/banned.json`. It contains fail2ban-style jail names and IP addresses.
**Impact:** Information disclosure (internal IPs, jail names). Useful for reconnaissance.
**Recommendation:**
- Move `banned.json` outside the web root (e.g. project root or a non-served volume) if it must live in the repo, **or**
- Exclude it from the volume used as document root (e.g. serve only a whitelisted subdirectory), **or**
- Stop serving it over HTTP (e.g. use it only in a backend/script that doesnt expose it).
### 1.2 Chat client uses `innerHTML` for message text (Medium)
**File:** `data/chat.js` (around line 102)
```javascript
// Comment says "use textContent for safety" but code uses innerHTML:
textSpan.innerHTML = msg.text;
```
**Issue:** The server sanitizes only `<` and `>` in `chat-server.js`. Using `innerHTML` with server output is fragile: if sanitization is ever relaxed or bypassed, or if the client receives data from another path, this becomes an XSS sink.
**Recommendation:** Use `textContent` for the message body so the DOM is not parsed as HTML. If you need newlines, use `textContent` and style with `white-space: pre-wrap` (or similar).
### 1.3 Services HTML built from JSON without escaping (Medium)
**File:** `data/services-loader.js`
**Issue:** Service `name`, `url`, `category`, and `meta[].label` / `meta[].url` are interpolated into HTML strings (e.g. `href="${meta.url}"`, `${service.name}`) without encoding. If `services-data.json` is ever edited incorrectly, compromised, or merged with untrusted data, a value containing `"` or `>` could break attributes or inject script.
**Recommendation:** Add a small `escapeHtml` (and optionally `escapeAttr`) helper and use it for every value that is inserted into HTML or attributes. Keep treating `services-data.json` as trusted input, but defense-in-depth avoids mistakes and future data sources.
### 1.4 Chat server: no rate limiting or abuse controls (Medium)
**File:** `data/chat-server.js`
**Issue:** Any client can connect and send unlimited messages. There is no per-IP or per-connection rate limit, no use of `banned.json`, and no max message size beyond the 200-character sanitizer slice.
**Impact:** DoS via message flood; spam; possible memory pressure from a very large `history` if `HISTORY_MAX` is raised.
**Recommendation:** Add rate limiting (e.g. per connection: max N messages per minute). Optionally enforce max message size and consider using `banned.json` (or a similar list) to reject connections from banned IPs if the proxy passes client IP (e.g. via `X-Forwarded-For` and a trusted proxy config).
### 1.5 Chat sanitization is minimal (Low)
**File:** `data/chat-server.js``sanitize()`
**Issue:** Only `<` and `>` are escaped. For plain text in a `<div>` (and if the client uses `textContent` as recommended), this is enough for basic XSS prevention. It does not normalize Unicode or protect against other edge cases (e.g. if the same string were ever used in an attribute).
**Recommendation:** Keep server-side sanitization and switch the client to `textContent`. If you later use the same string in attributes or other contexts, add encoding appropriate to that context (e.g. attribute encoding).
---
## 2. Correctness
### 2.1 Chat history protocol mismatch in gs.html (Bug)
**Files:** `data/chat-server.js` vs `data/gs.html`
**Issue:** The server sends history as:
```javascript
{ type: 'history', messages: history }
```
`gs.html` expects:
```javascript
if (Array.isArray(msg.history)) { ... }
```
So `msg.history` is always undefined and chat history is never shown on the GS page.
**Recommendation:** In `gs.html`, use `msg.messages` when `data.type === 'history'` (and optionally check `Array.isArray(msg.messages)`), consistent with `chat.js`.
---
## 3. Configuration & deployment
### 3.1 Apache proxy config duplication
**File:** `data/chat-proxy.conf`
**Issue:** Both `RewriteRule` and `ProxyPass`/`ProxyPassReverse` are used for `/chat` → WebSocket. This can be redundant or confusing; one consistent mechanism (e.g. mod_rewrite with `[P]` or ProxyPass) is easier to reason about.
**Recommendation:** Prefer one approach (e.g. RewriteRule with `[P,L]` for WebSocket upgrade, and ensure no double proxy). Document which Apache modules are required.
### 3.2 Chat server port exposure
**File:** `compose.yaml`
**Issue:** The chat service publishes `8098:8081`. So the WebSocket server is reachable on host port 8098 without going through Apache. If Apache is the intended single entry point for the site, consider not publishing the chat port on the host (only expose it on `internal-net` so Apache can proxy to it).
**Recommendation:** Remove the `ports:` mapping for the chat service if all access should be via Apaches `/chat`; otherwise document that 8098 is intentionally public.
### 3.3 Dependency pinning
**File:** `data/package.json`
**Issue:** `"ws": "^8.18.0"` allows minor/patch updates. Rebuilds can pull different versions.
**Recommendation:** Use exact versions (e.g. `"ws": "8.18.0"`) or lock with `package-lock.json` committed and `npm ci` in the image for reproducible builds.
---
## 4. Maintainability
### 4.1 Outdated comment / missing script
**File:** `data/services-loader.js` (header comment)
**Issue:** Historical note: the loader previously had an embedded-data workflow and referenced a script for syncing/embedding.
**Recommendation:** Keep `services-data.json` as the single source of truth and load it at runtime (no embed/sync step).
---
## 5. Positive notes
- Chat server sanitizes nickname and message length and strips `<`/`>`.
- `gs.html` uses an `escapeHtml` helper for chat nick and text when building HTML.
- `main.js` respects `prefers-reduced-motion` and limits star count on small viewports.
- Compose resource limits and logging options are set; networks are isolated.
- `.env` in project root is not under the web root, so it is not served.
---
## 6. Summary of recommended actions (all applied)
| Priority | Item | Status |
|----------|------|--------|
| High | Stop serving `banned.json` | Done: Apache `<Files "banned.json"> Require all denied` in `chat-proxy.conf` |
| Medium | In `chat.js`, use `textContent` for message text | Done |
| Medium | In `services-loader.js`, escape all dynamic values | Done: `escapeHtml` / `escapeAttr` added and used |
| Medium | Add rate limiting to chat server | Done: 30 msg/min per connection (configurable via `CHAT_RATE_LIMIT`) |
| Medium | Fix `gs.html` to use `msg.messages` for history | Done |
| Low | Simplify Apache proxy config and document | Done: comment + deny for banned.json |
| Low | Do not publish chat port; use Apache only | Done: port removed; `gs.html` uses `location.host + '/chat'` |
| Low | Pin `ws`; fix `services-loader.js` comment | Done: `"ws": "8.18.0"`; loader now reads `services-data.json` directly (no status/embed script). |