diff --git a/AGENTS.md b/AGENTS.md index f07f9b6e0..ac89841bb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -171,9 +171,32 @@ Use `context_history` to navigate the edit DAG: ## Documentation Files - Log all bugs in root `BUGS.md`, not per-package. Do not create `packages/*/BUGS.md`. -- Tracking docs (`PLAN.md`, `STATUS.md`, `WHAT_WE_DID.md`, `DO_NEXT.md`, `BUGS.md`) live at repo root. +- Tracking docs (`PLAN.md`, `STATUS.md`, `WHAT_WE_DID.md`, `DO_NEXT.md`, `BUGS.md`, `GAP_ANALYSIS.md`, `UPSTREAM_STATUS.md`) live at repo root. - Do not create tracking/status markdown files inside `packages/`. +## Phase Methodology + +Work follows the phased plan in `PLAN.md`. Each phase has exit criteria. + +**Before starting a phase:** +1. Read `PLAN.md` to understand the phase scope and exit criteria +2. Read `GAP_ANALYSIS.md` to understand current state vs target +3. Read `DO_NEXT.md` for immediate actions +4. Read `BUGS.md` for any open issues that might affect the phase + +**After completing a phase:** +1. Update `BUGS.md` — close fixed issues, add any new ones found +2. Update `STATUS.md` — refresh metrics (test count, error count, phase progress) +3. Update `WHAT_WE_DID.md` — add summary of work done +4. Update `GAP_ANALYSIS.md` — mark completed gaps, update current state +5. Update `DO_NEXT.md` — point to the next phase +6. Update `PLAN.md` — mark phase as complete, note PR number + +**When starting a new session:** +1. Review `STATUS.md` and `DO_NEXT.md` to orient +2. Verify tests pass and tsgo has 0 errors before making changes +3. Follow the current phase in `PLAN.md` + ## Type Checking - Always run `bun typecheck` from package directories (e.g., `packages/opencode`), never `tsc` directly. diff --git a/BUGS.md b/BUGS.md index 8840acb2f..302ba835a 100644 --- a/BUGS.md +++ b/BUGS.md @@ -4,7 +4,19 @@ All bugs tracked here. Do not create per-package bug files. --- -## Open (0) +## Open — Security (5) + +From upstream security audit. See [docs/SECURITY_AUDIT.md](docs/SECURITY_AUDIT.md) for full details. + +| # | Issue | Sev | Location | Upstream | Notes | +| --- | ----- | ---- | -------- | -------- | ----- | +| S1 | `Filesystem.contains()` symlink bypass | Crit | `util/filesystem.ts` | [#8313](https://github.com/anomalyco/opencode/issues/8313) | Lexical check only, no `realpathSync()` | +| S2 | `exec()` command injection in github.ts | High | `cli/cmd/github.ts` | [#17350](https://github.com/anomalyco/opencode/issues/17350) | Use `spawn` with arg array | +| S3 | Untrusted `.opencode/` autoloading (MCP + plugins) | High | `mcp/`, `plugin/` | [#6361](https://github.com/anomalyco/opencode/issues/6361), [#7163](https://github.com/anomalyco/opencode/issues/7163) | No trust prompt | +| S4 | Server unauthenticated on non-loopback | Med | `server/server.ts` | [#10973](https://github.com/anomalyco/opencode/issues/10973) | Auth skips when no password | +| S5 | Read tool exposes .env files (ignores .gitignore) | Med | `tool/read.ts` | [#12196](https://github.com/anomalyco/opencode/issues/12196) | No sensitive file deny-list | + +## Open — Bugs (0) _No open bugs._ diff --git a/DO_NEXT.md b/DO_NEXT.md index 48e224799..84ba6755d 100644 --- a/DO_NEXT.md +++ b/DO_NEXT.md @@ -1,29 +1,23 @@ # Frankencode — Do Next -## Priority 1: Zod v3 → v4 Migration +Follow the phases in [PLAN.md](PLAN.md). Security first. -Single site: `server/routes/experimental.ts:91` uses `zodToJsonSchema(... as any)`. Replace `zod-to-json-schema` with Zod v4's built-in `z.toJSONSchema()`. Also audit `hono-openapi` resolver/validator calls for v3 compatibility. +## Immediate: Phase 1 — Security Fixes -See `PLAN.md` for full site list. +1. Fix S1: `Filesystem.contains()` — add `realpathSync()` in `src/util/filesystem.ts` +2. Fix S2: `exec()` → `spawn()` in `src/cli/cmd/github.ts` +3. Fix S3: workspace trust prompt for `.opencode/` MCP and plugins +4. Fix S4: server auth for non-loopback binding +5. Fix S5: sensitive file deny-list for read tool +6. Evaluate upstream security PRs: #10763, #10974, #14581 -## Priority 2: Upstream Re-sync +## Then: Phase 2 — High-Priority Upstream Fixes -Upstream (`anomalyco/opencode`) has diverged since our last rebase. Effect-ification PRs are landing (7+ still open). Strategy: cherry-pick applicable fixes first, then full rebase. +Cherry-pick 8 commits from vouched contributors. See [PLAN.md](PLAN.md) Phase 2. -High-conflict areas: `session/prompt.ts`, `session/message-v2.ts`, `effect/`, `skill/skill.ts`. +## See Also -## Backlog: Testing - -- [ ] Unit tests for CAS (store, get, dedup) -- [ ] Unit tests for filterEdited (hidden parts stripped) -- [ ] Unit tests for EditGraph (commit chain, log walk, checkout) -- [ ] Unit tests for SideThread CRUD -- [ ] Unit tests for ContextEdit validation (ownership, budget, recency) -- [ ] Unit tests for lifecycle sweeper (discardable auto-hide, ephemeral auto-externalize) -- [ ] TUI dialog tests (9 remaining: command, provider, session-rename, etc.) -- [ ] TUI interaction tests (dialog-select keyboard nav, prompt input, command palette) - -## Backlog: Features - -- [ ] TUI rendering of edit indicators (hidden/replaced/annotated parts) -- [ ] CAS garbage collection improvements (size limits, age-based cleanup) +- [PLAN.md](PLAN.md) — full 6-phase roadmap +- [GAP_ANALYSIS.md](GAP_ANALYSIS.md) — current vs target state per phase +- [BUGS.md](BUGS.md) — security issues S1-S5 +- [UPSTREAM_STATUS.md](UPSTREAM_STATUS.md) — upstream commit/PR catalogue diff --git a/GAP_ANALYSIS.md b/GAP_ANALYSIS.md index ffda75459..625c2b2e3 100644 --- a/GAP_ANALYSIS.md +++ b/GAP_ANALYSIS.md @@ -2,21 +2,89 @@ **Date:** 2026-03-21 -## Goals — All Complete +## Current State → Target State by Phase -| Goal | Status | -|------|--------| -| Fix all bugs (B1-B52) | Done (51 fixed, 1 deferred) | -| Eliminate weak typing | Done (20 documented exceptions remain) | -| Architecture documentation | Done (8 docs in `docs/`) | +### Phase 1: Security Fixes -## Remaining Gaps +| Area | Current | Target | +|------|---------|--------| +| `Filesystem.contains()` | Lexical check only — symlinks escape project | `realpathSync()` + lexical check | +| `exec()` in github.ts | Shell string execution | `spawn()` with argument array | +| `.opencode/` autoloading | No trust prompt — MCP/plugins run on clone | Trust prompt before loading workspace configs | +| Server auth | Skips auth when no password set | Require password for non-loopback, or bind loopback only | +| Read tool | No .gitignore check — exposes .env | Sensitive file deny-list | +| Security bugs in BUGS.md | 5 open (S1-S5) | 0 open | -| Gap | Priority | Notes | -|-----|----------|-------| -| Zod v3→v4 migration | Medium | 1 site (`zodToJsonSchema`), see `PLAN.md` | -| Upstream re-sync | Medium | Upstream diverging, Effect PRs landing | -| Unit test coverage for Frankencode modules | Low | CAS, EditGraph, SideThread, ContextEdit, sweeper | -| TUI dialog/interaction tests | Low | 9 dialog + keyboard nav tests | +### Phase 2: High-Priority Upstream Fixes -See `DO_NEXT.md` for actionable items. +| Area | Current | Target | +|------|---------|--------| +| thinkingConfig | Set for all models | Only set for models with reasoning capability | +| SIGHUP handler | Missing | Restored (1-line fix from Dax) | +| Default timeout | Incorrect value | Fixed (2-line fix from Ariane Emory) | +| Event route processing | Synchronous | Queued to prevent backpressure | +| Forked prompt attachments | File parts lost | Preserved | +| Tagged error messages | Lost during processing | Preserved | +| Truncate permission | Import cycle | Broken cycle | +| Chunk timeout | Enabled by default | Disabled by default | + +### Phase 3: Quality + OpenTUI + +| Area | Current | Target | +|------|---------|--------| +| OpenTUI version | 0.1.88 | 0.1.90 | +| Agent/skill ordering | Non-deterministic | Stable ordering | +| apply_patch | Not in EDIT_TOOLS filter | Included | +| Provider.list() type | Loose | `Record` | +| Prompt schema debugging | No logs | Validation debug logs | + +### Phase 4: Community Fixes + Features + +| Area | Current | Target | +|------|---------|--------| +| Retry backoff | Unbounded exponential | Capped at 30s | +| 429 retry | Respects non-retryable flag | Retries transient 429s | +| Lone surrogates | 400 errors from providers | Stripped before sending | +| Empty content blocks | Sent to providers | Filtered out | +| LSP clients | Accumulate (memory leak) | Dead clients removed | +| Memory during prompting | Full scan | Lazy boundary scan + windowing | +| Subagent plan escape | Possible | Prevented | +| TUI permissions | Manual per-tool | Auto-accept mode option | + +### Phase 5: Tests + +| Area | Current | Target | +|------|---------|--------| +| filterEdited tests | 0 | Comprehensive (hidden, empty, synthetic placeholder) | +| ContextEdit validation | 0 | Ownership, budget, recency, privileged agents | +| TUI dialog tests | 0 | 9 dialogs covered | +| TUI interaction tests | 0 | Keyboard nav, prompt input, command palette | + +### Phase 6: Effect Behavioral Analysis + +| Area | Current | Target | +|------|---------|--------| +| Upstream Effect behaviors | Not analyzed | Each PR reviewed, valuable behaviors reimplemented | +| SkillService capabilities | Content cache only | + any new capabilities from upstream | +| FileTimeService | No semaphore | Evaluate if semaphore prevents races | +| VcsService | Our version | + HEAD filter fix if not already applied | + +--- + +## Permanently Out of Scope + +- Desktop/Electron app (never shipping) +- Bun→Node portability refactors (Bun-only target) +- Zen platform changes +- Web app UI (low priority, evaluate case-by-case) + +--- + +## Cross-references + +- [PLAN.md](PLAN.md) — phase details and exit criteria +- [BUGS.md](BUGS.md) — security issues S1-S5 + bug tracker +- [DO_NEXT.md](DO_NEXT.md) — immediate next actions +- [STATUS.md](STATUS.md) — current metrics +- [UPSTREAM_STATUS.md](UPSTREAM_STATUS.md) — full upstream catalogue +- [docs/SECURITY_AUDIT.md](docs/SECURITY_AUDIT.md) — CVEs and vulnerability details diff --git a/PLAN.md b/PLAN.md index bd44fa6e4..e8cfdc594 100644 --- a/PLAN.md +++ b/PLAN.md @@ -2,65 +2,137 @@ > **Frankencode** is a fork of [OpenCode](https://github.com/anomalyco/opencode) (`dev` branch) that adds context editing, content-addressable storage, and an edit graph. -**Status (2026-03-21):** Features implemented. 51 bugs fixed, 0 open. Type safety audit complete (20 documented `any` remain). 1448 tests passing, 0 tsgo errors. See `STATUS.md`. +**Status (2026-03-21):** All features implemented. 51 bugs fixed, 5 security issues open. Type safety complete. Zod v4 migrated. 1473 tests passing, 0 tsgo errors. + +**Upstream divergence:** 23 ahead, 162 behind, ~195 open PRs catalogued. See [UPSTREAM_STATUS.md](UPSTREAM_STATUS.md). + +--- + +## Phase 1: Security Fixes (CRITICAL) + +Fix the 5 security issues documented in [BUGS.md](BUGS.md) and [docs/SECURITY_AUDIT.md](docs/SECURITY_AUDIT.md). + +| # | Issue | Severity | Fix | +|---|-------|----------|-----| +| S1 | `Filesystem.contains()` symlink bypass | CRITICAL | Add `realpathSync()` before lexical check in `util/filesystem.ts` | +| S2 | `exec()` command injection in github.ts | HIGH | Replace `exec(cmd)` with `spawn(["open", url])` | +| S3 | Untrusted `.opencode/` autoloading | HIGH | Add workspace trust prompt before loading MCP/plugins | +| S4 | Server unauthenticated on non-loopback | MED | Require password or bind loopback-only by default | +| S5 | Read tool exposes .env files | MED | Add sensitive file deny-list | + +Also evaluate upstream security PRs: +- [#10763](https://github.com/anomalyco/opencode/pull/10763) — CVE-2025-58179 fix +- [#10974](https://github.com/anomalyco/opencode/pull/10974) — TUI server exposure guard +- [#14581](https://github.com/anomalyco/opencode/pull/14581) — Cross-drive path bypass (Windows) + +**Exit criteria:** All S1-S5 fixed, regression tests added, `BUGS.md` updated. + +--- + +## Phase 2: High-Priority Upstream Bug Fixes + +Cherry-pick 8 high-priority fixes from vouched contributors and critical bug reports. See [UPSTREAM_STATUS.md](UPSTREAM_STATUS.md) Phase 1. + +| SHA/PR | Author | Fix | Size | +|--------|--------|-----|------| +| `cc818f803` / #18283 | Protocol Zero | thinkingConfig only for reasoning models | Small | +| `7866dbcfc` / #18292 | Luke Parker | truncate permission import cycle | Small | +| `d69962b0f` / #18264 | James Long | disable chunk timeout by default | Small | +| `054075189` / #18259 | James Long | queue for event route processing | Small | +| `0d7e62a53` / #17815 | Kit Langton | forked prompt attachments losing file parts | Small | +| `84e62fc66` / #18165 | Kit Langton | preserve tagged error messages | Small | +| [#18527](https://github.com/anomalyco/opencode/pull/18527) | Dax Raad (Vouched) | restore SIGHUP exit handler | 1 line | +| [#18113](https://github.com/anomalyco/opencode/pull/18113) | Ariane Emory (Vouched) | fix default timeout value | 2 lines | + +**Exit criteria:** All 8 cherry-picked, tests pass, no regressions. --- -## Next: Zod v3 → v4 Migration +## Phase 3: Upstream Quality Fixes + OpenTUI Upgrade + +| SHA/PR | Author | Fix | +|--------|--------|-----| +| `040f551c5` / #18079 | Sebastian | OpenTUI 0.1.88 upgrade | +| [#18551](https://github.com/anomalyco/opencode/pull/18551) | Sebastian (Vouched) | OpenTUI 0.1.90 upgrade | +| `2dbcd79fd` / #18261 | jorge g | stabilize agent/skill ordering | +| `4b4dd2b88` / #18009 | Ariane Emory | apply_patch in EDIT_TOOLS filter | +| `5ddfe4ada` / #18123 | Kit Langton | type Provider.list() properly | +| `fee3c196c` / #17812 | Kit Langton | prompt schema validation debug logs | -See details below. +Also evaluate community bug fix PRs (~17 candidates, see UPSTREAM_STATUS.md): +- Retry backoff cap, 429 retry, lone surrogate prevention, empty content filtering +- LSP memory leak fix, MCP client recovery, snapshot git timeout + +**Exit criteria:** OpenTUI upgraded, quality fixes applied, tests pass. --- -## Future: Zod v3 → v4 Migration +## Phase 4: Community Bug Fixes + Features + +Cherry-pick or reimplement the best community contributions: -The codebase uses Zod v4 (`zod` package) but some patterns and downstream libraries (`zod-to-json-schema`, `hono-openapi`) expect Zod v3 types. Sites needing conversion: +**Bug fixes:** +- [#18539](https://github.com/anomalyco/opencode/pull/18539) — discourage _noop tool call during compaction +- [#18538](https://github.com/anomalyco/opencode/pull/18538) — handle SSE client disconnect +- [#18443](https://github.com/anomalyco/opencode/pull/18443) — retry 429 even when non-retryable +- [#17758](https://github.com/anomalyco/opencode/pull/17758) — prevent lone surrogate 400 errors +- [#17742](https://github.com/anomalyco/opencode/pull/17742) — filter empty text content blocks +- [#18137](https://github.com/anomalyco/opencode/pull/18137) — reduce memory during prompting (BYK) +- [#18516](https://github.com/anomalyco/opencode/pull/18516) — prevent subagent plan escape (BYK) +- [#17635](https://github.com/anomalyco/opencode/pull/17635) — remove dead LSP clients (memory leak) -| File | Pattern | Issue | -|------|---------|-------| -| `server/routes/experimental.ts:91` | `zodToJsonSchema(t.parameters as any)` | `zod-to-json-schema` expects Zod v3 `ZodType`, not v4 | -| `server/routes/*.ts` | `resolver()`, `validator()` from `hono-openapi` | May expect v3 schemas | -| `util/json.ts` | `JsonValue` uses `z.any()` with cast | `z.lazy()` generates `__schema0` $ref breaking SDK generation | -| `session/message-v2.ts` | `z.toJSONSchema()` | Uses Zod v4 native JSON Schema generation | -| `util/effect-zod.ts` | Effect-to-Zod bridge | Converts between Effect Schema and Zod | +**Features (evaluate):** +- [#12633](https://github.com/anomalyco/opencode/pull/12633) — auto-accept mode for TUI permissions (Dax) +- [#18317](https://github.com/anomalyco/opencode/pull/18317) — quiet mode for CLI runs +- [#18235](https://github.com/anomalyco/opencode/pull/18235) — offline mode +- [#18450](https://github.com/anomalyco/opencode/pull/18450) — native Output.object() (net code deletion) -**Action:** Audit all `zod-to-json-schema` call sites. Either migrate to Zod v4's `z.toJSONSchema()` or keep the v3 compatibility cast. Separate PR. +**Exit criteria:** Selected fixes applied, features evaluated, tests pass. --- -## Future: Upstream Re-sync +## Phase 5: Remaining Tests -Upstream (`anomalyco/opencode`) continues to diverge. The Effect-ification migration is ongoing upstream (7+ PRs still open). Key areas of conflict: +- [ ] filterEdited unit tests (hidden parts stripped, empty messages dropped) +- [ ] ContextEdit validation tests (ownership, budget, recency, privileged agents) +- [ ] TUI dialog tests (9: command, provider, session-rename, stash, etc.) +- [ ] TUI interaction tests (keyboard nav, prompt input, command palette) + +**Exit criteria:** Test count increases, coverage gaps filled. + +--- + +## Phase 6: Effect Behavioral Analysis + +For each of the 12 upstream Effect PRs, extract behavioral changes and reimplement in our architecture. NOT a rebase. + +Key PRs to analyze: +- #17544 — LayerMap (foundational, already done differently) +- #17849 — SkillService (we have content cache — check for new capabilities) +- #18483 — InstanceState consolidation (extract any bug fixes) +- #17829 — VcsService (contains HEAD filter bug fix) +- #17835 — FileTimeService (Semaphore locks — evaluate benefit) + +**Exit criteria:** Behavioral changes extracted and reimplemented where valuable. + +--- -| Area | Risk | Notes | -|------|------|-------| -| `session/prompt.ts` | High | Our filterEdited/filterEphemeral/focus injection vs upstream pipeline changes | -| `session/message-v2.ts` | High | Our EditMeta/LifecycleMeta/JsonValue vs upstream schema changes | -| `effect/` | Medium | Upstream consolidating into InstanceState; we deleted Instance entirely | -| `skill/skill.ts` | High | Upstream rewrote to Effect service; we added content cache | -| New Frankencode files | None | CAS, edit graph, context tools, side threads — no upstream conflict | +## Backlog: Features -**Strategy:** Periodic rebase onto `upstream/dev`. Cherry-pick applicable fixes first, then full rebase. +- [ ] TUI rendering of edit indicators (hidden/replaced/annotated parts) +- [ ] CAS garbage collection improvements (size limits, age-based cleanup) +- [ ] TUI features from upstream PRs (sidebar position, /edit command, syntax highlighting) --- -## Completed Features - -| Feature | Status | PR | -|---------|--------|-----| -| Plan Mode Fixes | Done | — | -| Verification Tool | Done | — | -| Progressive Disclosure | Done | — | -| Skills as Scripts | Done | — | -| Evaluator-Optimizer | Done | — | -| Bug Fix Pass (46 bugs) | Done | #10, #12 | -| Upstream Bug Backport P1 (B1-B9) | Done | #16 | -| Upstream Bug Backport P2 (B10-B16) | Done | #17 | -| Upstream Backport P3 (B17-B22) | Done | #18 | -| Upstream Full Rebase (Phase 4) | Done | #19 | -| Effect-ification B1 (state maps) | Done | #20 | -| Effect-ification B2-B10g + Instance deletion | Done | #21 | -| Bug fixes B47-B52 | Done | #22 | -| Type safety audit (~236 `any` eliminated) | Done | #22 | -| Architecture docs (Effect, ACP, Providers) | Done | #22 | -| Strong Zod schemas (JsonValue, ProviderMeta, ToolInput, ToolMeta) | Done | #22 | +## Completed (PRs #16-#25) + +| Feature | PR | +|---------|-----| +| Upstream bug backports (B1-B22) | #16-#18 | +| Upstream full rebase | #19 | +| Effect-ification (Instance deleted, 0 ALS fallbacks, 81 TUI tests) | #20-#21 | +| Bug fixes B47-B52 + type safety (~250 `any` eliminated) + architecture docs | #22 | +| TUI types + logger types | #23 | +| Zod v4 migration + 25 Frankencode unit tests | #24 | +| Upstream catalogue + security audit | #25 | diff --git a/STATUS.md b/STATUS.md index 013d212f8..8f5e9e038 100644 --- a/STATUS.md +++ b/STATUS.md @@ -1,22 +1,40 @@ # Frankencode — Project Status **Date:** 2026-03-21 -**Upstream:** `anomalyco/opencode` @ `dev` -**Fork:** `e6qu/frankencode` @ `dev` - -## Summary - -All features implemented. Type safety audit complete. 0 open bugs. +**Current phase:** Phase 1 (Security Fixes) — see [PLAN.md](PLAN.md) ## Metrics | Metric | Value | |--------|-------| -| Tests passing | 1448 (0 fail, 8 skip) | +| Tests | 1473 pass, 0 fail, 8 skip (127 files) | | tsgo errors | 0 | -| Open bugs | 0 (1 deferred: B51) | +| Open security issues | 5 (S1-S5) | +| Open bugs | 0 | +| Deferred bugs | 1 (B51) | | Fixed bugs | 51 | -| `any` remaining | 20 (all documented structural exceptions) | -| `z.any()` in our code | 0 (3 in upstream OpenAI SDK) | -| Architecture docs | 8 (in `docs/`) | -| PRs merged | #16-#23 | +| `any` remaining | 20 documented structural exceptions | +| Upstream divergence | 23 ahead, 162 behind, ~195 open PRs | +| PRs merged | #16-#25 | + +## Phase Progress + +| Phase | Status | Description | +|-------|--------|-------------| +| 1 | **Next** | Security fixes (S1-S5) | +| 2 | Planned | High-priority upstream bug fixes (8 cherry-picks) | +| 3 | Planned | Quality fixes + OpenTUI upgrade | +| 4 | Planned | Community bug fixes + features | +| 5 | Planned | Remaining tests | +| 6 | Planned | Effect behavioral analysis | + +## Key Documents + +| File | Purpose | +|------|---------| +| [PLAN.md](PLAN.md) | 6-phase roadmap with exit criteria | +| [GAP_ANALYSIS.md](GAP_ANALYSIS.md) | Current → target state per phase | +| [BUGS.md](BUGS.md) | Security issues S1-S5 + bug tracker | +| [DO_NEXT.md](DO_NEXT.md) | Immediate next actions | +| [UPSTREAM_STATUS.md](UPSTREAM_STATUS.md) | Upstream commit/PR catalogue | +| [docs/SECURITY_AUDIT.md](docs/SECURITY_AUDIT.md) | CVEs and vulnerability analysis | diff --git a/UPSTREAM_STATUS.md b/UPSTREAM_STATUS.md new file mode 100644 index 000000000..53c0c31f3 --- /dev/null +++ b/UPSTREAM_STATUS.md @@ -0,0 +1,282 @@ +# Upstream Status: anomalyco/opencode → e6qu/frankencode + +**Date:** 2026-03-21 +**Our base:** `origin/dev` @ `5560fd8e6` +**Upstream:** `upstream/dev` @ `832b8e252` +**Divergence:** 23 ahead, 162 behind + +**Security audit:** See [docs/SECURITY_AUDIT.md](docs/SECURITY_AUDIT.md) for upstream CVEs and Frankencode-specific vulnerabilities. + +--- + +## Integration Strategy + +Frankencode has diverged enough from upstream that a simple `git rebase` with conflict resolution is no longer viable. Each upstream change must be **analyzed individually** for how it can be incorporated into our codebase — not mechanically merged. + +**Key principles:** +- **No desktop app.** Frankencode will never ship the Electron desktop app. Desktop-only changes are permanently skipped. +- **Web app: keep but don't prioritize.** Web app changes may be useful but are not a focus. +- **Manual cherry-pick or reimplementation.** Each fix/feature is either cherry-picked (if it applies cleanly), manually reimplemented (if it conflicts with our architecture), or skipped (if irrelevant). +- **Effect-ification divergence.** Upstream uses `InstanceState` + `ScopedCache`; we deleted `Instance` entirely and use `InstanceALS` + `InstanceLifecycle` + module-level state maps with `registerDisposer`. These are fundamentally different architectures — upstream Effect PRs cannot be cherry-picked, they must be analyzed for relevant behavioral changes and reimplemented in our architecture if needed. +- **Refactors need justification.** Portability refactors (Bun→Node) are only worth adopting if we plan to support Node.js runtimes. Otherwise they add churn without benefit. + +--- + +## Backportable Fixes (15) + +Bug fixes applicable to our fork. Each needs individual analysis for clean application. + +| SHA | PR | Author | Description | Priority | +|-----|-----|--------|-------------|----------| +| `cc818f803` | #18283 | Protocol Zero | fix(provider): only set thinkingConfig for models with reasoning capability | High | +| `214a6c6cf` | #18438 | Kit Langton | fix: switch consumers to service imports to break bundle cycles | High | +| `d70099b05` | #18418 | Kit Langton | fix: apply Layer.fresh at instance service definition site | High | +| `7866dbcfc` | #18292 | Luke Parker | fix: avoid truncate permission import cycle | High | +| `d69962b0f` | #18264 | James Long | fix(core): disable chunk timeout by default | High | +| `054075189` | #18259 | James Long | fix(core): use a queue to process events in event routes | High | +| `0d7e62a53` | #17815 | Kit Langton | fix forked prompt attachments losing file parts | High | +| `84e62fc66` | #18165 | Kit Langton | fix(session): preserve tagged error messages | High | +| `24f9df546` | #18426 | Kit Langton | fix: update stale account url/email on re-login | Med | +| `1071aca91` | #18328 | Dax | fix: miscellaneous small fixes | Med | +| `6fcc970de` | #18320 | Dax | fix: include cache bin directory in which() lookups | Med | +| `6e09a1d90` | #18281 | Kit Langton | fix(account): handle pending console login polling | Med | +| `56102ff64` | #17763 | Johannes Loher | fix(core): detect vLLM context overflow errors | Med | +| `5c6ec1caa` | — | Dax Raad | fix question cross out | Med | +| `f80343b87` | — | Dax Raad | fix annotation | Low | + +## Backportable Features (8) + +| SHA | PR | Author | Description | Relevance | +|-----|-----|--------|-------------|-----------| +| `040f551c5` | #18079 | Sebastian | Upgrade opentui to 0.1.88 | High — TUI dep | +| `92cd908fb` | #18324 | Dax | feat: add Node.js entry point and build script | High — portability | +| `b3d0446d1` | #18175 | Jaaneek | feat: switch xai provider to responses API | Med | +| `05d3e65f7` | #18014 | Vladimir Glafirov | feat: enable GitLab Agent Platform with workflow model discovery | Med | +| `e6f521477` | #17961 | Shoubhit Dash | feat: add git-backed session review modes | Med | +| `81be54498` | #18138 | Kit Langton | feat(filesystem): add AppFileSystem service, migrate Snapshot | Med — Effect | +| `171e69c2f` | #18035 | Aiden Cline | feat: integrate support for multi step auth flows | Med | +| `8e09e8c61` | #18103 | Aiden Cline | feat: integrate multistep auth flows into desktop app | Low — desktop | + +## Refactors — Analysis + +Each refactor analyzed for whether it provides actual value to Frankencode. + +### Worth adopting + +| SHA | PR | Author | Description | Why | +|-----|-----|--------|-------------|-----| +| `2dbcd79fd` | #18261 | jorge g | fix: stabilize agent and skill ordering in prompt descriptions | Deterministic ordering prevents flaky LLM behavior | +| `5ddfe4ada` | #18123 | Kit Langton | type Provider.list() as Record, delete dead code | Better types, dead code removal — aligns with our type safety work | +| `4b4dd2b88` | #18009 | Ariane Emory | fix: Add apply_patch to EDIT_TOOLS filter | Bug fix disguised as refactor — apply_patch should be in the filter | +| `fee3c196c` | #17812 | Kit Langton | add prompt schema validation debug logs | Useful for debugging schema issues | + +### Not worth adopting (Bun→Node portability) + +These replace Bun-specific APIs with Node.js equivalents. Only valuable if we plan to support Node.js runtimes. **Frankencode targets Bun only** — these add churn without benefit. + +| SHA | PR | Author | Description | Skip reason | +|-----|-----|--------|-------------|-------------| +| `52a7a04ad` | #18318 | Dax | replace Bun shell with portable Process utilities | Bun-only — no Node.js target | +| `37b8662a9` | #18316 | Dax | abstract SQLite behind runtime-conditional #db import | Bun-only — we use bun:sqlite directly | +| `ddcb32ae0` | #18304 | Dax | replace Bun-specific TUI APIs with portable alternatives | Bun-only | +| `63585db6a` | #18301 | Dax | replace Bun.sleep with node:timers/promises sleep | Bun-only | +| `92cd908fb` | #18324 | Dax | add Node.js entry point and build script | Bun-only — no Node target | + +### Evaluate case-by-case + +| SHA | PR | Author | Description | Notes | +|-----|-----|--------|-------------|-------| +| `812d1bb32` | #18303 | Dax | inline tool descriptions, remove separate .txt files | **Conflicts** — our Frankencode agents use .txt prompt files. Would need to keep our .txt files. | +| `8ee939c74` | #18140 | Aiden Cline | remove unnecessary parts from the fallback system prompt | Review what was removed — might remove things we rely on | + +## TUI Fixes (5) + +| SHA | PR | Author | Description | Priority | +|-----|-----|--------|-------------|----------| +| `a64f604d5` | #16779 | Kyle Altendorf | fix(tui): check for selected text in dialog escape handler | Med | +| `51fcd04a7` | #17782 | Shoubhit Dash | Wrap question option descriptions instead of truncating | Med | +| `3256886e2` | — | David Hill | tui: make the title bar search easier to scan | Low | +| `e9a17e448` | #17146 | AbigailJixiangyuyu | fix(windows): restore /editor support on Windows | Low | +| `54ed87d53` | #18010 | Luke Parker | fix(windows): use cross-spawn for shim-backed commands | Low | + +## Effect-ification (12) — Kit Langton + +**Cannot be cherry-picked.** These are architectural changes that conflict with Frankencode's approach: + +- **Upstream:** `Instance` → `InstanceState` (using `ScopedCache` from Effect) +- **Frankencode:** `Instance` deleted → `InstanceALS` + `InstanceLifecycle` + module-level state maps with `registerDisposer` + +Each PR must be analyzed for **behavioral changes** (bug fixes, new capabilities) that we should reimplement in our architecture, versus pure structural refactors (rename, move) that don't apply. + +| SHA | PR | Description | Conflict | +|-----|-----|-------------|----------| +| `469c3a420` | #17544 | refactor(instance): move scoped services to LayerMap | **HIGH** | +| `9e740d994` | #17827 | effectify FileWatcherService | Med | +| `e5cbecf17` | #17829 | fix+refactor: effectify VcsService | Med | +| `2cbdf04ec` | #17835 | effectify FileTimeService with Semaphore locks | Med | +| `335356280` | #17675 | effectify FormatService | Med | +| `69381f6ae` | #17845 | effectify FileService | Med | +| `384982276` | #17849 | effectify SkillService | **HIGH** — we have skill cache | +| `9e7c136de` | #17878 | effectify SnapshotService | Med | +| `5dfe86dcb` | #17957 | effectify TruncateService, delete Scheduler | Med | +| `a800583ae` | #18093 | unify service namespaces and align naming | High | +| `e78944e9a` | #18266 | effectify Installation, drop Effect suffix | Med | +| `38e0dc9cc` | #18483 | Move state into InstanceState, flatten facades | **HIGH** | +| `5d2f8d77f` | #18158 | upgrade effect beta, fix test regressions (Luke Parker) | Med | + +## App/Desktop (20+) — Permanently Skip Desktop, Evaluate Web App + +**Desktop (Electron):** Frankencode will never ship a desktop app. All desktop-specific changes are permanently skipped. + +**Web app:** May be useful to keep functional. Web app changes should be evaluated individually but are not a priority. Most are by Brendan Allan (frontend) and Shoubhit Dash (review UI). + +## Chore / Generate / CI / Docs (50+) — Skip + +Auto-generated code (15x `chore: generate`), nix hashes (8x), CI, docs, vouched/disavow lists. + +## Zen Platform (8) — Skip (Frank) + +Zen-specific pricing, routing, model updates. + +## Other Notable + +| SHA | PR | Author | Description | Action | +|-----|-----|--------|-------------|--------| +| `5dc47905a` | — | Dax Raad | allow customizing DB location | Evaluate | +| `bfdc38e42` | #18337 | Aiden Cline | adjust codex plugin logic (oauth plan) | Evaluate | +| `68809365d` | #17847 | Aiden Cline | fix: github copilot enterprise integration | Med — backport | +| `0bbf26a1c` | #18343 | Luke Parker | deslopity deslopity (code cleanup) | Evaluate | +| `1ac1a0287` | #18186 | Dax | anthropic legal requests | Low | + +--- + +## Recommended Backport Order + +**Phase 1 — High-priority fixes (8 commits, cherry-pick individually):** +- `cc818f803` #18283: thinkingConfig for reasoning models only +- `7866dbcfc` #18292: truncate permission import cycle +- `d69962b0f` #18264: disable chunk timeout by default +- `054075189` #18259: queue for event route processing +- `0d7e62a53` #17815: forked prompt attachments losing file parts +- `84e62fc66` #18165: preserve tagged error messages +- `214a6c6cf` #18438: service imports to break bundle cycles +- `d70099b05` #18418: Layer.fresh at instance service site + +**Phase 2 — Quality improvements (5 commits):** +- `040f551c5` #18079: OpenTUI 0.1.88 +- `2dbcd79fd` #18261: stabilize agent/skill ordering +- `4b4dd2b88` #18009: apply_patch in EDIT_TOOLS filter +- `5ddfe4ada` #18123: type Provider.list() as Record +- `fee3c196c` #17812: prompt schema validation debug logs + +**Phase 3 — Medium fixes + features (remaining):** +Cherry-pick remaining backportable fixes and evaluate features. + +**Phase 4 — Effect behavioral analysis (NOT a rebase):** +For each of the 12 Effect PRs, read the diff and extract: +1. Bug fixes embedded in the refactor (e.g., VcsService #17829 fixes HEAD filter bug) +2. New capabilities (e.g., Semaphore locks in FileTimeService) +3. Reimplement those behaviors in our architecture — do NOT rebase + +**Permanently skipped:** +- All desktop/Electron changes (Frankencode will never ship desktop) +- All Bun→Node portability refactors (Frankencode targets Bun only) +- All chore/generate/nix/CI commits +- All Zen platform changes + +--- + +--- + +## Open PRs — Backport Candidates (~195 total, ~20 high priority) + +### From Recognized/Vouched Contributors + +| PR | Author | Title | Priority | +|----|--------|-------|----------| +| [#18527](https://github.com/anomalyco/opencode/pull/18527) | Dax Raad (Vouched) | fix(core): restore SIGHUP exit handler (+1/-0) | **HIGH** | +| [#18551](https://github.com/anomalyco/opencode/pull/18551) | Sebastian (Vouched) | Upgrade opentui to 0.1.90 | **HIGH** | +| [#18113](https://github.com/anomalyco/opencode/pull/18113) | Ariane Emory (Vouched) | fix: Fix default timeout value (+2/-2) | **HIGH** | +| [#12633](https://github.com/anomalyco/opencode/pull/12633) | Dax Raad (beta) | feat(tui): add auto-accept mode for permissions | **HIGH** | +| [#18348](https://github.com/anomalyco/opencode/pull/18348) | rekram1-node (Vouched) | fix: plugins can register providers with config changes | Med | +| [#18155](https://github.com/anomalyco/opencode/pull/18155) | rekram1-node (Vouched) | feat: add model reconciliation hook | Med | +| [#13692](https://github.com/anomalyco/opencode/pull/13692) | Dax Raad | feat: add reference agent for searching external repos | Med | +| [#18173](https://github.com/anomalyco/opencode/pull/18173) | Kit Langton (Vouched) | feat(bus): migrate Bus to Effect PubSub | Med — Effect | +| [#18336](https://github.com/anomalyco/opencode/pull/18336) | Tim Smart | refactor effect runtime | Med — Effect | + +Kit Langton has 10 more Effect PRs — all DRAFT, all require behavioral analysis not cherry-pick. + +### Security PRs + +| PR | Author | Title | Priority | +|----|--------|-------|----------| +| [#10763](https://github.com/anomalyco/opencode/pull/10763) | orbisai0security | Fix CVE-2025-58179 (astrojs/cloudflare) | **HIGH** | +| [#10974](https://github.com/anomalyco/opencode/pull/10974) | MaxMiksa | Guard TUI server exposure | **HIGH** | +| [#14581](https://github.com/anomalyco/opencode/pull/14581) | Nicoo01x | Prevent cross-drive path bypass (Windows) | Med | +| [#17362](https://github.com/anomalyco/opencode/pull/17362) | kvenux | Sanitize markdown link XSS | Med (web only) | + +### Core Bug Fixes (non-contributor, worth evaluating) + +| PR | Author | Title | Why | +|----|--------|-------|-----| +| [#18539](https://github.com/anomalyco/opencode/pull/18539) | KnutZuidema | Discourage _noop tool call during compaction | Small, targeted | +| [#18538](https://github.com/anomalyco/opencode/pull/18538) | zaxbysauce | Handle client disconnect in SSE writes | Crash prevention | +| [#18443](https://github.com/anomalyco/opencode/pull/18443) | LucasSantana-Dev | Retry 429 even when provider says non-retryable | Reliability | +| [#18445](https://github.com/anomalyco/opencode/pull/18445) | LucasSantana-Dev | Account for OpenRouter cache write tokens | Cost accuracy | +| [#17834](https://github.com/anomalyco/opencode/pull/17834) | TomRoyls | Cap retry backoff to 30s | 2-line fix | +| [#17758](https://github.com/anomalyco/opencode/pull/17758) | SunCreation | Prevent lone surrogate 400 errors in tool results | Provider compat | +| [#17742](https://github.com/anomalyco/opencode/pull/17742) | RhoninSeiei | Filter empty text content blocks for all providers | Provider compat | +| [#17712](https://github.com/anomalyco/opencode/pull/17712) | jpvelasco | Drop empty messages after reasoning filter | Provider fix | +| [#18412](https://github.com/anomalyco/opencode/pull/18412) | ernestodeoliveira | Don't decode percent-encoding in filesystem paths | Path safety | +| [#18137](https://github.com/anomalyco/opencode/pull/18137) | BYK | Reduce memory during prompting (lazy scan + windowing) | Performance | +| [#18516](https://github.com/anomalyco/opencode/pull/18516) | BYK | Prevent subagent plan escape | Subagent safety | +| [#17818](https://github.com/anomalyco/opencode/pull/17818) | LehaoLin | Validate JSON in tool call arguments | Robustness | +| [#17635](https://github.com/anomalyco/opencode/pull/17635) | SHL0MS | Remove dead LSP clients (memory leak) | Memory | +| [#17651](https://github.com/anomalyco/opencode/pull/17651) | vesector | Recover MCP clients after transient failures | MCP reliability | +| [#17645](https://github.com/anomalyco/opencode/pull/17645) | mollux | Apply config model cost overrides at runtime | Cost accuracy | +| [#18069](https://github.com/anomalyco/opencode/pull/18069) | ihubanov | Timeout for snapshot git add (large worktrees) | Reliability | +| [#17888](https://github.com/anomalyco/opencode/pull/17888) | flacks | Honor model:inherit in subagent frontmatter | 1-line fix | + +### TUI Feature PRs (evaluate) + +| PR | Author | Title | Notes | +|----|--------|-------|-------| +| [#18497](https://github.com/anomalyco/opencode/pull/18497) | amosbird | Sidebar position config | TUI layout | +| [#17644](https://github.com/anomalyco/opencode/pull/17644) | joeyism | /edit command to open files in $EDITOR | TUI UX | +| [#17868](https://github.com/anomalyco/opencode/pull/17868) | jwcrystal | Prompt after /compact (continue or branch) | TUI UX | +| [#17156](https://github.com/anomalyco/opencode/pull/17156) | shiyuhang0 | Show skills in sidebar | TUI feature | +| [#14190](https://github.com/anomalyco/opencode/pull/14190) | mocksoul | Tail-f effect for tool output | TUI UX | +| [#17992](https://github.com/anomalyco/opencode/pull/17992) | saltykovdg | Light-clean theme | TUI theme | +| [#18198](https://github.com/anomalyco/opencode/pull/18198) | 2KAbhishek | Syntax highlighting for kotlin, hcl, lua, toml | TUI feature | + +### Core Feature PRs (evaluate) + +| PR | Author | Title | Notes | +|----|--------|-------|-------| +| [#18317](https://github.com/anomalyco/opencode/pull/18317) | vaporwavie | Quiet mode for CLI runs | CLI UX | +| [#18235](https://github.com/anomalyco/opencode/pull/18235) | dgruzd | Offline mode | Network control | +| [#18178](https://github.com/anomalyco/opencode/pull/18178) | mjdouglas | Custom system prompt per model | Config | +| [#17670](https://github.com/anomalyco/opencode/pull/17670) | dmitryryabkov | Dynamic model discovery for local providers | Provider feature | +| [#18450](https://github.com/anomalyco/opencode/pull/18450) | potlee | Use native Output.object() for structured output | Net code deletion | +| [#18280](https://github.com/anomalyco/opencode/pull/18280) | ryanskidmore | Plugin system robustness improvements | Plugin stability | + +### Permanently Skipped (~80 PRs) + +- **Desktop/Electron:** ~15 PRs (Brendan Allan, Luke Parker, OpeOginni) +- **Web App UI:** ~25 PRs (anduimagui, Rohansguliani, Shoubhit Dash) +- **Bun→Node portability:** ~5 PRs (Dax Raad) +- **Docs/translations/ecosystem:** ~15 PRs +- **CI/chore:** ~10 PRs +- **Niche/massive:** ~10 PRs (Kiro provider +4309 lines, multi-session daemon +7589 lines) + +--- + +## Decision Key + +- **Cherry-pick** = isolated fix, applies cleanly +- **Reimplement** = extract behavioral change from architectural refactor, apply to our architecture +- **Skip (desktop)** = Frankencode never ships desktop — permanently irrelevant +- **Skip (portability)** = Bun→Node refactor — Frankencode targets Bun only +- **Skip (web app)** = Web app changes — low priority, evaluate individually +- **Skip (chore)** = auto-generated, CI, docs, translations +- **Evaluate** = needs code-level review before deciding diff --git a/WHAT_WE_DID.md b/WHAT_WE_DID.md index 7fea3dc66..6316e997a 100644 --- a/WHAT_WE_DID.md +++ b/WHAT_WE_DID.md @@ -1,37 +1,17 @@ # Frankencode — What We Did -_Summary of all completed work. Detailed per-session logs archived in git history._ +_Compressed summary. Per-session details in git history._ ## Features (Phases 0-4) -- CAS (content-addressable store), edit graph (DAG), context editing (6 operations) -- Side threads (project-level), objective tracker, classifier/focus/rewrite agents -- System prompt injection, plugin hooks, lifecycle sweeper, ephemeral commands -- Verify tool (circuit breaker), refine tool (evaluator-optimizer loop), script discovery -- /btw, /focus, /focus-rewrite-history, /reset-context, /cost commands +CAS, edit graph, context editing (6 ops), side threads, objective tracker, classifier/focus/rewrite agents, verify/refine tools, ephemeral commands, /btw /focus /cost. -## Upstream Sync (PRs #16-#19) +## Infrastructure (PRs #16-#24) -- Phase 1-3: 22 upstream bug fixes backported (B1-B22) -- Phase 4: Full rebase onto upstream/dev with conflict resolution - -## Effect-ification (PRs #20-#21) - -- B1-B10g: Instance split into InstanceALS + InstanceLifecycle + InstanceContext -- `src/project/instance.ts` deleted; test shim at `test/fixture/instance-shim.ts` -- All 59 ALS fallback patterns eliminated; 81 TUI component tests added - -## Bug Fixes + Type Safety + Docs (PR #22) - -- 6 bug fixes (B47-B52): objective cache, session error handling, mark transaction, queue, bus -- ~250+ `any` types eliminated; strong Zod schemas (JsonValue, ProviderMeta, ToolInput, ToolMeta) -- `util/json.ts` extracted to avoid circular imports; SDK build fixed -- 5 architecture docs: Effect-ification, ACP, API Providers, Frankencode Differences, doc index -- AGENTS.md updated with strong typing rules; CLAUDE.md symlink created - -## TUI Types + Logger (PR #23) - -- 17 TUI `any` types removed (ToolProps generics, state narrowing, typed inputs) -- Logger types: `LogMessage = string | Error`, `LogExtra = Record` -- 17 caller files updated with error narrowing -- PLAN.md rewritten; stale sections removed +- **#16-#18:** 22 upstream bug fixes backported +- **#19:** Full rebase onto upstream/dev +- **#20-#21:** Effect-ification complete — Instance deleted, 0 ALS fallbacks, 81 TUI tests +- **#22:** 6 bug fixes (B47-B52), ~250 `any` eliminated, strong Zod schemas, 5 architecture docs, SDK build fix +- **#23:** TUI types fixed, logger types strengthened (unknown → string | Error) +- **#24:** Zod v4 migration (zodToJsonSchema → z.toJSONSchema), 25 Frankencode unit tests, tracking docs cleanup +- **#25:** Upstream catalogue (162 commits + ~195 PRs), security audit (2 CVEs, 5 issues), 6-phase roadmap diff --git a/docs/README.md b/docs/README.md index 51700d8f0..0187b44dd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,6 +13,7 @@ | [EFFECTIFICATION.md](EFFECTIFICATION.md) | Effect-TS architecture, 22 services, LayerMap, dual-layer context | | [AGENT_CLIENT_PROTOCOL.md](AGENT_CLIENT_PROTOCOL.md) | ACP v1 protocol support for IDE integration | | [API_PROVIDERS.md](API_PROVIDERS.md) | 21+ LLM providers, models.dev API, transform pipeline | +| [SECURITY_AUDIT.md](SECURITY_AUDIT.md) | CVEs, upstream security issues, Frankencode-specific vulnerabilities | ## Architecture at a Glance diff --git a/docs/SECURITY_AUDIT.md b/docs/SECURITY_AUDIT.md new file mode 100644 index 000000000..301f3d8f8 --- /dev/null +++ b/docs/SECURITY_AUDIT.md @@ -0,0 +1,123 @@ +# Security Audit: Frankencode + +**Date:** 2026-03-21 +**Scope:** Upstream OpenCode security issues + Frankencode-specific patterns + +--- + +## Published CVEs (2) + +### CVE-2026-22812 — Unauthenticated HTTP Server RCE (CVSS 8.8) + +- **Advisory:** [GHSA-vxw4-wv6m-9hhh](https://github.com/anomalyco/opencode/security/advisories/GHSA-vxw4-wv6m-9hhh) +- **Reporter:** CyberShadow +- **CWEs:** CWE-306 (Missing Auth), CWE-749 (Exposed Dangerous Method), CWE-942 (Permissive CORS) +- **Impact:** Any local process or website can execute shell commands and read files via unauthenticated HTTP server +- **Patched in:** opencode-ai >=1.0.216 +- **Frankencode status:** PARTIALLY MITIGATED — `basicAuth` middleware exists but skips when `OPENCODE_SERVER_PASSWORD` is not set. CORS is now origin-restricted (localhost + opencode.ai domains). +- **Action needed:** Consider requiring authentication or binding server only to loopback by default + +### CVE-2026-22813 — XSS to RCE via Markdown Renderer (CVSS 9.4) + +- **Advisory:** [GHSA-c83v-7274-4vgp](https://github.com/anomalyco/opencode/security/advisories/GHSA-c83v-7274-4vgp) +- **Reporter:** AlbertSPedersen +- **Impact:** Malicious website loads attacker-controlled sessions into localhost, XSS chains into RCE via pty endpoints +- **Patched in:** opencode >=1.1.10 +- **Frankencode status:** NEEDS AUDIT — verify `?url=` parameter behavior and markdown sanitization in `packages/ui/` +- **Action needed:** Audit markdown renderer for XSS, verify `?url=` server override is disabled or validated + +--- + +## High-Priority Issues Affecting Frankencode (8) + +| # | Title | Author | Severity | Our File | +|---|-------|--------|----------|----------| +| [#8313](https://github.com/anomalyco/opencode/issues/8313) | Path traversal via symlinks bypasses `Filesystem.contains()` | mluckydream | **CRITICAL** | `src/util/filesystem.ts:151` | +| [#6361](https://github.com/anomalyco/opencode/issues/6361) | Arbitrary command exec via untrusted `.opencode/` MCP config | Mishkun | **HIGH** | `src/mcp/index.ts` | +| [#7163](https://github.com/anomalyco/opencode/issues/7163) | RCE via auto-loaded `.opencode` plugins | xpcmdshell | **HIGH** | `src/plugin/index.ts` | +| [#7173](https://github.com/anomalyco/opencode/issues/7173) | RCE via lifecycle scripts in `.opencode/package.json` | xpcmdshell | **HIGH** | `bun install` | +| [#6527](https://github.com/anomalyco/opencode/issues/6527) | Plan mode restrictions bypassed via sub-agents | w0wl0lxd | **HIGH** | `src/tool/task.ts` | +| [#17350](https://github.com/anomalyco/opencode/issues/17350) | CLI command injection via `exec()` in github.ts | kvenux | **HIGH** | `src/cli/cmd/github.ts` | +| [#11703](https://github.com/anomalyco/opencode/issues/11703) | Path traversal in file read/write tools | — | **HIGH** | `src/tool/read.ts`, `write.ts`, `edit.ts` | +| [#12196](https://github.com/anomalyco/opencode/issues/12196) | Read tool bypasses .gitignore, exposing .env files | cupton-paa | **MEDIUM** | `src/tool/read.ts` | + +--- + +## Frankencode-Specific Vulnerable Patterns + +### 1. `Filesystem.contains()` — Symlink Bypass (CRITICAL) + +**File:** `src/util/filesystem.ts` line ~151 +**Pattern:** Purely lexical relative-path check (`!relative(parent, child).startsWith("..")`) — no `realpath()` resolution. +**Impact:** Symlinks escape the project directory. Agent can read/write files outside the project boundary. +**Fix:** Call `fs.realpathSync()` on both `parent` and `child` before the lexical check. +**Upstream issue:** [#8313](https://github.com/anomalyco/opencode/issues/8313) + +### 2. Untrusted Workspace Autoloading (HIGH) + +**Files:** `src/mcp/index.ts`, `src/plugin/index.ts` +**Pattern:** `.opencode/` directory in a cloned repo can contain MCP server configs and plugins that run automatically without user consent. +**Impact:** Cloning a malicious repo gives the attacker code execution. +**Fix:** Add a trust prompt before loading workspace-local configs/plugins. Or require explicit opt-in. +**Upstream issues:** [#6361](https://github.com/anomalyco/opencode/issues/6361), [#7163](https://github.com/anomalyco/opencode/issues/7163), [#7173](https://github.com/anomalyco/opencode/issues/7173) + +### 3. `exec()` Command Injection (HIGH) + +**File:** `src/cli/cmd/github.ts` line ~338 +**Pattern:** `exec(command)` where `command` includes a URL. If the URL is attacker-influenced, shell metacharacters can inject commands. +**Fix:** Use `Process.spawn(["open", url])` with argument array instead of `exec()`. +**Upstream issue:** [#17350](https://github.com/anomalyco/opencode/issues/17350) + +### 4. Server Auth Defaults (MEDIUM) + +**File:** `src/server/server.ts` +**Pattern:** Auth middleware returns `next()` when `OPENCODE_SERVER_PASSWORD` is not set — effectively no auth. +**Fix:** Bind to loopback only by default, or require password when binding to non-loopback. +**Upstream issues:** [#6355](https://github.com/anomalyco/opencode/issues/6355), [#10973](https://github.com/anomalyco/opencode/issues/10973) + +### 5. .env File Exposure (MEDIUM) + +**File:** `src/tool/read.ts` +**Pattern:** Read tool does not check `.gitignore` patterns. Agent can read `.env`, `credentials.json`, etc. +**Fix:** Check `.gitignore` before reading files, or maintain a deny-list of sensitive file patterns. +**Upstream issue:** [#12196](https://github.com/anomalyco/opencode/issues/12196) + +--- + +## Open Security PRs Worth Evaluating + +| PR | Author | Description | Action | +|----|--------|-------------|--------| +| [#6948](https://github.com/anomalyco/opencode/pull/6948) | RinZ27 | Harden BashTool command parsing | Evaluate for our bash tool | +| [#14108](https://github.com/anomalyco/opencode/pull/14108) | edevil | Strip env var assignments from bash permissions | Evaluate | +| [#14568](https://github.com/anomalyco/opencode/pull/14568) | judepereira | Remove SERVER_PASSWORD from env after read | Easy apply | +| [#14581](https://github.com/anomalyco/opencode/pull/14581) | Nicoo01x | Prevent cross-drive path bypass | Apply — fixes #14579 | +| [#6532](https://github.com/anomalyco/opencode/pull/6532) | w0wl0lxd | Inherit Plan mode permissions for sub-agents | Evaluate | + +--- + +## Upstream Security Policy Notes + +From `SECURITY.md`: +- The permission system is **UX only, not security isolation** — there is no sandbox +- Server mode is opt-in; unauthenticated by default is **by design** +- MCP server behavior, malicious config files, and LLM provider data handling are **out of scope** +- AI-generated security reports are auto-rejected + +--- + +## Recommended Priority for Frankencode + +1. **Fix `Filesystem.contains()` symlink bypass** — add `realpathSync()` resolution +2. **Fix `exec()` in github.ts** — use `spawn` with argument array +3. **Add workspace trust prompt** — before loading `.opencode/` plugins and MCP configs +4. **Require server auth for non-loopback** — or bind to loopback only +5. **Add .env/credentials deny-list to read tool** — prevent accidental secret exposure + +--- + +## See Also + +- [UPSTREAM_STATUS.md](../UPSTREAM_STATUS.md) — full upstream commit catalogue +- [FRANKENCODE_DIFFERENCES.md](FRANKENCODE_DIFFERENCES.md) — Frankencode vs OpenCode differences +- [EFFECTIFICATION.md](EFFECTIFICATION.md) — Effect architecture (relevant to service isolation)