Skip to content

Security

forkctl holds a GitHub token and operates on repos on your behalf. We take that seriously.

AssetRiskMitigation
GITHUB_TOKENLeaked via logs, error messages, telemetryNever logged or echoed. Scrubbed from error output. No telemetry.
Tool inputs (from LLM)Prompt-injected paths, owner/repo strings, branch namesAll inputs validated through Zod schemas. No shell interpolation of inputs.
Destination repoWrong owner / wrong visibility on createOperations require explicit destination owner; visibility is opt-in.
Source repoAccidental writes during make_forkableDefault mode is plan. pr mode requires explicit flag.
Sync conflictsForce-overwrite of fork branchessync never force-pushes. Conflicts surface as propose_sync_pr.
Local state DBOperation history with org/repo metadataStored in OS user-state dir, not in repo. Never transmitted.

forkctl will never:

  • Force-push to any branch
  • Delete a repository
  • Delete a branch
  • Skip git hooks
  • Send telemetry, analytics, or any outbound network call other than to the configured GitHub API
  • Print or persist the GITHUB_TOKEN value

These aren’t honor-system promises — they’re absent code paths. There is no force: true flag anywhere in the source. There is no telemetry library imported.

Your GITHUB_TOKEN is read once when an Octokit client is built (src/lib/github.ts). From there:

  • It’s only sent to the configured GITHUB_API_URL (defaults to https://api.github.com).
  • Error messages from the GitHub API are run through scrubToken() before being shown to the user — any ghp_…, gho_…, ghu_…, ghs_…, ghr_…, or github_pat_… pattern in error text is replaced with [redacted-token].
  • The token never appears in the audit log. The redactInput() function in src/lib/audit.ts strips known sensitive keys and inline PAT patterns before persisting.

Sensitive keys redacted at write time:

token, GITHUB_TOKEN, password, secret, apiKey, api_key

Inline patterns redacted in any string value:

PatternDetects
(ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{16,}GitHub PATs

The drift scanner (forkctl_scan_drift) extends this list to AWS access keys (AKIA…), OpenAI keys (sk-…), and Google API keys (AIza…). When secrets are detected in scanned files, the evidence field is set to literal "<redacted>" — the secret value itself is never returned.

Every tool input passes through a Zod schema before reaching its handler. Bad input never reaches GitHub — it’s rejected with INVALID_INPUT and a hint listing the offending paths.

Repo references are pinned to ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ — no schemes, no whitespace, no traversal characters.

make_forkable writes to the source repo (which you may not own). To prevent accidents:

  • Default mode is plan — produces a patch plan and writes nothing.
  • mode=pr is explicit opt-in. Even then, forkctl opens a PR — it never commits to the default branch.
  • The PR description always discloses that the change was generated by forkctl and lists the blockers being fixed.

Open a private security advisory. Do not file public issues for security problems.

We aim to acknowledge within 72 hours and ship a fix or mitigation within 14 days for high-severity reports.

forkctl depends on a small, intentional set of packages:

  • @modelcontextprotocol/sdk — MCP server transport
  • @octokit/rest + @octokit/request-error — GitHub API client
  • better-sqlite3 — local state DB
  • commander — CLI parser
  • env-paths — OS user-state directory resolution
  • zod + zod-to-json-schema — input validation + MCP tool descriptors

npm audit is part of npm ci in CI. The current snapshot reports zero vulnerabilities.