From e88722efcd57dca0f2be224d1ae7e00679c64c64 Mon Sep 17 00:00:00 2001 From: POWERFULMOVES Date: Sun, 15 Mar 2026 15:08:01 -0400 Subject: [PATCH 1/4] fix(security): remove hardcoded CHIT passphrase from consciousness-service Remove `pmoves-chit-default` default from Dockerfile ENV and main.py fallback. Docker-compose enforces runtime injection via ${CHIT_PROD_PASSPHRASE:?...}, but the Dockerfile default was a security smell if the image ran standalone. Addresses: Z890 gap analysis Issue #3 (PR #905) Co-Authored-By: Claude Opus 4.6 (1M context) --- pmoves/services/consciousness-service/Dockerfile | 2 +- pmoves/services/consciousness-service/main.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pmoves/services/consciousness-service/Dockerfile b/pmoves/services/consciousness-service/Dockerfile index f94ea21352..f9ef2a0d22 100644 --- a/pmoves/services/consciousness-service/Dockerfile +++ b/pmoves/services/consciousness-service/Dockerfile @@ -31,7 +31,7 @@ USER pmoves ENV SERVICE_NAME=consciousness-service ENV SERVICE_PORT=8105 ENV NATS_URL=nats://nats:pmoves@nats:4222 -ENV CHIT_PROD_PASSPHRASE=pmoves-chit-default +# CHIT_PROD_PASSPHRASE injected at runtime via docker-compose ENV SUPABASE_URL=http://supabase-kong:8000 ENV PYTHONUNBUFFERED=1 diff --git a/pmoves/services/consciousness-service/main.py b/pmoves/services/consciousness-service/main.py index 094a772327..f097ca8193 100644 --- a/pmoves/services/consciousness-service/main.py +++ b/pmoves/services/consciousness-service/main.py @@ -37,7 +37,9 @@ SERVICE_NAME = os.environ.get("SERVICE_NAME", "consciousness-service") SERVICE_PORT = int(os.environ.get("SERVICE_PORT", "8105")) NATS_URL = os.environ.get("NATS_URL", "nats://nats:pmoves@nats:4222") -CHIT_PASSPHRASE = os.environ.get("CHIT_PROD_PASSPHRASE", "pmoves-chit-default") +CHIT_PASSPHRASE = os.environ.get("CHIT_PROD_PASSPHRASE", "") +if not CHIT_PASSPHRASE: + logger.warning("CHIT_PROD_PASSPHRASE not set — CGP signing disabled") SUPABASE_URL = os.environ.get("SUPABASE_URL", "http://supabase-kong:8000") SUPABASE_ANON_KEY = os.environ.get("SUPABASE_ANON_KEY", "") From f93744de3941b21fe0ec96e5f92f7ccdf27742b7 Mon Sep 17 00:00:00 2001 From: POWERFULMOVES Date: Sun, 15 Mar 2026 15:08:20 -0400 Subject: [PATCH 2/4] fix(security): add dev-mode warning and restrict role in cast-tts auth Downgrade dev bypass role from "admin" to "dev" to limit privilege escalation in development mode. Add logger.warning() when auth is bypassed so operators can detect misconfiguration in production logs. Addresses: Z890 gap analysis Issue #4 (PR #926) Co-Authored-By: Claude Opus 4.6 (1M context) --- pmoves/services/cast-tts-gateway/auth.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pmoves/services/cast-tts-gateway/auth.py b/pmoves/services/cast-tts-gateway/auth.py index a31c86ad78..91d1614d25 100644 --- a/pmoves/services/cast-tts-gateway/auth.py +++ b/pmoves/services/cast-tts-gateway/auth.py @@ -4,12 +4,15 @@ with optional development mode bypass for local testing. """ +import logging import os from typing import Callable, Awaitable from aiohttp import web import jose.jwt +logger = logging.getLogger(__name__) + async def get_user_context(request: web.Request) -> dict: """ @@ -34,9 +37,10 @@ async def get_user_context(request: web.Request) -> dict: # Allow unauthenticated requests in development mode if os.getenv("CAST_AUTH_REQUIRED", "true") == "false": + logger.warning("SECURITY: CAST_AUTH_REQUIRED=false — authentication bypassed (dev mode)") return { "user_id": "dev_user", - "role": "admin", + "role": "dev", "email": "dev@pmoves.ai" } From 61089726e9ab255f5dfd6568e9363f0d57c56713 Mon Sep 17 00:00:00 2001 From: POWERFULMOVES Date: Sun, 15 Mar 2026 15:08:29 -0400 Subject: [PATCH 3/4] fix(security): warn on unauthenticated NATS fallback in flute-gateway _build_nats_url() silently fell back to unauthenticated nats:// when NATS_URL and NATS_USER/NATS_PASSWORD were all unset. Add logger.warning() so operators can detect missing NATS credentials in logs. Addresses: Z890 gap analysis Issue #5 (PR #927) Co-Authored-By: Claude Opus 4.6 (1M context) --- pmoves/services/flute-gateway/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pmoves/services/flute-gateway/main.py b/pmoves/services/flute-gateway/main.py index 32546f4964..e6b4ec02b6 100644 --- a/pmoves/services/flute-gateway/main.py +++ b/pmoves/services/flute-gateway/main.py @@ -98,6 +98,7 @@ def _build_nats_url() -> str: return f"nats://{user}:{password}@{host}:{port}" if user: return f"nats://{user}@{host}:{port}" + logger.warning("NATS connection without credentials — set NATS_URL or NATS_USER/NATS_PASSWORD") return f"nats://{host}:{port}" From 2496348cb55d695e5350ba254df89dbb307e1f72 Mon Sep 17 00:00:00 2001 From: POWERFULMOVES Date: Sun, 15 Mar 2026 15:08:38 -0400 Subject: [PATCH 4/4] fix(security): remove docsRoot path leak from audit summary API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /api/audit/summary response included `docsRoot` — an absolute server filesystem path — in the JSON body. This leaks internal directory structure to unauthenticated clients. Remove it from the response payload. Addresses: Z890 gap analysis Issue #7 (PR #922) Co-Authored-By: Claude Opus 4.6 (1M context) --- pmoves/ui/app/api/audit/summary/route.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/pmoves/ui/app/api/audit/summary/route.ts b/pmoves/ui/app/api/audit/summary/route.ts index 68053a0a23..213646d92d 100644 --- a/pmoves/ui/app/api/audit/summary/route.ts +++ b/pmoves/ui/app/api/audit/summary/route.ts @@ -350,7 +350,6 @@ export async function GET(request: NextRequest) { return NextResponse.json( { generatedAt: new Date().toISOString(), - docsRoot, warnings, productionAudit: { source: dashboardMarkdown ? dashboardPath : null,