Initial commit: Beads issue tracker with security fixes
Core features:
- Dependency-aware issue tracking with SQLite backend
- Ready work detection (issues with no open blockers)
- Dependency tree visualization
- Cycle detection and prevention
- Full audit trail
- CLI with colored output
Security and correctness fixes applied:
- Fixed SQL injection vulnerability in UpdateIssue (whitelisted fields)
- Fixed race condition in ID generation (added mutex)
- Fixed cycle detection to return full paths (not just issue IDs)
- Added cycle prevention in AddDependency (validates before commit)
- Added comprehensive input validation (priority, status, types, etc.)
- Fixed N+1 query in GetBlockedIssues (using GROUP_CONCAT)
- Improved query building in GetReadyWork (proper string joining)
- Fixed P0 priority filter bug (using Changed() instead of value check)
All critical and major issues from code review have been addressed.
🤖 Generated with Claude Code
This commit is contained in:
61
internal/storage/storage.go
Normal file
61
internal/storage/storage.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/steveyackey/beads/internal/types"
|
||||
)
|
||||
|
||||
// Storage defines the interface for issue storage backends
|
||||
type Storage interface {
|
||||
// Issues
|
||||
CreateIssue(ctx context.Context, issue *types.Issue, actor string) error
|
||||
GetIssue(ctx context.Context, id string) (*types.Issue, error)
|
||||
UpdateIssue(ctx context.Context, id string, updates map[string]interface{}, actor string) error
|
||||
CloseIssue(ctx context.Context, id string, reason string, actor string) error
|
||||
SearchIssues(ctx context.Context, query string, filter types.IssueFilter) ([]*types.Issue, error)
|
||||
|
||||
// Dependencies
|
||||
AddDependency(ctx context.Context, dep *types.Dependency, actor string) error
|
||||
RemoveDependency(ctx context.Context, issueID, dependsOnID string, actor string) error
|
||||
GetDependencies(ctx context.Context, issueID string) ([]*types.Issue, error)
|
||||
GetDependents(ctx context.Context, issueID string) ([]*types.Issue, error)
|
||||
GetDependencyTree(ctx context.Context, issueID string, maxDepth int) ([]*types.TreeNode, error)
|
||||
DetectCycles(ctx context.Context) ([][]*types.Issue, error)
|
||||
|
||||
// Labels
|
||||
AddLabel(ctx context.Context, issueID, label, actor string) error
|
||||
RemoveLabel(ctx context.Context, issueID, label, actor string) error
|
||||
GetLabels(ctx context.Context, issueID string) ([]string, error)
|
||||
GetIssuesByLabel(ctx context.Context, label string) ([]*types.Issue, error)
|
||||
|
||||
// Ready Work & Blocking
|
||||
GetReadyWork(ctx context.Context, filter types.WorkFilter) ([]*types.Issue, error)
|
||||
GetBlockedIssues(ctx context.Context) ([]*types.BlockedIssue, error)
|
||||
|
||||
// Events
|
||||
AddComment(ctx context.Context, issueID, actor, comment string) error
|
||||
GetEvents(ctx context.Context, issueID string, limit int) ([]*types.Event, error)
|
||||
|
||||
// Statistics
|
||||
GetStatistics(ctx context.Context) (*types.Statistics, error)
|
||||
|
||||
// Lifecycle
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Config holds database configuration
|
||||
type Config struct {
|
||||
Backend string // "sqlite" or "postgres"
|
||||
|
||||
// SQLite config
|
||||
Path string // database file path
|
||||
|
||||
// PostgreSQL config
|
||||
Host string
|
||||
Port int
|
||||
Database string
|
||||
User string
|
||||
Password string
|
||||
SSLMode string
|
||||
}
|
||||
Reference in New Issue
Block a user