fix: add slug validation to prevent SSRF (OFFSEC-006) #930

Merged
devops-engineer merged 1 commits from fix/offsec-006-slug-validation into main 2026-05-14 02:22:15 +00:00
Member

OFFSEC-006: SSRF + Bearer token exfiltration in promote-tenant-image.sh

Severity: HIGH

Four URL construction points used unsanitised tenant slug in URL paths and
subdomains, allowing an attacker to craft --tenants values that exfiltrate
the CP Bearer token or redirect to an attacker-controlled domain.

Vulnerable injection points

Function URL pattern Attack
cp_redeploy_tenant /cp/admin/tenants/$slug/redeploy a?url=https://evil.com
tenant_buildinfo https://$slug.moleculesai.app/buildinfo evil.com@legit
tenant_health https://$slug.moleculesai.app/health (same)
resolve_tenant_instance_id /cp/admin/tenants/$slug a?url=https://evil.com

Fix

  • Add validate_slug() with regex ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$
    — called up-front in main() before any network I/O, and as
    defense-in-depth inside every function that interpolates slug into a URL.
  • Exit 64 on invalid slug.
  • Also fixes a latent promote_rc=1 bug (see commit message).

Test coverage

  • Test 9: 8 attack-slab variants rejected with exit 64 (?, &, @, /, , space, etc.)
  • Test 10: 6 valid slugs accepted (chloe-dong, ab, a, etc.)
  • All 54 tests pass.

Reviewers

cc @core-security

🤖 Generated with Claude Code

## OFFSEC-006: SSRF + Bearer token exfiltration in promote-tenant-image.sh **Severity: HIGH** Four URL construction points used unsanitised tenant slug in URL paths and subdomains, allowing an attacker to craft `--tenants` values that exfiltrate the CP Bearer token or redirect to an attacker-controlled domain. ### Vulnerable injection points | Function | URL pattern | Attack | |---|---|---| | `cp_redeploy_tenant` | `/cp/admin/tenants/$slug/redeploy` | `a?url=https://evil.com` | | `tenant_buildinfo` | `https://$slug.moleculesai.app/buildinfo` | `evil.com@legit` | | `tenant_health` | `https://$slug.moleculesai.app/health` | (same) | | `resolve_tenant_instance_id` | `/cp/admin/tenants/$slug` | `a?url=https://evil.com` | ### Fix - Add `validate_slug()` with regex `^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$` — called up-front in `main()` before any network I/O, and as defense-in-depth inside every function that interpolates slug into a URL. - Exit 64 on invalid slug. - Also fixes a latent `promote_rc=1` bug (see commit message). ### Test coverage - **Test 9**: 8 attack-slab variants rejected with exit 64 (?, &, @, /, \, space, etc.) - **Test 10**: 6 valid slugs accepted (chloe-dong, ab, a, etc.) - All 54 tests pass. ### Reviewers cc @core-security 🤖 Generated with [Claude Code](https://claude.ai/claude-code)
core-devops added 1 commit 2026-05-14 02:18:45 +00:00
fix: add slug validation to prevent SSRF (OFFSEC-006)
sop-checklist / all-items-acked (pull_request) injected
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 17s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 55s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 39s
CI / Detect changes (pull_request) Successful in 58s
E2E API Smoke Test / detect-changes (pull_request) Successful in 56s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 49s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 28s
qa-review / approved (pull_request) Successful in 13s
gate-check-v3 / gate-check (pull_request) Successful in 33s
sop-checklist-gate / gate (pull_request) Successful in 13s
security-review / approved (pull_request) Successful in 15s
sop-tier-check / tier-check (pull_request) Successful in 20s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m17s
audit-force-merge / audit (pull_request) Successful in 10s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Successful in 1m42s
CI / Platform (Go) (pull_request) Successful in 7s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 6s
CI / Canvas (Next.js) (pull_request) Successful in 7s
CI / Python Lint & Test (pull_request) Successful in 6s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 8s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 8s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 7s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 22s
CI / Canvas Deploy Reminder (pull_request) Successful in 8s
CI / all-required (pull_request) Successful in 3s
9153a2e464
OFFSEC-006 (HIGH): promote-tenant-image.sh interpolated raw --tenants
slug into URL paths and subdomains without sanitisation.  Four injection
points were vulnerable:

  • cp_redeploy_tenant  (line 193): /cp/admin/tenants/$slug/redeploy
  • tenant_buildinfo    (line 209): https://${slug}.moleculesai.app/buildinfo
  • tenant_health      (line 217): https://${slug}.moleculesai.app/health
  • resolve_tenant_instance_id (line 263): /cp/admin/tenants/$slug

Attack vectors:
  --tenants 'a?url=https://evil.com'  → curl splits on ? as query separator
  --tenants 'evil.com@legitimate'    → subdomain takeover via @

Fix:
  • Add validate_slug() function with regex ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$
    before any URL interpolation.  Exit 64 on invalid slug.
  • Call validate_slug() in main() before any operations (up-front guard).
  • Add defense-in-depth calls inside cp_redeploy_tenant, tenant_buildinfo,
    tenant_health, resolve_tenant_instance_id, redeploy_tenant,
    verify_tenant, and the rollback loop.
  • Also fix a latent promote_rc=1 bug where `cmd || promote_rc=1` inside
    `set -e` returned exit 1 and triggered early script exit instead of
    setting the variable.  Replaced with `if ! cmd; then promote_rc=1; fi`.

Test additions (test-promote-tenant-image.sh):
  • Test 9:  8 invalid slug variants rejected with exit 64 (?, &, @, /, \, space, etc.)
  • Test 10: 6 valid slugs accepted (chloe-dong, ab, a, etc.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
hongming added the tier:low label 2026-05-14 02:19:50 +00:00
Member

/sop-ack comprehensive-testing

/sop-ack comprehensive-testing
Author
Member

cc @core-security — OFFSEC-006 fix, please review. All 54 tests pass.

cc @core-security — OFFSEC-006 fix, please review. All 54 tests pass.
Member

/sop-ack local-postgres-e2e

/sop-ack local-postgres-e2e
Author
Member

cc @core-security — OFFSEC-006 fix, please review. All 54 tests pass.

cc @core-security — OFFSEC-006 fix, please review. All 54 tests pass.
Member

/sop-ack staging-smoke

/sop-ack staging-smoke
Author
Member

cc @core-security — OFFSEC-006 fix, please review. All 54 tests pass.

cc @core-security — OFFSEC-006 fix, please review. All 54 tests pass.
Member

/sop-ack five-axis-review

/sop-ack five-axis-review
Member

/sop-ack memory-consulted

/sop-ack memory-consulted
core-qa approved these changes 2026-05-14 02:21:54 +00:00
core-qa left a comment
Member

LGTM — SSRF slug validation fix on promote-tenant-image.sh; HIGH severity OFFSEC-006; tier:low targeted patch

LGTM — SSRF slug validation fix on promote-tenant-image.sh; HIGH severity OFFSEC-006; tier:low targeted patch
devops-engineer merged commit 369b2d3690 into main 2026-05-14 02:22:15 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#930