Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/daemon/broadcaster.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Event struct {
SHA string `json:"sha"`
Agent string `json:"agent,omitempty"`
Verdict string `json:"verdict,omitempty"`
Findings string `json:"findings,omitempty"`
Error string `json:"error,omitempty"`
}

Expand Down
1 change: 1 addition & 0 deletions internal/daemon/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func interpolate(cmd string, event Event) string {
"{sha}", shellEscape(event.SHA),
"{agent}", shellEscape(event.Agent),
"{verdict}", shellEscape(event.Verdict),
"{findings}", shellEscape(event.Findings),
"{error}", shellEscape(event.Error),
)
return r.Replace(cmd)
Expand Down
38 changes: 21 additions & 17 deletions internal/daemon/hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func TestInterpolate(t *testing.T) {
SHA: "abc123def456",
Agent: "codex",
Verdict: "F",
Findings: "High — missing input validation in handler",
Error: "agent timeout",
}

Expand All @@ -96,6 +97,10 @@ func TestInterpolate(t *testing.T) {
"log {error}",
"log " + q("agent timeout"),
},
{
"process {findings}",
"process " + q("High — missing input validation in handler"),
},
{
"",
"",
Expand Down Expand Up @@ -124,25 +129,24 @@ func TestInterpolateShellInjection(t *testing.T) {
}

for _, payload := range payloads {
event := Event{JobID: 1, Repo: "/repo", Error: payload}
// Test via both {error} and {findings} since findings contain arbitrary agent output
event := Event{JobID: 1, Repo: "/repo", Error: payload, Findings: payload}
got := interpolate("echo {error}", event)
gotFindings := interpolate("echo {findings}", event)

prefix := "echo "
if !strings.HasPrefix(got, prefix) || len(got) <= len(prefix)+1 {
t.Fatalf("payload %q: unexpected format (too short or wrong prefix): %q", payload, got)
}
val := got[len(prefix):]

// The value must be fully enclosed in single quotes on all platforms.
if val[0] != '\'' || val[len(val)-1] != '\'' {
t.Errorf("payload %q: not single-quoted: %q", payload, got)
}

// The payload content must be present inside the quoted region (not dropped).
// Use an unambiguous substring that survives escaping.
substr := payload[:4] // first 4 chars are always safe to check
if !strings.Contains(val, substr) {
t.Errorf("payload %q: escaped value doesn't contain expected substring %q: %q", payload, substr, val)
for _, result := range []string{got, gotFindings} {
prefix := "echo "
if !strings.HasPrefix(result, prefix) || len(result) <= len(prefix)+1 {
t.Fatalf("payload %q: unexpected format (too short or wrong prefix): %q", payload, result)
}
val := result[len(prefix):]
if val[0] != '\'' || val[len(val)-1] != '\'' {
t.Errorf("payload %q: not single-quoted: %q", payload, result)
}
substr := payload[:4]
if !strings.Contains(val, substr) {
t.Errorf("payload %q: escaped value doesn't contain expected substring %q: %q", payload, substr, val)
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions internal/daemon/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ func (wp *WorkerPool) processJob(workerID string, job *storage.ReviewJob) {
SHA: job.GitRef,
Agent: agentName,
Verdict: verdict,
Findings: output,
})
}

Expand Down
Loading