ci(security): task #146 lint — no GITEA/GITHUB token in tenant-writer paths #1565

Merged
hongming merged 3 commits from ci/146-lint-no-tenant-gitea-token into main 2026-05-19 19:19:29 +00:00
Member

Summary

Adds .gitea/workflows/lint-no-tenant-gitea-token.yml — a broader-pattern CI lint companion to mc#1555's RFC#523 Layer 3 (lint-forbidden-env-keys.yml).

Why

lint-forbidden-env-keys.yml catches QUOTED-literal hits of forbidden env names under workspace-server/internal/. RFC#523's threat-model statement is broader: "tenant workspaces MUST NOT receive operator-scope repo-host tokens." A future writer could route the value via a variable, a struct field, or a config key and slip past the existing literal scan; this lint catches those routing patterns at PR review time.

How

Co-occurrence scan: a file FIRES the lint only if it BOTH:

  • References a tenant-writer surface marker (one of: workspace_secrets, global_secrets, seedAllowList, /settings/secrets, envVars[, containerEnv, userData, provisionPayload, provisionContext)
  • Quotes a repo-host token name (one of: GITEA_TOKEN, GITEA_PAT, GITHUB_TOKEN, GITHUB_PAT, GH_TOKEN)

The co-occurrence requirement is the false-positive control — a file that just logs a missing-env message won't fire.

Dry-run result on current main

17 candidate files (tenant-writer surface), 0 hits → CLEAN. Lint will pass on PR open.

Drift contract

Keys list shared (subset) with lint-forbidden-env-keys.yml and Go source-of-truth workspace-server/internal/handlers/workspace_provision_forbidden_env.go — if RFC#523's deny set grows, update all three.

Test plan

  • CI green on this PR (the new lint included)
  • Synthetic offender (add os.Getenv("GITEA_TOKEN") next to envVars[ in a handler file) → lint FAILS
  • Confirm exempt-list works (touch workspace_provision_forbidden_env.go adding a new quoted literal — should remain CLEAN)

Refs


DO NOT MERGE — leave for 2-eye review.

## Summary Adds `.gitea/workflows/lint-no-tenant-gitea-token.yml` — a broader-pattern CI lint companion to mc#1555's RFC#523 Layer 3 (`lint-forbidden-env-keys.yml`). ## Why `lint-forbidden-env-keys.yml` catches QUOTED-literal hits of forbidden env names under `workspace-server/internal/`. RFC#523's threat-model statement is broader: "tenant workspaces MUST NOT receive operator-scope repo-host tokens." A future writer could route the value via a variable, a struct field, or a config key and slip past the existing literal scan; this lint catches those routing patterns at PR review time. ## How Co-occurrence scan: a file FIRES the lint only if it BOTH: - References a tenant-writer surface marker (one of: `workspace_secrets`, `global_secrets`, `seedAllowList`, `/settings/secrets`, `envVars[`, `containerEnv`, `userData`, `provisionPayload`, `provisionContext`) - Quotes a repo-host token name (one of: `GITEA_TOKEN`, `GITEA_PAT`, `GITHUB_TOKEN`, `GITHUB_PAT`, `GH_TOKEN`) The co-occurrence requirement is the false-positive control — a file that just logs a missing-env message won't fire. ## Dry-run result on current main 17 candidate files (tenant-writer surface), 0 hits → CLEAN. Lint will pass on PR open. ## Drift contract Keys list shared (subset) with `lint-forbidden-env-keys.yml` and Go source-of-truth `workspace-server/internal/handlers/workspace_provision_forbidden_env.go` — if RFC#523's deny set grows, update all three. ## Test plan - [ ] CI green on this PR (the new lint included) - [ ] Synthetic offender (add `os.Getenv("GITEA_TOKEN")` next to `envVars[` in a handler file) → lint FAILS - [ ] Confirm exempt-list works (touch `workspace_provision_forbidden_env.go` adding a new quoted literal — should remain CLEAN) ## Refs - RFC#523 / internal#523 - mc#1555 (3-layer guardrail merged) - Companion PR: molecule-controlplane (link in description after open) - Task #146 --- DO NOT MERGE — leave for 2-eye review.
core-be added 1 commit 2026-05-19 03:36:29 +00:00
ci(security): task #146 lint — no GITEA/GITHUB token in tenant-writer paths
CI / Canvas Deploy Reminder (pull_request) Blocked by required conditions
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 4s
CI / Shellcheck (E2E scripts) (pull_request) Failing after 6s
CI / all-required (pull_request) Failing after 1s
CI / Detect changes (pull_request) Successful in 14s
E2E API Smoke Test / detect-changes (pull_request) Successful in 8s
E2E Chat / detect-changes (pull_request) Successful in 10s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 8s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 4s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 12s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 4s
Lint no tenant GITEA/GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 15s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 33s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 26s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Failing after 2s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Failing after 25s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 1m39s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 9s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Failing after 1m22s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 8s
gate-check-v3 / gate-check (pull_request) Successful in 5s
security-review / approved (pull_request) Failing after 7s
qa-review / approved (pull_request) Failing after 7s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 4s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2s
sop-tier-check / tier-check (pull_request) Successful in 7s
E2E Chat / E2E Chat (pull_request) Successful in 3s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 3s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 5s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 7s
CI / Platform (Go) (pull_request) Successful in 7m0s
CI / Python Lint & Test (pull_request) Successful in 7m5s
CI / Canvas (Next.js) (pull_request) Successful in 8m42s
0468cee8a8
Adds .gitea/workflows/lint-no-tenant-gitea-token.yml — a broader-pattern
companion to mc#1555's RFC#523 Layer 3 lint (lint-forbidden-env-keys.yml).

The existing lint catches QUOTED-literal hits of forbidden env names
under workspace-server/internal/. This new lint catches the BROADER
class: any Go source that BOTH references a tenant-writer surface
(workspace_secrets / global_secrets / seedAllowList / /settings/secrets
/ envVars[ / containerEnv / userData / provisionPayload / provisionContext)
AND quotes a repo-host token name (GITEA_TOKEN / GITEA_PAT / GITHUB_TOKEN
/ GITHUB_PAT / GH_TOKEN).

The co-occurrence requirement is the false-positive control — a file
that just logs a missing-env message won't fire. Curated regex set
verified against current main: 17 candidate files, 0 hits (clean).

Exempt-list documents existing RFC#523 deny-set / silent-strip / pre-
existing persona-fallback paths. New entries require reviewer signoff.

Task #146 — RFC#523 (internal#523)
core-devops approved these changes 2026-05-19 03:58:48 +00:00
Dismissed
core-devops left a comment
Member

core-devops 5-axis review — APPROVE.

Correctness: 179-line workflow, same co-occurrence shape as cp#202 (companion lint in CP). SURFACE_PATTERN broader (adds provisionContext) to match molecule-core's writer shapes; FORBIDDEN_KEYS identical (GITEA_TOKEN, GITEA_PAT, GITHUB_TOKEN, GITHUB_PAT, GH_TOKEN). EXEMPT_FILES enumerates legitimate guard / silent-strip / persona-fallback files (workspace_provision_forbidden_env.go + test, provisioner.go + test, agent_git_identity.go, org_helpers.go, org.go, cp_provisioner.go). 0 violations in molecule-core — verified by the self-running lint on this PR being green.

Readability: drift contract spelled out (this lint + cp#202 + Go SOT workspace_provision_forbidden_env.go); per-exempt one-line justification.

Architecture: no paths: filter (required-status discipline); action pinned by full SHA; fetch-depth: 1 (sub-second scan).

Regex tightness: quoted-literal match (avoids comment false-positives); SURFACE_PATTERN curated against current main grep verification.

Security: catches the routing-pattern class (var/struct/config-key paths slip past literal-only scans). Open-source-template-friendly (no MOLECULE_-prefix literals).

Performance: sub-second scan.

Verdict: APPROVE.

core-devops 5-axis review — APPROVE. Correctness: 179-line workflow, same co-occurrence shape as cp#202 (companion lint in CP). SURFACE_PATTERN broader (adds `provisionContext`) to match molecule-core's writer shapes; FORBIDDEN_KEYS identical (GITEA_TOKEN, GITEA_PAT, GITHUB_TOKEN, GITHUB_PAT, GH_TOKEN). EXEMPT_FILES enumerates legitimate guard / silent-strip / persona-fallback files (`workspace_provision_forbidden_env.go` + test, `provisioner.go` + test, `agent_git_identity.go`, `org_helpers.go`, `org.go`, `cp_provisioner.go`). 0 violations in molecule-core — verified by the self-running lint on this PR being green. Readability: drift contract spelled out (this lint + cp#202 + Go SOT `workspace_provision_forbidden_env.go`); per-exempt one-line justification. Architecture: no `paths:` filter (required-status discipline); action pinned by full SHA; `fetch-depth: 1` (sub-second scan). Regex tightness: quoted-literal match (avoids comment false-positives); SURFACE_PATTERN curated against current main grep verification. Security: catches the routing-pattern class (var/struct/config-key paths slip past literal-only scans). Open-source-template-friendly (no MOLECULE_-prefix literals). Performance: sub-second scan. Verdict: APPROVE.
core-security approved these changes 2026-05-19 03:58:58 +00:00
Dismissed
core-security left a comment
Member

core-security 5-axis review — APPROVE.

Threat model coverage: RFC#523 — molecule-core workspace-server is where tenant workspaces are PROVISIONED. A repo-host token leaked into a tenant's containerEnv / userData / provisionPayload = whole-platform git-host escalation. This lint catches that class at PR review time even when the token name is routed via a variable rather than a quoted literal in the writer file (because the lint requires only co-occurrence within the file, not on the same line).

Regex covers the threat model:

  • FORBIDDEN_KEYS scoped to repo-host class {GITEA_TOKEN, GITEA_PAT, GITHUB_TOKEN, GITHUB_PAT, GH_TOKEN} — operator-fleet tokens covered by the broader lint-forbidden-env-keys.yml.
  • SURFACE_PATTERN covers writer shapes in this repo (workspace_secrets / global_secrets / seedAllowList / /settings/secrets / envVars[ / containerEnv / userData / provisionPayload / provisionContext).
  • Quoted-literal match (false-positive control).
  • *_test.go excluded from candidates.

No token still slipping through this lint's scope: 0 violations on molecule-core/main (verified by the lint self-running on this PR — green). The 6 EXEMPT_FILES enumerate legitimate guard / strip / fallback paths (RFC#523 L1 SOT + tests, silent-strip denylist + tests, persona-fallback writers, CP→platform admin auth). Each exempt has a one-line justification.

Drift contract: this lint + cp#202 + Go SOT workspace_provision_forbidden_env.go — must stay in sync, comment is explicit.

Verdict: APPROVE.

core-security 5-axis review — APPROVE. Threat model coverage: RFC#523 — molecule-core workspace-server is where tenant workspaces are PROVISIONED. A repo-host token leaked into a tenant's containerEnv / userData / provisionPayload = whole-platform git-host escalation. This lint catches that class at PR review time even when the token name is routed via a variable rather than a quoted literal in the writer file (because the lint requires only co-occurrence within the file, not on the same line). Regex covers the threat model: - FORBIDDEN_KEYS scoped to repo-host class {GITEA_TOKEN, GITEA_PAT, GITHUB_TOKEN, GITHUB_PAT, GH_TOKEN} — operator-fleet tokens covered by the broader `lint-forbidden-env-keys.yml`. - SURFACE_PATTERN covers writer shapes in this repo (workspace_secrets / global_secrets / seedAllowList / /settings/secrets / envVars\[ / containerEnv / userData / provisionPayload / provisionContext). - Quoted-literal match (false-positive control). - `*_test.go` excluded from candidates. No token still slipping through this lint's scope: 0 violations on molecule-core/main (verified by the lint self-running on this PR — green). The 6 EXEMPT_FILES enumerate legitimate guard / strip / fallback paths (RFC#523 L1 SOT + tests, silent-strip denylist + tests, persona-fallback writers, CP→platform admin auth). Each exempt has a one-line justification. Drift contract: this lint + cp#202 + Go SOT `workspace_provision_forbidden_env.go` — must stay in sync, comment is explicit. Verdict: APPROVE.
core-be added 1 commit 2026-05-19 03:59:59 +00:00
ci: re-trigger pipeline (transient shellcheck flake on prior run)
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 3s
CI / Detect changes (pull_request) Successful in 9s
Check migration collisions / Migration version collision check (pull_request) Successful in 24s
CI / Shellcheck (E2E scripts) (pull_request) Failing after 21s
CI / all-required (pull_request) Failing after 4s
E2E API Smoke Test / detect-changes (pull_request) Successful in 13s
E2E Chat / detect-changes (pull_request) Successful in 12s
CI / Canvas (Next.js) (pull_request) Successful in 3m42s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Has been skipped
Handlers Postgres Integration / detect-changes (pull_request) Successful in 4s
Harness Replays / detect-changes (pull_request) Successful in 4s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 10s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Failing after 47s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 48s
Lint no tenant GITEA/GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 3s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 33s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 5s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 4s
CI / Platform (Go) (pull_request) Successful in 5m6s
CI / Canvas Deploy Reminder (pull_request) Has been cancelled
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 31s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Failing after 3s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Failing after 25s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 8s
lint-mask-pr-atomicity / lint-mask-pr-atomicity (pull_request) Successful in 1m15s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
gate-check-v3 / gate-check (pull_request) Successful in 5s
qa-review / approved (pull_request) Failing after 4s
sop-checklist / na-declarations (pull_request) N/A: (none)
security-review / approved (pull_request) Successful in 3s
sop-checklist / all-items-acked (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Successful in 4s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Failing after 1m20s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m2s
CI / Python Lint & Test (pull_request) Successful in 6m57s
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m26s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Failing after 1m10s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
Harness Replays / Harness Replays (pull_request) Successful in 1s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 1s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2m0s
E2E Chat / E2E Chat (pull_request) Failing after 6m38s
23506ab751
core-devops approved these changes 2026-05-19 04:00:31 +00:00
Dismissed
core-devops left a comment
Member

core-devops re-approve at new head 23506ab7 (re-triggered for transient shellcheck flake on prior head 0468cee8). No code changes — empty commit only. Original 5-axis pass stands (id=4808): APPROVE.

core-devops re-approve at new head 23506ab7 (re-triggered for transient shellcheck flake on prior head 0468cee8). No code changes — empty commit only. Original 5-axis pass stands (id=4808): APPROVE.
core-security approved these changes 2026-05-19 04:00:34 +00:00
Dismissed
core-security left a comment
Member

core-security re-approve at new head 23506ab7 (empty re-trigger commit only, no security-relevant change). Original 5-axis pass stands (id=4809): APPROVE.

core-security re-approve at new head 23506ab7 (empty re-trigger commit only, no security-relevant change). Original 5-axis pass stands (id=4809): APPROVE.
core-devops added 1 commit 2026-05-19 04:13:04 +00:00
Merge branch 'main' of https://git.moleculesai.app/molecule-ai/molecule-core into ci/146-lint-no-tenant-gitea-token
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 7s
CI / Detect changes (pull_request) Successful in 6s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 22s
E2E API Smoke Test / detect-changes (pull_request) Successful in 7s
E2E Chat / detect-changes (pull_request) Successful in 7s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 8s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 5s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 8s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 6s
Lint no tenant GITEA/GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 5s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Failing after 28s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 1m11s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Successful in 4s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 21s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 8s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 3s
CI / Platform (Go) (pull_request) Successful in 5m19s
gate-check-v3 / gate-check (pull_request) Successful in 4s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 1m20s
security-review / approved (pull_request) Successful in 3s
sop-checklist / na-declarations (pull_request) N/A: (none)
qa-review / approved (pull_request) Failing after 5s
sop-checklist / all-items-acked (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Successful in 5s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Failing after 1m9s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 3s
E2E Chat / E2E Chat (pull_request) Successful in 3s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 2s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 1s
CI / Canvas (Next.js) (pull_request) Successful in 7m10s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / Python Lint & Test (pull_request) Successful in 7m6s
CI / all-required (pull_request) Successful in 6m44s
audit-force-merge / audit (pull_request) Successful in 7s
f2161bdad0
Member

core-devops: merged main into branch to pick up the # shellcheck disable=SC2034 annotations on tests/e2e/lib/peer_visibility_assert.sh + tests/e2e/test_peer_visibility_mcp_local.sh (introduced post-PR-branch in mc#1561). PR was branched before that landed → shellcheck failed against the rebased rule set even though this PR adds 0 shell files. Verified locally with the runner image: shellcheck exit=0 post-merge. Re-approving at new head.

core-devops: merged main into branch to pick up the `# shellcheck disable=SC2034` annotations on `tests/e2e/lib/peer_visibility_assert.sh` + `tests/e2e/test_peer_visibility_mcp_local.sh` (introduced post-PR-branch in mc#1561). PR was branched before that landed → shellcheck failed against the rebased rule set even though this PR adds 0 shell files. Verified locally with the runner image: shellcheck exit=0 post-merge. Re-approving at new head.
core-devops approved these changes 2026-05-19 04:13:17 +00:00
core-devops left a comment
Member

core-devops re-approve at new head f2161bda (main-merge to pick up post-1561 shellcheck disables; no functional change to the lint workflow). Original 5-axis pass stands (id=4808). APPROVE.

core-devops re-approve at new head f2161bda (main-merge to pick up post-1561 shellcheck disables; no functional change to the lint workflow). Original 5-axis pass stands (id=4808). APPROVE.
core-security approved these changes 2026-05-19 04:13:20 +00:00
core-security left a comment
Member

core-security re-approve at new head f2161bda (main-merge to clear unrelated shellcheck red; no security-relevant change). Original 5-axis pass stands (id=4809). APPROVE.

core-security re-approve at new head f2161bda (main-merge to clear unrelated shellcheck red; no security-relevant change). Original 5-axis pass stands (id=4809). APPROVE.
core-qa approved these changes 2026-05-19 19:16:55 +00:00
core-qa left a comment
Member

QA team APPROVE for RFC#523 CI lint workflow (lint-no-tenant-gitea-token.yml). Read-only static lint, scoped to .gitea/workflows; no runtime side-effects; gate is fail-closed by design. Reviewed at head f2161bdad0.

QA team APPROVE for RFC#523 CI lint workflow (lint-no-tenant-gitea-token.yml). Read-only static lint, scoped to .gitea/workflows; no runtime side-effects; gate is fail-closed by design. Reviewed at head f2161bdad002eed25fa373a4081aea72cb6ed045.
Owner

/qa-recheck

/qa-recheck
hongming merged commit c7b523a0a9 into main 2026-05-19 19:19:29 +00:00
Sign in to join this conversation.
5 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#1565