Fix SQL injection and refresh scheduling in convoy panel
- Add convoyID validation with regex pattern ^hq-[a-zA-Z0-9-]+$ to prevent SQL injection in getTrackedIssueStatus (gt-ur4c4) - Fix duplicate refresh scheduling: tick schedules fetch, fetch schedules next tick (gt-yqfrx) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
d37bd53a90
commit
4178940d39
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -13,6 +14,9 @@ import (
|
|||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// convoyIDPattern validates convoy IDs to prevent SQL injection
|
||||||
|
var convoyIDPattern = regexp.MustCompile(`^hq-[a-zA-Z0-9-]+$`)
|
||||||
|
|
||||||
// Convoy represents a convoy's status for the dashboard
|
// Convoy represents a convoy's status for the dashboard
|
||||||
type Convoy struct {
|
type Convoy struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
@@ -145,9 +149,15 @@ type trackedStatus struct {
|
|||||||
|
|
||||||
// getTrackedIssueStatus queries tracked issues and their status
|
// getTrackedIssueStatus queries tracked issues and their status
|
||||||
func getTrackedIssueStatus(beadsDir, convoyID string) []trackedStatus {
|
func getTrackedIssueStatus(beadsDir, convoyID string) []trackedStatus {
|
||||||
|
// Validate convoyID to prevent SQL injection
|
||||||
|
if !convoyIDPattern.MatchString(convoyID) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
dbPath := filepath.Join(beadsDir, "beads.db")
|
dbPath := filepath.Join(beadsDir, "beads.db")
|
||||||
|
|
||||||
// Query tracked dependencies from SQLite
|
// Query tracked dependencies from SQLite
|
||||||
|
// convoyID is validated above to match ^hq-[a-zA-Z0-9-]+$
|
||||||
cmd := exec.Command("sqlite3", "-json", dbPath,
|
cmd := exec.Command("sqlite3", "-json", dbPath,
|
||||||
fmt.Sprintf(`SELECT depends_on_id FROM dependencies WHERE issue_id = '%s' AND type = 'tracks'`, convoyID))
|
fmt.Sprintf(`SELECT depends_on_id FROM dependencies WHERE issue_id = '%s' AND type = 'tracks'`, convoyID))
|
||||||
|
|
||||||
|
|||||||
@@ -190,11 +190,14 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
|
|
||||||
case convoyUpdateMsg:
|
case convoyUpdateMsg:
|
||||||
if msg.state != nil {
|
if msg.state != nil {
|
||||||
|
// Fresh data arrived - update state and schedule next tick
|
||||||
m.convoyState = msg.state
|
m.convoyState = msg.state
|
||||||
m.updateViewContent()
|
m.updateViewContent()
|
||||||
|
cmds = append(cmds, m.convoyRefreshTick())
|
||||||
|
} else {
|
||||||
|
// Tick fired - fetch new data
|
||||||
|
cmds = append(cmds, m.fetchConvoys())
|
||||||
}
|
}
|
||||||
// Schedule next refresh
|
|
||||||
cmds = append(cmds, m.fetchConvoys(), m.convoyRefreshTick())
|
|
||||||
|
|
||||||
case tickMsg:
|
case tickMsg:
|
||||||
cmds = append(cmds, tick())
|
cmds = append(cmds, tick())
|
||||||
|
|||||||
Reference in New Issue
Block a user