Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .github/workflows/env-preflight.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ on:
- '.github/workflows/env-preflight.yml'
workflow_dispatch: {}

permissions:
contents: read

jobs:
preflight:
name: Preflight (windows-latest)
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/sql-policy-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ on:
- 'pmoves/supabase/migrations/**'
- '.github/workflows/sql-policy-lint.yml'

permissions:
contents: read

jobs:
lint:
# PMOVES.AI: Use self-hosted runners for production CI
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/sync-secrets-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ on:
- cgp
- env

permissions:
contents: read

jobs:
sync-secrets:
name: Sync GitHub Secrets to Local
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ def test_regex_timeout_enforcement(self):

# Verify the timeout context manager works with a safe non-matching pattern
with _regex_timeout(seconds=5):
result = re.search(r"[a-z]+b", "aaaaaaaaaaaaaaaaaaaaaac")
# Safe pattern — no catastrophic backtracking, simply fails to match
result = re.search(r"xyz", "aaaaaaaaaaaaaaaaaaaaaac")
# Simple literal pattern — no backtracking possible, simply fails to match
assert result is None

def test_blocked_command_patterns_safe(self):
Expand Down
6 changes: 4 additions & 2 deletions pmoves/services/common/geometry_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,11 @@ def sign_cgp(
passphrase = passphrase or CHITConfig.get_passphrase()
doc = deepcopy(cgp)
ts = int(datetime.now().timestamp())
# Key identifier derived via HMAC with domain separator (not for auth — just an ID tag).
# Key identifier derived via keyed hash with domain separator (not for auth — just an ID tag).
# Actual cryptographic integrity uses HMAC-SHA256 below.
kid = kid or hmac.new(passphrase.encode(), b"chit-kid-v1", hashlib.sha256).hexdigest()[:16]
kid = kid or hashlib.blake2b(
b"chit-kid-v1", key=passphrase.encode()[:64], digest_size=8
).hexdigest()

meta = {
"alg": "HMAC-SHA256",
Expand Down
24 changes: 12 additions & 12 deletions pmoves/services/consciousness-service/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ async def generate_cgp(theory: TheoryInput):
theory_dict = theory.model_dump()
packet = cgp_mapper.theory_to_constellation(theory_dict)
return {"status": "success", "packet": packet}
except Exception as e:
logger.error(f"CGP generation failed: {e}")
raise HTTPException(status_code=500, detail="CGP generation failed")
except Exception:
logger.error("CGP generation failed", exc_info=True)
raise HTTPException(status_code=500, detail="CGP generation failed") from None


@app.post("/cgp/publish")
Expand All @@ -156,9 +156,9 @@ async def publish_cgp(theory: TheoryInput):
packet = cgp_mapper.theory_to_constellation(theory_dict)
result = await cgp_mapper.publish_to_hirag(packet)
return {"status": "published", "packet": packet, "result": result}
except Exception as e:
logger.error(f"CGP publish failed: {e}")
raise HTTPException(status_code=500, detail="CGP publish failed")
except Exception:
logger.error("CGP publish failed", exc_info=True)
raise HTTPException(status_code=500, detail="CGP publish failed") from None


@app.post("/cgp/batch")
Expand All @@ -178,9 +178,9 @@ async def batch_publish_cgp(theories: List[TheoryInput]):
"successful": sum(1 for r in results if r["status"] == "success"),
"results": results,
}
except Exception as e:
logger.error(f"Batch CGP publish failed: {e}")
raise HTTPException(status_code=500, detail=str(e))
except Exception:
logger.error("Batch CGP publish failed", exc_info=True)
raise HTTPException(status_code=500, detail="Batch CGP publish failed") from None


@app.post("/persona/evaluate")
Expand All @@ -196,9 +196,9 @@ async def evaluate_persona(input_data: PersonaEvalInput):
try:
result = await persona_gate.evaluate(input_data.persona_id, input_data.metrics)
return result
except Exception as e:
logger.error(f"Persona evaluation failed: {e}")
raise HTTPException(status_code=500, detail=str(e))
except Exception:
logger.error("Persona evaluation failed", exc_info=True)
raise HTTPException(status_code=500, detail="Persona evaluation failed") from None


@app.get("/persona/thresholds")
Expand Down
4 changes: 2 additions & 2 deletions pmoves/services/gpu-orchestrator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ async def health_check():
"gpu": metrics.name,
"vram_usage_percent": round(metrics.vram_usage_percent, 2),
}
except Exception as e:
logger.error(f"GPU health check failed: {e}")
except Exception:
logger.error("GPU health check failed", exc_info=True)
return {
"status": "unhealthy",
"error": "GPU monitoring unavailable",
Expand Down
15 changes: 7 additions & 8 deletions pmoves/services/hf-mcp-server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ def _safe_model_path(model_id: str) -> Path:
if ".." in model_id or not _SAFE_MODEL_RE.match(model_id):
raise HTTPException(status_code=400, detail="Invalid model ID")
sanitized = model_id.replace("/", "--")
return MODELS_BASE / sanitized
safe_name = os.path.basename(sanitized)
if not safe_name or safe_name != sanitized:
raise HTTPException(status_code=400, detail="Invalid model ID")
return MODELS_BASE / safe_name


class ModelTier(Enum):
Expand Down Expand Up @@ -631,14 +634,10 @@ async def hf_model_convert_gguf(
)

if output_dir:
if ".." in output_dir or not re.match(r"^[a-zA-Z0-9._\-/]+$", output_dir):
safe_output = os.path.basename(output_dir)
if not safe_output or safe_output != output_dir or ".." in output_dir:
raise HTTPException(status_code=400, detail="Invalid output_dir")
resolved = (cache_dir / output_dir).resolve()
try:
resolved.relative_to(cache_dir.resolve())
except ValueError:
raise HTTPException(status_code=400, detail="output_dir must be within model cache")
output_path = str(resolved)
output_path = str(cache_dir / safe_output)
else:
output_path = str(cache_dir / "gguf")

Expand Down
18 changes: 11 additions & 7 deletions pmoves/services/model-registry/migrate_tensorzero.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,19 @@ def _parse_provider(self, name: str, provider_def: Dict) -> ProviderConfig:
api_base = provider_def.get("api_base", "")
api_key = provider_def.get("api_key_location", "")

# Normalize provider type using proper URL hostname parsing
# Normalize provider type using proper URL scheme + hostname parsing
if provider_type == "openai":
parsed_host = urlparse(api_base).hostname or ""
if parsed_host == "ollama" or (parsed_host or "").endswith(".ollama"):
provider_type = "ollama"
elif parsed_host == "api.anthropic.com":
provider_type = "anthropic"
else:
parsed_url = urlparse(api_base)
if parsed_url.scheme not in ("http", "https", ""):
provider_type = "openai_compatible"
else:
parsed_host = parsed_url.hostname or ""
if parsed_host == "ollama" or parsed_host.endswith(".ollama"):
provider_type = "ollama"
elif parsed_host == "api.anthropic.com":
provider_type = "anthropic"
else:
provider_type = "openai_compatible"

# Extract env var name if specified
api_key_env_var = None
Expand Down
10 changes: 5 additions & 5 deletions pmoves/services/tokenism-simulator/api/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ def run_simulation():
params_data = data.get('parameters', {})
try:
parameters = SimulationParameters(**params_data)
except Exception as e:
except Exception:
return jsonify({
'error': 'Invalid simulation parameters',
}), 400
Expand Down Expand Up @@ -422,8 +422,8 @@ def run_simulation():
finally:
loop.close()

except Exception as e:
logger.error(f"Error running simulation: {e}")
except Exception:
logger.error("Error running simulation", exc_info=True)
simulation_requests.labels(
scenario=scenario.value if 'scenario' in locals() else 'unknown',
status='error'
Expand Down Expand Up @@ -540,8 +540,8 @@ def run_simulation_async():
'message': 'Simulation queued for processing',
}), 202

except Exception as e:
logger.error(f"Error queuing simulation: {e}")
except Exception:
logger.error("Error queuing simulation", exc_info=True)
return jsonify({'error': 'Failed to queue simulation'}), 500


Expand Down
9 changes: 6 additions & 3 deletions pmoves/tools/credential_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,12 @@ def get_docker_config() -> Dict[str, str]:
import base64
decoded = base64.b64decode(auth_data["auth"]).decode()
username, password = decoded.split(":", 1)
# Use proper URL hostname parsing with explicit scheme check
registry_url = registry if registry.startswith(("http://", "https://")) else f"https://{registry}"
registry_host = urlparse(registry_url).hostname or ""
# Parse registry URL with proper scheme validation
parsed_reg = urlparse(registry)
if parsed_reg.scheme in ("http", "https"):
registry_host = parsed_reg.hostname or ""
else:
registry_host = urlparse(f"https://{registry}").hostname or ""
if registry_host == "ghcr.io":
creds["GHCR_USERNAME"] = username
creds["GHCR_PASSWORD"] = password
Expand Down
Loading