Files
Agent-System/internal/engineeringdashboard/runner.go
T
weichenandCursor a07ba1065b feat(engineering-dashboard): U2-U4 docs git and runtime collectors
Add allowlisted markdown indexing, read-only git plumbing via injected
runners, and docker/readiness collection with path and loopback guards.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 07:03:32 +00:00

176 lines
4.2 KiB
Go

package engineeringdashboard
import (
"bytes"
"context"
"errors"
"fmt"
"os/exec"
"strings"
"time"
)
const (
defaultCommandTimeout = 2 * time.Second
defaultOutputLimit = 512 * 1024
)
// CommandRunner executes fixed argv without a shell.
type CommandRunner interface {
Run(ctx context.Context, dir string, name string, args ...string) (stdout string, stderr string, err error)
}
// ExecRunner is the production runner using os/exec.
type ExecRunner struct {
Timeout time.Duration
OutputLimit int
}
func (r ExecRunner) Run(ctx context.Context, dir string, name string, args ...string) (string, string, error) {
if name == "" {
return "", "", errors.New("empty command name")
}
if err := denyDangerousGitArgs(name, args); err != nil {
return "", "", err
}
timeout := r.Timeout
if timeout <= 0 {
timeout = defaultCommandTimeout
}
limit := r.OutputLimit
if limit <= 0 {
limit = defaultOutputLimit
}
cctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
cmd := exec.CommandContext(cctx, name, args...)
cmd.Dir = dir
var stdout, stderr bytes.Buffer
cmd.Stdout = &limitedWriter{buf: &stdout, limit: limit}
cmd.Stderr = &limitedWriter{buf: &stderr, limit: limit}
err := cmd.Run()
out := sanitizeOutput(stdout.String())
errOut := sanitizeOutput(stderr.String())
if cctx.Err() == context.DeadlineExceeded {
return out, errOut, fmt.Errorf("command timed out: %s", name)
}
return out, errOut, err
}
type limitedWriter struct {
buf *bytes.Buffer
limit int
n int
}
func (w *limitedWriter) Write(p []byte) (int, error) {
remain := w.limit - w.n
if remain <= 0 {
return len(p), nil
}
if len(p) > remain {
p = p[:remain]
}
n, err := w.buf.Write(p)
w.n += n
return len(p), err
}
func sanitizeOutput(s string) string {
lower := strings.ToLower(s)
for _, needle := range []string{"password=", "secret=", "token=", "access_token=", "api_key="} {
if idx := strings.Index(lower, needle); idx >= 0 {
return s[:idx] + needle + "[redacted]"
}
}
return s
}
func denyDangerousGitArgs(name string, args []string) error {
base := name
if i := strings.LastIndex(name, "/"); i >= 0 {
base = name[i+1:]
}
if base != "git" {
return nil
}
banned := map[string]bool{
"fetch": true, "push": true, "pull": true, "clone": true,
"commit": true, "checkout": true, "merge": true, "rebase": true,
"reset": true, "clean": true, "add": true, "tag": true,
}
for _, a := range args {
if strings.HasPrefix(a, "-") {
continue
}
if banned[a] {
return fmt.Errorf("git write/network command denied: %s", a)
}
break
}
joined := strings.Join(args, " ")
if strings.Contains(joined, "&&") || strings.Contains(joined, "|") || strings.Contains(joined, ";") {
return errors.New("shell metacharacters denied")
}
return nil
}
// FakeRunner records invocations for tests.
type FakeRunner struct {
Calls [][]string
Map map[string]FakeResult // key: name + " " + joined args
}
// FakeResult is a canned command result.
type FakeResult struct {
Stdout string
Stderr string
Err error
}
func (f *FakeRunner) Run(_ context.Context, _ string, name string, args ...string) (string, string, error) {
key := name + " " + strings.Join(args, " ")
f.Calls = append(f.Calls, append([]string{name}, args...))
if f.Map != nil {
if res, ok := f.Map[key]; ok {
return res.Stdout, res.Stderr, res.Err
}
// prefix match for flexibility
for k, res := range f.Map {
if strings.HasPrefix(key, k) {
return res.Stdout, res.Stderr, res.Err
}
}
}
return "", "", fmt.Errorf("no fake result for %q", key)
}
func (f *FakeRunner) AssertNoForbidden(t interface{ Fatalf(string, ...any) }) {
for _, call := range f.Calls {
if len(call) == 0 {
continue
}
name := call[0]
base := name
if i := strings.LastIndex(name, "/"); i >= 0 {
base = name[i+1:]
}
if base == "sh" || base == "bash" || base == "zsh" {
t.Fatalf("forbidden shell observed: %v", call)
}
joined := strings.Join(call, " ")
if base == "git" {
for _, a := range call[1:] {
if strings.HasPrefix(a, "-") {
continue
}
switch a {
case "fetch", "push", "pull", "clone", "commit", "checkout", "merge", "rebase", "reset", "clean", "add", "tag":
t.Fatalf("forbidden git command observed: %s", joined)
}
break
}
}
}
}