Skip to content

security: scrub leaked Composio API key from server/opencode.json - #16

Merged
Prat011 merged 2 commits into
masterfrom
zen/scrub-leaked-api-key-iyef66
May 3, 2026
Merged

security: scrub leaked Composio API key from server/opencode.json#16
Prat011 merged 2 commits into
masterfrom
zen/scrub-leaked-api-key-iyef66

Conversation

@zen-agent

@zen-agent zen-agent commented May 3, 2026

Copy link
Copy Markdown
Contributor

Description

A live Composio API key (ak_***REDACTED***) and tool router id (trs_***REDACTED***) were committed to server/opencode.json in commit c271660, exposing them publicly on this repo.

Note: Literal secret values are intentionally redacted in this PR description and comments. The actual values are still visible in the diff of this PR (since this PR removes them from the file) — see "the diff itself still shows the secret" in the section below for how to handle that.

Root cause: server/opencode.json is auto-generated at runtime by updateOpencodeConfig() in server/server.js (it writes the live MCP URL + headers from composio.create() into the file every time the backend starts). The file was added in aee23f2 with YOUR_API_KEY / YOUR_ROUTER_ID placeholders, but c271660 then overwrote those placeholders with a developer's actual session credentials and committed them. The file was never in .gitignore.

This PR:

  • Restores server/opencode.json to the original placeholder version (commit b581975).
  • git rm --cached server/opencode.json so future regenerations by the server don't re-leak (commit d5bfc31). .gitignore alone doesn't apply to already-tracked files.
  • Adds server/opencode.json to .gitignore so a future npm start won't re-commit a regenerated key.

Important — this PR alone is not enough

  1. Rotate the leaked key in the Composio dashboard immediately. It has been public on this branch for the entirety of its lifetime and may already have been scraped — consider it compromised regardless of any history rewrite.

  2. Git history still contains the secret at commit c271660 and in the diff of this PR (commit b581975). To remove it from history you need a git filter-repo (or BFG) pass followed by a force-push to master. Verified working pass — values to substitute are NOT in this PR; copy them from the c271660 commit page (or fetch them locally) into your scrub.txt:

    # Use the standalone script (no install needed):
    curl -fsSL https://raw.githubusercontent.com/newren/git-filter-repo/main/git-filter-repo \
      -o /usr/local/bin/git-filter-repo && chmod +x /usr/local/bin/git-filter-repo
    
    # Fresh clone, scrub, force-push.
    # Replace <LEAKED_API_KEY> and <LEAKED_ROUTER_ID> with the actual literal values
    # by reading them from commit c271660 in your local clone — do NOT paste them
    # into a public file or chat.
    git clone https://github.com/ComposioHQ/open-claude-cowork.git
    cd open-claude-cowork
    cat > /tmp/scrub.txt <<TXT
    <LEAKED_API_KEY>==>YOUR_API_KEY
    <LEAKED_ROUTER_ID>==>YOUR_ROUTER_ID
    TXT
    git filter-repo --replace-text /tmp/scrub.txt --force
    git remote add origin https://github.com/ComposioHQ/open-claude-cowork.git
    git push --force origin master

    This rewrites every commit SHA on master. Side effects:

  3. The diff of this PR itself shows the secret (commit b581975 has - "x-api-key": "<leaked key>"). Editing PR text won't fix that — the only way to make the diff safe is to force-push the git filter-repo rewrite first, then rebase this PR onto the new master. After the rewrite, the parent commit will already contain YOUR_API_KEY, so the rebased PR will only show the structural change (untracking + .gitignore entry) with no secret in the diff.

    Suggested order of operations:

    1. Rotate the leaked key in the Composio dashboard.
    2. Run the git filter-repo rewrite + force-push master (above).
    3. Rebase this branch onto the new master and force-push it — diff is then safe.
    4. Merge.

How did I test this PR

  • cat server/opencode.json after the change matches the original placeholder version added in aee23f2 (verified via git show aee23f2:server/opencode.json).
  • grep -rn "<leaked-key-substring>" . (with the actual literal locally) returns nothing in the post-PR working tree.
  • After this branch lands, git check-ignore -v server/opencode.json confirms the file is ignored, so future npm start runs that regenerate it won't re-leak the key.
  • Dry-run of the git filter-repo pass above on a fresh clone: git log --all -p -S "<leaked-key>" returns 0 hits and git log --all -p -S "<leaked-router-id>" returns 0 hits after the rewrite.

Triggered by: saransh@composio.dev | Source: slack
Session: https://zen-api-production-4c98.up.railway.app/dashboard/#/chat/zen-07ecd6f2e120

zen-agent and others added 2 commits May 3, 2026 03:52
The committed `server/opencode.json` contained a live Composio API key
(`ak_*`) and tool router id (`trs_*`) that leaked when commit c271660
overwrote the original placeholder values with a developer's actual
credentials. The file is generated at runtime by `updateOpencodeConfig()`
in `server/server.js` from the live `composio.create()` session, so it
should never have been committed.

- Restore the original `YOUR_API_KEY` / `YOUR_ROUTER_ID` placeholder
  contents (matching the version added in aee23f2) so a fresh checkout
  no longer ships a real key.
- Add `server/opencode.json` to `.gitignore` so future runs of the
  server don't re-commit a regenerated key.

The leaked key remains visible in git history at commit c271660 and
must be rotated/revoked in the Composio dashboard regardless of this
change. Scrubbing it from history requires a separate `git filter-repo`
+ force-push to `main` (handled outside this PR).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Co-authored-by: Saransh Rana <saransh@composio.dev>
.gitignore alone doesn't apply to tracked files — the server's
updateOpencodeConfig() in server/server.js overwrites the file with
live MCP credentials on every startup. With the file still tracked,
a developer running `npm start` would see the regenerated file as a
modification and could re-commit a real key.

`git rm --cached` removes it from version control entirely; the
placeholder content remains in the working tree as untracked, and
fresh clones get the file generated on first server start.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Co-authored-by: Saransh Rana <saransh@composio.dev>
@groovyBugify

Copy link
Copy Markdown
Contributor

@rohanprabhu can you please review the PR

@zen-agent

zen-agent commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

Post-PR status update

Step Result
/build-checks Repo not in known-roots profile; ran manual deterministic checks instead — JSON validity OK for server/opencode.json, server/package.json, package.json; secret string absent from working tree and from HEAD; git check-ignore -v server/opencode.json confirms the gitignore entry now applies (file is untracked).
/codex-review-loop LGTM iteration 1 — "The change removes a committed runtime config containing a live API key and ignores the generated replacement file. No regressions are evident from the diff." No actionable findings.
/gh-fix-ci-loop Repo has no GitHub Actions workflows and no commit-status checks configured (gh api repos/.../actions/workflows returns empty; gh api .../commits/<sha>/statuses returns empty). Nothing to wait on or fix.
Tests for changed behavior The change is a single-file untracking + a .gitignore entry; there's no production code path to assert against. Verified via grep that no remaining reference to the leaked key exists in the tree.
E2E/runtime checks N/A — no service to hit.
/address-pr-comments 0 inline review comments; 1 routing comment ("@rohanprabhu can you please review the PR") which is not actionable feedback.

Follow-up commit pushed during this loop: d5bfc31 security: untrack server/opencode.json so server.js writes don't recur.gitignore alone doesn't apply to already-tracked files, so without git rm --cached the next npm start would still leave server/opencode.json as a pending git modification with live credentials.

Still blocked on (out of scope for this PR):

  1. Rotate the leaked Composio API key (ak_***REDACTED***) in the Composio dashboard — the key has been public on master since commit c271660 and must be considered compromised.
  2. Force-push a git filter-repo-rewritten master to scrub the secret from history (commands in the PR description). This will require the 8 open external-contributor PRs to be rebased.

@Prat011
Prat011 merged commit 52335ef into master May 3, 2026
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.

3 participants