· 2 min read · ← All posts
Ardan Labs Go Security Agents

Field notes from working through example 24 of Ardan Labs’ Ultimate AI course by Bill Kennedy and Florin Pățan (Apache 2.0). My fork: PratikDhanave/ai-training. Thank you Bill and Florin for teaching this material — the patterns in this post are derived from the course; the production reflections at the end are mine.

What the example teaches

The naive shell tool:

func runCommand(args string) string {
    out, _ := exec.Command("sh", "-c", args).Output()
    return string(out)
}

The hardened version:

What it looks like

type ShellTool struct {
    AllowedBinaries map[string]bool
    Permissions     func(user, binary string) bool
    MaxDuration     time.Duration
    MaxOutputBytes  int
    Audit           AuditLog
}

func (t *ShellTool) Run(ctx context.Context, user, binary string, args []string) (string, error) {
    if !t.AllowedBinaries[binary] {
        return "", ErrBinaryNotAllowed
    }
    if !t.Permissions(user, binary) {
        return "", ErrPermissionDenied
    }
    if err := validateArgs(args); err != nil {
        return "", err
    }
    ctx, cancel := context.WithTimeout(ctx, t.MaxDuration)
    defer cancel()
    out, err := exec.CommandContext(ctx, binary, args...).Output()
    t.Audit.Log(user, binary, args, len(out), err)
    return truncate(string(out), t.MaxOutputBytes), err
}

What I learned

sh -c "$user_input" is never the right answer. Always invoke the binary directly with separated argv. The temptation to use shell for “convenience” (pipes, redirects, env vars) is the temptation that gets the system owned.

RBAC per binary is more useful than RBAC per user. A user might be allowed to run grep but not rm. The grain matters.

Production connection

The Genie tool-calling layer applies a similar pattern — pkg/safety plugin chain runs against every tool invocation, including binary allow-list + argument validation. The coding agent example (#31) without these guards is a demo; with them, it’s something you can actually run against your own filesystem.


Credit & reference. This post is field notes on example 24 from Ardan Labs’ Ultimate AI by Bill Kennedy + Florin Pățan, licensed Apache 2.0. The original example: cmd/examples/example24-tool-security/. My fork with notes: PratikDhanave/ai-training. Highly recommend the course for anyone building AI applications in Go — the material is rigorous and the Kronk + yzma + llama.cpp pipeline gives you hardware-accelerated local inference end-to-end. Thank you, Bill and Florin.

← Back to all posts