From 92b7b0811349b966c72ec2b9c1ddb6c41ccd426a Mon Sep 17 00:00:00 2001 From: Snider Date: Sat, 21 Mar 2026 17:57:03 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Codex=20round=209=20=E2=80=94=20verify?= =?UTF-8?q?=20push=20target,=20plan=20path=20traversal,=20mirror=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit High: verify rebase push now targets Forge remote, not local origin High/Security: planPath sanitises ID via filepath.Base to prevent path traversal in plan read/update/delete Medium: mirror compares and pushes local default branch, not HEAD Findings 3-6 verified as false positives/known issues (bridge async by design, API returns top-level memories not {data:...}, inbox uses {messages:...} confirmed against live API). Co-Authored-By: Virgil --- pkg/agentic/mirror.go | 12 +++++++----- pkg/agentic/plan.go | 7 ++++++- pkg/agentic/verify.go | 14 ++++++++++++-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/pkg/agentic/mirror.go b/pkg/agentic/mirror.go index 86fe058..c50ea37 100644 --- a/pkg/agentic/mirror.go +++ b/pkg/agentic/mirror.go @@ -87,14 +87,15 @@ func (s *PrepSubsystem) mirror(ctx context.Context, _ *mcp.CallToolRequest, inpu fetchCmd.Dir = repoDir fetchCmd.Run() - // Check how far ahead we are - ahead := commitsAhead(repoDir, "github/main", "HEAD") + // Check how far ahead local default branch is vs github + localBase := gitDefaultBranch(repoDir) + ahead := commitsAhead(repoDir, "github/main", localBase) if ahead == 0 { continue // Already in sync } // Count files changed - files := filesChanged(repoDir, "github/main", "HEAD") + files := filesChanged(repoDir, "github/main", localBase) sync := MirrorSync{ Repo: repo, @@ -118,8 +119,9 @@ func (s *PrepSubsystem) mirror(ctx context.Context, _ *mcp.CallToolRequest, inpu // Ensure dev branch exists on GitHub ensureDevBranch(repoDir) - // Push local main to github dev - pushCmd := exec.CommandContext(ctx, "git", "push", "github", "HEAD:refs/heads/dev", "--force") + // Push local main to github dev (explicit main, not HEAD) + base := gitDefaultBranch(repoDir) + pushCmd := exec.CommandContext(ctx, "git", "push", "github", base+":refs/heads/dev", "--force") pushCmd.Dir = repoDir if err := pushCmd.Run(); err != nil { sync.Skipped = fmt.Sprintf("push failed: %v", err) diff --git a/pkg/agentic/plan.go b/pkg/agentic/plan.go index de17e93..2c2d7a3 100644 --- a/pkg/agentic/plan.go +++ b/pkg/agentic/plan.go @@ -313,7 +313,12 @@ func (s *PrepSubsystem) planList(_ context.Context, _ *mcp.CallToolRequest, inpu // --- Helpers --- func planPath(dir, id string) string { - return filepath.Join(dir, id+".json") + // Sanitise ID to prevent path traversal + safe := filepath.Base(id) + if safe == "." || safe == ".." || safe == "" { + safe = "invalid" + } + return filepath.Join(dir, safe+".json") } func generatePlanID(title string) string { diff --git a/pkg/agentic/verify.go b/pkg/agentic/verify.go index 146ee35..06f8943 100644 --- a/pkg/agentic/verify.go +++ b/pkg/agentic/verify.go @@ -132,8 +132,18 @@ func (s *PrepSubsystem) rebaseBranch(srcDir, branch string) bool { return false } - // Force-push the rebased branch - push := exec.Command("git", "push", "--force-with-lease", "origin", branch) + // Force-push the rebased branch to Forge (origin is local clone) + st, _ := readStatus(filepath.Dir(srcDir)) + org := "core" + repo := "" + if st != nil { + if st.Org != "" { + org = st.Org + } + repo = st.Repo + } + forgeRemote := fmt.Sprintf("ssh://git@forge.lthn.ai:2223/%s/%s.git", org, repo) + push := exec.Command("git", "push", "--force-with-lease", forgeRemote, branch) push.Dir = srcDir return push.Run() == nil }