fix: address all code review findings + remove exposed secrets
Code review fixes: - 🟡 #1: Replace python3 with jq in Dockerfile template stages (~50MB → ~2MB) - 🟡 #2: Add clone count verification to scripts/clone-manifest.sh (set -e + expected vs actual count check — fails build if any clone fails) - 🟡 #3: Drop 'unsafe-eval' from CSP (not needed for Next.js production standalone builds, only dev mode). Updated test assertion. - 🟡 #4: Remove broken pyproject.toml from workspace-template/ (it claimed to package as molecule-ai-workspace-runtime but the directory structure didn't match — the real package ships from the standalone repo) - 🔵 #1: Add version-pinning TODO comment to manifest.json - 🔵 #3: Add full repo URLs + test counts for SDK/MCP/CLI/runtime in CLAUDE.md Security (GitGuardian alert): - Removed Telegram bot token (8633739353:AA...) from template-molecule-dev pm/.env — replaced with ${TELEGRAM_BOT_TOKEN} placeholder - Removed Claude OAuth token (sk-ant-oat01-...) from template-molecule-dev root .env — replaced with ${CLAUDE_CODE_OAUTH_TOKEN} placeholder - Both tokens need immediate rotation by the operator Tests: Platform middleware tests updated + all pass.
This commit is contained in:
parent
73865ee164
commit
510c40089f
@ -240,9 +240,11 @@ OPENAI_API_KEY=... bash scripts/test-team-e2e.sh # E2E: Multi-template
|
||||
cd platform && go test -race ./... # 818 Go tests (handlers, registry, provisioner, CLI, delegation, org, channels, wsauth, middleware, scheduler, crypto, db — sqlmock + miniredis; +2 on 2026-04-15 tick-32 for YAML-injection runtime/model allowlist + TestSanitizeRuntime_Allowlist; +70 on 2026-04-15 overnight sweep across the security fix cluster; +6 on 2026-04-14 tick-8 for TestTenantGuard_*)
|
||||
cd canvas && npm test # 482 Vitest tests (store, components, hydration, buildTree, secrets API, org template import, ConfirmDialog singleButton + 7 native-dialog replacements, WCAG critical batch, +12 on tick-32 for CookieConsent dialog + privacy-preserving default, +17 on tick-32 for PricingTable dispatch matrix + billing helper)
|
||||
cd workspace-template && python -m pytest -v # 1179 pytest tests (adds platform_auth token store for Phase 30.1, memory_write activity logging, Hermes multi-provider registry, +10 on tick-32 for test_hermes_phase2_dispatch covering native Anthropic + native Gemini paths via auth_scheme dispatch; fixed env-var leak in test_hermes_providers fixture via snapshot/restore)
|
||||
# SDK + MCP server tests now in standalone repos:
|
||||
# github.com/Molecule-AI/molecule-sdk-python (pip install molecule-ai-sdk)
|
||||
# github.com/Molecule-AI/molecule-mcp-server (npx @molecule-ai/mcp-server)
|
||||
# SDK, MCP, CLI, and workspace runtime now in standalone repos:
|
||||
# https://github.com/Molecule-AI/molecule-sdk-python pip install molecule-ai-sdk (132 tests)
|
||||
# https://github.com/Molecule-AI/molecule-mcp-server npx @molecule-ai/mcp-server (97 tests)
|
||||
# https://github.com/Molecule-AI/molecule-cli go install (Go TUI dashboard)
|
||||
# https://github.com/Molecule-AI/molecule-ai-workspace-runtime pip install molecule-ai-workspace-runtime (shared adapter base)
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"_comment": "TODO: pin refs to release tags (v1.0.0) before first customer deploy for reproducible builds. 'main' is OK while all repos are internal.",
|
||||
"version": 1,
|
||||
"plugins": [
|
||||
{"name": "browser-automation", "repo": "Molecule-AI/molecule-ai-plugin-browser-automation", "ref": "main"},
|
||||
|
||||
@ -12,7 +12,7 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o /platform ./cmd/server
|
||||
|
||||
# Clone templates + plugins at build time from manifest.json
|
||||
FROM alpine:3.20 AS templates
|
||||
RUN apk add --no-cache git python3
|
||||
RUN apk add --no-cache git jq
|
||||
COPY manifest.json /manifest.json
|
||||
COPY scripts/clone-manifest.sh /scripts/clone-manifest.sh
|
||||
RUN chmod +x /scripts/clone-manifest.sh && /scripts/clone-manifest.sh /manifest.json /workspace-configs-templates /org-templates /plugins
|
||||
|
||||
@ -35,7 +35,7 @@ RUN npm run build
|
||||
|
||||
# ── Stage 3: Clone templates + plugins from manifest.json ─────────────
|
||||
FROM alpine:3.20 AS templates
|
||||
RUN apk add --no-cache git python3
|
||||
RUN apk add --no-cache git jq
|
||||
COPY manifest.json /manifest.json
|
||||
COPY scripts/clone-manifest.sh /scripts/clone-manifest.sh
|
||||
RUN chmod +x /scripts/clone-manifest.sh && /scripts/clone-manifest.sh /manifest.json /workspace-configs-templates /org-templates /plugins
|
||||
|
||||
@ -37,13 +37,12 @@ func SecurityHeaders() gin.HandlerFunc {
|
||||
// Content-Type after Next() — but that's too late for headers.
|
||||
//
|
||||
// Simpler: apply a permissive CSP that allows Next.js to work.
|
||||
// 'unsafe-inline' + 'unsafe-eval' are needed for Next.js dev
|
||||
// hydration; in production Next.js uses nonces but we don't
|
||||
// propagate them through the proxy. This is acceptable because
|
||||
// the canvas is our own code, not user-generated content.
|
||||
// 'unsafe-inline' is needed for Next.js standalone builds that
|
||||
// inject inline scripts for hydration. 'unsafe-eval' was dropped
|
||||
// after confirming production canvas renders without it.
|
||||
c.Header("Content-Security-Policy",
|
||||
"default-src 'self'; "+
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; "+
|
||||
"script-src 'self' 'unsafe-inline'; "+
|
||||
"style-src 'self' 'unsafe-inline'; "+
|
||||
"img-src 'self' data: blob:; "+
|
||||
"connect-src 'self' ws: wss:; "+
|
||||
|
||||
@ -56,7 +56,7 @@ func TestSecurityHeaders(t *testing.T) {
|
||||
csp := w.Header().Get("Content-Security-Policy")
|
||||
for _, fragment := range []string{
|
||||
"default-src 'self'",
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
|
||||
"script-src 'self' 'unsafe-inline'",
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data: blob:",
|
||||
"connect-src 'self' ws: wss:",
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# clone-manifest.sh — clone all repos listed in manifest.json into their
|
||||
# target directories. Replaces 33 hardcoded git-clone lines in Dockerfiles.
|
||||
# target directories. Replaces hardcoded git-clone lines in Dockerfiles.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/clone-manifest.sh <manifest.json> <ws-templates-dir> <org-templates-dir> <plugins-dir>
|
||||
#
|
||||
# Example (Docker build stage):
|
||||
# /scripts/clone-manifest.sh /manifest.json /workspace-configs-templates /org-templates /plugins
|
||||
# Requires: git, jq (lighter than python3 — ~2MB vs ~50MB in Alpine)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@ -15,26 +14,34 @@ WS_DIR="${2:?Missing workspace-templates dir}"
|
||||
ORG_DIR="${3:?Missing org-templates dir}"
|
||||
PLUGINS_DIR="${4:?Missing plugins dir}"
|
||||
|
||||
EXPECTED=0
|
||||
CLONED=0
|
||||
|
||||
clone_category() {
|
||||
local category="$1"
|
||||
local target_dir="$2"
|
||||
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
# Use python3 to parse JSON (jq may not be available in Docker)
|
||||
python3 -c "
|
||||
import json, sys
|
||||
with open('$MANIFEST') as f:
|
||||
m = json.load(f)
|
||||
for entry in m.get('$category', []):
|
||||
print(entry['name'], entry['repo'], entry.get('ref', 'main'))
|
||||
" | while read -r name repo ref; do
|
||||
local count
|
||||
count=$(jq -r ".${category} | length" "$MANIFEST")
|
||||
EXPECTED=$((EXPECTED + count))
|
||||
|
||||
local i=0
|
||||
while [ "$i" -lt "$count" ]; do
|
||||
local name repo ref
|
||||
name=$(jq -r ".${category}[$i].name" "$MANIFEST")
|
||||
repo=$(jq -r ".${category}[$i].repo" "$MANIFEST")
|
||||
ref=$(jq -r ".${category}[$i].ref // \"main\"" "$MANIFEST")
|
||||
|
||||
echo " cloning $repo -> $target_dir/$name (ref=$ref)"
|
||||
if [ "$ref" = "main" ]; then
|
||||
git clone --depth=1 -q "https://github.com/${repo}.git" "$target_dir/$name"
|
||||
else
|
||||
git clone --depth=1 -q --branch "$ref" "https://github.com/${repo}.git" "$target_dir/$name"
|
||||
fi
|
||||
CLONED=$((CLONED + 1))
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
# Strip .git dirs to save space
|
||||
@ -50,4 +57,10 @@ clone_category "org_templates" "$ORG_DIR"
|
||||
echo "==> Cloning plugins..."
|
||||
clone_category "plugins" "$PLUGINS_DIR"
|
||||
|
||||
echo "==> Done. $(find "$WS_DIR" "$ORG_DIR" "$PLUGINS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ') repos cloned."
|
||||
# Verify all repos were cloned
|
||||
if [ "$CLONED" -ne "$EXPECTED" ]; then
|
||||
echo "::error::Expected $EXPECTED repos but only cloned $CLONED — some clones failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Done. $CLONED/$EXPECTED repos cloned successfully."
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=68.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "molecule-ai-workspace-runtime"
|
||||
version = "0.1.0"
|
||||
description = "Molecule AI workspace runtime — shared infrastructure for all agent adapters"
|
||||
requires-python = ">=3.11"
|
||||
license = {text = "BSL-1.1"}
|
||||
readme = "README.md"
|
||||
# Don't pin heavy deps — each adapter adds its own
|
||||
dependencies = [
|
||||
"a2a-sdk[http-server]>=0.3.25",
|
||||
"httpx>=0.27.0",
|
||||
"uvicorn>=0.30.0",
|
||||
"starlette>=0.38.0",
|
||||
"websockets>=12.0",
|
||||
"pyyaml>=6.0",
|
||||
"langchain-core>=0.3.0",
|
||||
"opentelemetry-api>=1.24.0",
|
||||
"opentelemetry-sdk>=1.24.0",
|
||||
"opentelemetry-exporter-otlp-proto-http>=1.24.0",
|
||||
"temporalio>=1.7.0",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
molecule-runtime = "molecule_runtime.main:main_sync"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["."]
|
||||
include = ["molecule_runtime*"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"molecule_runtime" = ["py.typed"]
|
||||
Loading…
Reference in New Issue
Block a user