Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions pmoves/chrome-extension/options/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,13 @@ $('#diag-recent-shapes').addEventListener('click', async () => {
const link = document.createElement('a');
const gatewayBase = $('#url-gateway')?.value || 'http://localhost:8085';
const vizUrl = `${gatewayBase}/viz/shape/${encodeURIComponent(s.shape_id)}.svg`;
// Validate URL scheme to prevent javascript: XSS (CodeQL #194)
if (/^https?:\/\//.test(vizUrl)) {
link.href = vizUrl;
}
// Validate URL via constructor to prevent XSS (CodeQL #194, #196)
try {
const parsed = new URL(vizUrl);
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
link.href = parsed.href;
}
} catch { /* invalid URL — skip href assignment */ }
Comment on lines +262 to +268

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for other unvalidated URL usages in the chrome extension

echo "=== Checking popup.js for URL handling ==="
rg -n -C3 'chrome\.tabs\.create' pmoves/chrome-extension/

echo ""
echo "=== Checking for direct href assignments without URL validation ==="
rg -n '\.href\s*=' pmoves/chrome-extension/ --type js

echo ""
echo "=== Checking pmoves-api.js for URL construction ==="
rg -n -C3 '_services\.gateway' pmoves/chrome-extension/

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 1372


🏁 Script executed:

#!/bin/bash
# Examine the msg() function and where r.url originates
echo "=== Examining popup.js msg function and getShapeSvgUrl action ==="
rg -n -B5 -A10 "getShapeSvgUrl" pmoves/chrome-extension/

echo ""
echo "=== Looking for message handler in popup.js or background script ==="
rg -n -B5 -A10 "getShapeSvgUrl" pmoves/chrome-extension/ --type js

echo ""
echo "=== Checking if pmoves-api.js exists ==="
find pmoves -name "*api*" -o -name "*pmoves*api*" 2>/dev/null

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 5368


🏁 Script executed:

#!/bin/bash
# Get more context around the msg() function in popup.js
echo "=== Finding msg function definition ==="
rg -n "msg\s*=" pmoves/chrome-extension/popup/popup.js -A3

echo ""
echo "=== Looking for background script or service worker ==="
find pmoves/chrome-extension -name "*.js" | grep -E "(background|service|worker)" | head -5

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 201


🏁 Script executed:

#!/bin/bash
# Find shapeSvgUrl function definition
echo "=== Finding shapeSvgUrl function ==="
rg -n "shapeSvgUrl" pmoves/chrome-extension/background.js -B2 -A5

echo ""
echo "=== Looking at pmoves-api.js for URL construction ==="
head -50 pmoves/chrome-extension/lib/pmoves-api.js

echo ""
echo "=== Searching for _services.gateway in pmoves-api.js ==="
rg -n "_services\\.gateway\|gateway" pmoves/chrome-extension/lib/pmoves-api.js -C2

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 2179


🏁 Script executed:

#!/bin/bash
# Find shapeSvgUrl function in pmoves-api.js
echo "=== Finding shapeSvgUrl implementation ==="
rg -n "shapeSvgUrl" pmoves/chrome-extension/lib/pmoves-api.js -B2 -A8

echo ""
echo "=== Full pmoves-api.js to see all exports ==="
wc -l pmoves/chrome-extension/lib/pmoves-api.js

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 488


URL validation missing in popup.js: chrome.tabs.create() passes unvalidated URLs.

At popup.js:188, the code passes r.url directly to chrome.tabs.create({ url: r.url }) without validation. This URL originates from shapeSvgUrl() (pmoves-api.js:319), which constructs it from the configurable _services.gateway without protocol validation:

return `${_services.gateway}/viz/shape/${encodeURIComponent(shapeId)}.svg`;

If _services.gateway is set to a javascript: or data: URL, the extension would attempt to open it, bypassing the protocol validation now applied in options.js.

Apply the same new URL() constructor + protocol check before calling chrome.tabs.create() to ensure consistent security posture.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pmoves/chrome-extension/options/options.js` around lines 262 - 268, In
popup.js, before calling chrome.tabs.create({ url: r.url }) (the place using
r.url returned from shapeSvgUrl() in pmoves-api.js), validate r.url by
constructing new URL(r.url) inside a try/catch and ensure its protocol is
'http:' or 'https:'; only pass the parsed.href to chrome.tabs.create when the
check succeeds, otherwise skip opening the tab or handle the error. This mirrors
the URL validation used in options.js and ensures untrusted _services.gateway
values (including 'javascript:' or 'data:') are not opened.

link.target = '_blank';
link.style.cssText = 'margin-left:8px;color:#667eea;';
link.textContent = 'View SVG';
Expand Down
2 changes: 1 addition & 1 deletion pmoves/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ services:
supabase-db:
condition: service_healthy
ports:
- ${SUPABASE_REALTIME_PORT:-4000}:4000
- ${SUPABASE_REALTIME_PORT:-4010}:4000
Comment on lines 711 to +712

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify env files and test defaults for port consistency

echo "=== Checking env.supabase for SUPABASE_REALTIME_PORT ==="
rg -n "SUPABASE_REALTIME_PORT" pmoves/env.supabase pmoves/env.tier-supabase 2>/dev/null || echo "Files not found or no matches"

echo ""
echo "=== Checking test files for hardcoded port 4000 references ==="
rg -n "localhost:4000" pmoves/tests/

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 460


Update env.supabase and test file defaults to match the new 4010 host port.

The docker-compose change from port 4000→4010 avoids a port collision, but pmoves/env.supabase (line 167) still sets SUPABASE_REALTIME_PORT=4000, which will override this default. Additionally, test files at pmoves/tests/smoke/test_supabase_selfhosted.py:31 and test_supabase_realtime_tenant.py:29 use hardcoded ws://localhost:4000/socket/websocket, causing test failures against the new compose default.

The internal container port (4000) correctly remains unchanged, so container-to-container references continue working.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pmoves/docker-compose.yml` around lines 711 - 712, Update the
SUPABASE_REALTIME_PORT default in pmoves/env.supabase (change
SUPABASE_REALTIME_PORT from 4000 to 4010) and update the hardcoded websocket
URLs in pmoves/tests/smoke/test_supabase_selfhosted.py (line with
"ws://localhost:4000/socket/websocket") and
pmoves/tests/smoke/test_supabase_realtime_tenant.py (line with
"ws://localhost:4000/socket/websocket") to use port 4010 or derive the host URL
from SUPABASE_REALTIME_PORT so tests follow the new docker-compose host port;
leave the internal container port (4000) references unchanged.

networks:
- pmoves_api
- pmoves_data
Expand Down
35 changes: 27 additions & 8 deletions pmoves/docs/PRODUCTION_AUDIT_DASHBOARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,44 @@
> **Single source of truth** for PMOVES.AI production readiness.
> Supersedes all individual audit documents accumulated Feb 7 -- Feb 18, 2026.

**Last Updated:** 2026-03-11 (CodeQL #195 suppression + final branch sync)
**Last Updated:** 2026-03-11 (Post-PR #865 Supabase unification + CodeQL #196 fix)
**Branch:** `main`
**Commit:** `da0fd666` (CodeQL #195 suppression + dashboard update)
**Commit:** `65c3efd3` (Merge PR #865 — Supabase unification)
**Consolidated From:** 27 audit documents
**Evidence:** live runbook execution on 2026-03-05 (`make ghcr-prepublish-inrepo-build`, strict local Trivy sweep logs under `pmoves/docs/logs/ghcr-local-prepublish/`)

---

## Latest Changes (Mar 11, 2026)

### Branch Strategy Validation & Final Cleanup
### Post-PR #865 — Supabase Unification Complete

- **Supabase unification merged (PR #865):** 4 competing compose stacks → 1 canonical stack
- 13 services under `supabase-local` profile (DB, GoTrue, PostgREST, Kong, Realtime, Storage, Studio, imgproxy, pg-meta, Edge Functions, Analytics/Logflare, Vector, Supavisor)
- Consumer URL: `http://supabase-kong:8000/rest/v1`
- All 13 services healthchecked (12 native + PostgREST documented exception)
- `generate-keys.sh` fixed: pipefail-safe local/assignment split + macOS `openssl base64 -A` compat
- ServiceTier aligned to canonical 7 tiers (added missing `ui` tier)
- **PR #864 closed** — superseded by #865 (integration credentials folded into unification)
- **CodeQL #196 fixed:** `js/xss-through-dom` in `chrome-extension/options/options.js:264` — strengthened URL sanitization from regex to `new URL()` constructor with strict protocol allowlist (`http:`/`https:` only via `parsed.href`)
- **CodeQL #194 auto-closed:** Rescan confirmed fix from `6c3a0455` (scheme validation)
- **Branch sync:** main → Hardened synced (`6726c146`)
- **Branch cleanup:** Deleted 2 feature branches (`feat/supabase-unify`, `fix/review-864`)
- **Remaining branches:** `main`, `PMOVES.AI-Edition-Hardened`, `PMOVES.AI-Edition-Hardened-Integrations`, `PMOVES.AI-Edition-Hardened-v3-clean`
- **CodeRabbit non-blocking follow-ups from PR #865:**
- `health_to_research.json`: DeepResearch endpoint reference (cosmetic)
- `integration_status_reporter.json`: Discord notification field names (cosmetic)
- `voice_health_checkin.json`: Orphaned prompt template (cleanup)

### Previous — Branch Strategy Validation & Final Cleanup

- **Branch sync completed:** Reconciled bidirectional divergence (16 Hardened-only / 24 main-only commits)
- Merged main → Hardened (PRs #848–#863 squash-merges)
- Merged Hardened → main (distributed topology docs, submodule pins, dashboard refresh)
- Synced Integrations branch to match Hardened
- **Stale branch cleanup:** Deleted 18 remote branches from merged/closed PRs (#842–#863)
- Remaining branches: `main`, `PMOVES.AI-Edition-Hardened`, `PMOVES.AI-Edition-Hardened-Integrations`, `PMOVES.AI-Edition-Hardened-v3-clean`
- **CodeQL #194 fixed:** `js/xss-through-dom` in `chrome-extension/options/options.js` — added URL scheme validation (`/^https?:\/\//`) before assigning user-controlled `gatewayBase` to `link.href` (pending rescan auto-closure)
- **CodeQL #194 fixed:** `js/xss-through-dom` in `chrome-extension/options/options.js` — added URL scheme validation (`/^https?:\/\//`) before assigning user-controlled `gatewayBase` to `link.href` (auto-closed by rescan)
- **CodeQL #195 suppressed:** `js/resource-exhaustion` in `ui/lib/serviceHealth.ts:71` — FALSE POSITIVE, timeout already clamped to `[1s, 60s]` via `Math.min(Math.max())` at line 69. Added `lgtm[js/resource-exhaustion]` suppression comment
- **Legacy CI refs cleaned:** Removed non-existent `integration` branch from `chit-contract.yml` and `deploy-gateway-agent.yml` workflow triggers
- **CONTRIBUTING.md updated:** PR target changed from `main` to `PMOVES.AI-Edition-Hardened-Integrations` per documented branch strategy
Expand Down Expand Up @@ -467,15 +486,15 @@ Three CI pipelines build Docker images. This matrix is the single cross-referenc

| Metric | Value |
|--------|-------|
| Quantitative snapshot timestamp | 2026-03-11 (PR gate sweep — all PRs merged, 0 open) |
| Quantitative snapshot timestamp | 2026-03-11 (Post-PR #865 Supabase unification + CodeQL #196 fix) |
| Total tracked items | 24 |
| Resolved | 24 (+1 since last update) |
| Active blockers (release-blocking) | 0 |
| Critical | 0 |
| High | 0 |
| Medium | 0 |
| Low | 0 |
| CodeQL alerts (open) | **0 open** (live GitHub API on 2026-03-11; #194 pending rescan, #195 suppressed as false positive) |
| CodeQL alerts (open) | **1 open** → **0 after merge** (#194 auto-closed by rescan; #195 suppressed as FP; #196 fixed in this PR via `new URL()` constructor) |
| Dependabot alerts | **0 open** (live GitHub API on 2026-03-09) |
| Open PRs | **0** |
| CI queue | **HEALTHY** — 3/4 self-hosted runners online (2 Docker containers via `local_cert_runners.py` + 1 Windows native). Phase policy `local-certification` PASS. Start: `make -C pmoves ci-runners-local-cert-up`. Hotfix runner offline (non-blocking). |
Expand Down Expand Up @@ -641,7 +660,7 @@ These are tracked as release gates and should be closed with command evidence be
## CodeQL Alert Triage (2026-02-18 Baseline → 2026-02-28 Update)

**Historical section:** this table preserves the 2026-02-28 triage baseline for traceability.
**Live status on 2026-03-11:** CodeQL open alerts are **0** (#194 fixed in `6c3a0455` pending rescan auto-closure; #195 false positive suppressed with `lgtm` comment).
**Live status on 2026-03-11:** CodeQL open alerts are **0** after merge (#194 auto-closed by rescan; #195 false positive suppressed with `lgtm` comment; #196 fixed via `new URL()` constructor sanitization in `options.js:263-268`).

| Group | Count | Severity | Rule | Files | Remediation | Status |
|-------|-------|----------|------|-------|-------------|--------|
Expand All @@ -650,7 +669,7 @@ These are tracked as release gates and should be closed with command evidence be
| C | 6 | high | `py/path-injection` | `gateway/api/viz.py` (4), `gateway/api/chit.py` (2) | Validate/sanitize file path parameters | **FIXED** (PR #715) |
| D | 2 | high | `py/path-injection` | `hf-mcp-server/main.py` (L522, L630) | Validate HuggingFace model paths | **FIXED** (PR #715) |
| E | 5 | medium | `py/stack-trace-exposure` | `consciousness-service/main.py` (3), `gateway/api/workflow.py`, `supaserch/app.py` | Replace traceback in HTTP responses with generic errors | **FIXED** (PR #715) |
| F | 2 | high | `js/xss-through-dom`, `js/resource-exhaustion` | `gateway/web/client.html:69`, `ui/lib/serviceHealth.ts:56` | Sanitize innerHTML; add request limits/timeouts | **FIXED** (#194 scheme validation in `6c3a0455`; #195 false positive suppressed — timeout clamped `[1s,60s]`) |
| F | 3 | high | `js/xss-through-dom`, `js/resource-exhaustion` | `gateway/web/client.html:69`, `ui/lib/serviceHealth.ts:56`, `chrome-extension/options/options.js:264` | Sanitize innerHTML; add request limits/timeouts | **FIXED** (#194 scheme validation in `6c3a0455`; #195 FP suppressed; #196 `new URL()` constructor sanitization) |
| G | 1 | high | `py/clear-text-logging` | `tools/chit_credential_demo.py:123` | Demo tool; redact or suppress sensitive logging | OPEN |
| H | 31 | mixed | Various | New/expanded scan results from PRs #716-719 | Requires fresh triage pass | **NEW** |

Expand Down
4 changes: 2 additions & 2 deletions pmoves/tests/smoke/test_supabase_realtime_tenant.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def test_realtime_has_seed_self_host_enabled() -> None:
@pytest.mark.smoke
def test_realtime_jwt_secret_configured() -> None:
"""Verify Realtime JWT secret is configured."""
output = grep_context(COMPOSE_FILE, r"supabase-realtime:", after=30)
output = grep_context(COMPOSE_FILE, r"supabase-realtime:", after=55)
if not output:
pytest.skip("supabase-realtime service not found in docker-compose.yml")

Expand Down Expand Up @@ -203,7 +203,7 @@ def test_realtime_database_schema_exists() -> None:
@pytest.mark.smoke
def test_realtime_healthcheck_configured() -> None:
"""Verify Realtime has a healthcheck configured in docker-compose."""
output = grep_context(COMPOSE_FILE, r"supabase-realtime:", after=30)
output = grep_context(COMPOSE_FILE, r"supabase-realtime:", after=55)
assert output, "supabase-realtime service not found"

assert "healthcheck:" in output, (
Expand Down
Loading