Skip to content

v0.6.5 — security hardening, Python-first Getting Started, architecture cleanup#32

Merged
ryanntannn merged 4 commits into
masterfrom
release/v0.6.5-security-hardening
Jun 28, 2026
Merged

v0.6.5 — security hardening, Python-first Getting Started, architecture cleanup#32
ryanntannn merged 4 commits into
masterfrom
release/v0.6.5-security-hardening

Conversation

@ryanntannn

Copy link
Copy Markdown
Contributor

Summary

Bundles the v0.6.5 release: a security pass that closes the independent audit findings, the Python-first Getting Started rewrite, and an audit-driven architecture cleanup.

1. Getting Started leads with Python (docs(portal))

Python SDK is now the primary path; REST/curl is demoted to a "not using Python?" fallback. Step 3 shows the same run two ways — a Jupyter notebook (cells, %pip, inline-rendered metrics) and a runnable script (run_simulation.py, __main__ guard, SYNERGY_API_KEY from env).

2. Security remediation (fix(security)) — closes docs/SECURITY_AUDIT.md #1#8

# Sev Fix
1 High Object-level authz on all read endpoints (404, no existence leak) + tests
2 High Dev-login backdoor is fail-closed (explicit PORTAL_DEV_LOGIN=1 only); compose binds portal to 127.0.0.1
3 High apiserver off the operator's cluster-wide SA (base → no-RBAC default; prod IRSA overlay untouched)
4 Med Live sp_live_ token scrubbed from the sample notebook (historical tokens still need rotation — see below)
5 Med Server-side s3://-into-the-right-bucket ref validation (blocks file://, bare paths, cross-bucket)
6 Med Portal refuses to start in prod with an unset/placeholder BETTER_AUTH_SECRET (runtime guard, not build)
7 Med Runner runs as non-root UID 10001
8 Low Batch variant cap (SP_MAX_BATCH_VARIANTS, default 10000)

Also drops a production magic-link bearer token from an auth log line.

3. Architecture cleanup (refactor)

24 safe, behavior-preserving items from a per-subsystem deep-module/domain-language audit: dead-code removal, SQL projection + scan-helper dedup, a pagination helper, controller renames, portal "jobs"→"Simulation" vocabulary alignment, runner heartbeatrenew_lease + a failed-upload artifact-URI correctness fix, SDK dedup + docstring fixes. 17 larger redesigns were deferred to follow-ups (see below).

4. Release (chore)

Bump portal/runner/SDK to 0.6.5 and pin the prod overlay to the 0.6.5 images the release CI publishes.

Verification

  • go build/vet/test ✅ · portal tsc ✅ · runner + SDK pytest ✅ · manifest YAML ✅ · gofmt
  • Adversarial multi-agent security review: GO — all findings closed, no bypass/regression/new vuln.

⚠️ Required operator follow-up (cannot be done in-repo)

  • Rotate credentials: the two exposed sp_live_… API tokens (one in git history, one that was in the working tree) and the Gmail SMTP app-password. The code scrub does not revoke them.
  • Remove gmail.com from any production ALLOWED_EMAIL_DOMAINS.

Deferred follow-ups (tracked, not in this PR)

Audit #9 (gateway rate limiting), #10 (content-hash cache oracle), #11 (TLS posture — owned by SynergyPlusIAC); the EligibleQueued CRD field removal (needs controller-gen, unavailable here); and 17 moderate architecture redesigns (typed response DTOs, single-source cache-resolution policy, KEDA ScaledObject watch, etc.).

🤖 Generated with Claude Code

https://claude.ai/code/session_01NPhdd5pXcgjdcJA9ZSdSzF

Ryan Tan bdgv224 and others added 4 commits June 28, 2026 11:28
Restructure the portal Getting Started page to lead with the Python SDK
and demote REST/curl to a "not using Python?" fallback. Step 3 now shows
the same run two ways — a Jupyter notebook (cells, %pip, inline-rendered
metrics) and a runnable script (run_simulation.py with a __main__ guard
and SYNERGY_API_KEY from the env).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPhdd5pXcgjdcJA9ZSdSzF
Closes the High/Medium findings in docs/SECURITY_AUDIT.md before release:

- #1 IDOR: add object-level ownership checks to all read handlers
  (GetSimulation/GetResult/GetBatch/ListBatchSimulations) — a non-owner
  now gets the same 404 as a missing object (no existence leak),
  mirroring handleListArtifacts. New handlers_authz_test.go.
- #2 dev-login backdoor: devLoginEnabled is now fail-closed (explicit
  PORTAL_DEV_LOGIN=1 only; no NODE_ENV default). Local compose opts in
  but binds the portal to 127.0.0.1.
- #3 apiserver SA: base no longer shares the operator's cluster-wide SA
  (uses the no-RBAC default SA); prod keeps its scoped IRSA SA via the
  overlay (untouched).
- #4 leaked credential: scrub the live sp_live_ token from the sample
  notebook (placeholder + generic localhost URL, cleared outputs).
  NOTE: the historical tokens still require server-side rotation.
- #5 ref validation: validateInputRef rejects model/weather refs that
  aren't s3:// into the kind's bucket — blocks file://, bare paths, and
  cross-bucket reads, wired into both create paths (incl. per-variant).
- #6 auth secret: portal refuses to start in production with an unset /
  placeholder BETTER_AUTH_SECRET (runtime instrumentation, not build);
  compose given a real local secret.
- #7 runner: run as non-root UID 10001 (after the privileged build steps).
- #8 batch cap: bound variants per batch (SP_MAX_BATCH_VARIANTS, 10000).

Also drops a production bearer-token (magic-link) from an auth log line.
Verified: go build/vet/test, portal tsc, manifest YAML validation, and an
adversarial multi-agent review (GO, no bypass/regression).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPhdd5pXcgjdcJA9ZSdSzF
Safe, behavior-preserving cleanups from a per-subsystem architecture
audit (deep-module / domain-language / dedup lens):

- Go: drop dead apiserver Config.PerUserCap; extract shared SELECT
  column lists + scan helpers in store; a pageParams() pagination helper;
  InsertSimulation returns (id, err) (state was always echoed);
  rename caps()->orDefault() and collapse deployName/scaledObjectName
  into resourceName() in the controller; fix a stale presign docstring.
- Portal: align dashboard "jobs" vocabulary to the Simulation ubiquitous
  language; delete dead env.apiBaseUrl and api-keys last4(); rename the
  misleading last4 field to hashTail; single-source the created key name;
  hoist email-domain allow-list checks into one pure helper; add
  getRequiredPortalUser() to replace non-null user assertions.
- Runner: delete dead Database.lookup_result; rename heartbeat()->
  renew_lease() and _reconnect()->reconnect() to match the domain;
  don't record an artifact_uri when the upload failed; drop stale
  "worker" provenance now that worker/ is gone.
- SDK: delete dead read_artifact_text from the StorageBackend; de-dup
  Variant model resolution; fix docstrings that wrongly required boto3
  for the default presigned path.

Verified: go build/vet/test, portal tsc, runner + SDK pytest all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPhdd5pXcgjdcJA9ZSdSzF
Bump portal, runner, and SDK to 0.6.5 and pin the prod kustomize overlay
(operator/apiserver/portal/runner images) to the 0.6.5 tag the release
CI publishes. The overlay also carries the apiserver SA comment update
from the audit #3 split.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPhdd5pXcgjdcJA9ZSdSzF
@ryanntannn
ryanntannn merged commit 8fc980f into master Jun 28, 2026
@ryanntannn
ryanntannn deleted the release/v0.6.5-security-hardening branch June 28, 2026 03:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant