-
Notifications
You must be signed in to change notification settings - Fork 279
fix: patch systemic OS-level vulnerabilities, OOM risks, and concurre… #799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -317,7 +317,8 @@ func (g *GeminiCLIAgent) ChunkTranscript(ctx context.Context, content []byte, ma | |||
|
|
||||
| var chunks [][]byte | ||||
| var currentMessages []GeminiMessage | ||||
| currentSize := len(`{"messages":[]}`) // Base JSON structure size | ||||
| baseSize := len(`{"messages":[]}`) | ||||
| currentSize := baseSize // Base JSON structure size | ||||
|
|
||||
| for i, msg := range transcript.Messages { | ||||
| // Marshal message to get its size | ||||
|
|
@@ -330,6 +331,10 @@ func (g *GeminiCLIAgent) ChunkTranscript(ctx context.Context, content []byte, ma | |||
| continue | ||||
| } | ||||
| msgSize := len(msgBytes) + 1 // +1 for comma separator | ||||
|
|
||||
|
||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -98,6 +98,10 @@ func (a *OpenCodeAgent) ChunkTranscript(_ context.Context, content []byte, maxSi | |
| } | ||
| msgSize := len(msgBytes) + 1 // +1 for comma separator | ||
|
|
||
| if msgSize+baseSize > maxSize { | ||
| return nil, fmt.Errorf("single message size (%d) exceeds chunk maxSize (%d)", msgSize+baseSize, maxSize) | ||
| } | ||
|
|
||
| if currentSize+msgSize > maxSize && len(currentMessages) > 0 { | ||
| // Save current chunk | ||
| chunkData, err := json.Marshal(ExportSession{Info: session.Info, Messages: currentMessages}) | ||
|
|
@@ -167,7 +171,7 @@ func (a *OpenCodeAgent) GetSessionID(input *agent.HookInput) string { | |
| // GetSessionDir returns the directory where Entire stores OpenCode session transcripts. | ||
| // Transcripts are ephemeral handoff files between the TS plugin and the Go hook handler. | ||
| // Once checkpointed, the data lives on git refs and the file is disposable. | ||
| // Stored in os.TempDir()/entire-opencode/<sanitized-path>/ to avoid squatting on | ||
| // Stored in repoPath/.git/entire-opencode/<sanitized-path>/ to avoid squatting on | ||
| // OpenCode's own directories (~/.opencode/ is project-level, not home-level). | ||
| func (a *OpenCodeAgent) GetSessionDir(repoPath string) (string, error) { | ||
| // Check for test environment override | ||
|
|
@@ -176,7 +180,11 @@ func (a *OpenCodeAgent) GetSessionDir(repoPath string) (string, error) { | |
| } | ||
|
|
||
| projectDir := SanitizePathForOpenCode(repoPath) | ||
| return filepath.Join(os.TempDir(), "entire-opencode", projectDir), nil | ||
| dir := filepath.Join(repoPath, ".git", "entire-opencode", projectDir) | ||
| if err := os.MkdirAll(dir, 0o700); err != nil { | ||
| return "", fmt.Errorf("failed to create secure opencode session dir: %w", err) | ||
| } | ||
|
Comment on lines
182
to
+186
|
||
| return dir, nil | ||
| } | ||
|
|
||
| func (a *OpenCodeAgent) ResolveSessionFile(sessionDir, agentSessionID string) string { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,7 @@ package session | |||||||||
| import ( | ||||||||||
| "context" | ||||||||||
| "encoding/json" | ||||||||||
| "errors" | ||||||||||
| "fmt" | ||||||||||
| "log/slog" | ||||||||||
| "os" | ||||||||||
|
|
@@ -364,6 +365,15 @@ func (s *StateStore) Save(ctx context.Context, state *State) error { | |||||||||
| if err := os.Rename(tmpFile, stateFile); err != nil { | ||||||||||
| return fmt.Errorf("failed to rename session state file: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| info, err := os.Lstat(stateFile) | ||||||||||
| if err != nil { | ||||||||||
| return fmt.Errorf("failed to stat session state file after rename: %w", err) | ||||||||||
| } | ||||||||||
| if info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular() { | ||||||||||
| _ = os.Remove(stateFile) | ||||||||||
|
||||||||||
| _ = os.Remove(stateFile) | |
| if removeErr := os.Remove(stateFile); removeErr != nil { | |
| return fmt.Errorf("security error: state file is not a regular file (symlink / path traversal detected); additionally failed to remove suspicious file: %w", removeErr) | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maxLineSizeisn’t enforced strictly: the guards only checklen(lineBuf) < maxLineSizebeforeappend, so a final append can pushlineBufpast the intended limit (and it will still be unmarshaled). Consider checkinglen(lineBuf)+len(toAppend) <= maxLineSize(or truncating/discarding the remainder of the line once the limit is hit) to keep the memory bound predictable.