security

package
v0.32.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package security provides input sanitization and output scanning for fullsend's agent entrypoints. These scanners run at the boundaries:

  • Input boundary: before untrusted text (issue bodies, PR descriptions, code comments, context files) reaches agent processing
  • Output boundary: before agent-generated text (PR comments, issue comments) is posted via the forge API

The scanners are adapted from Hermes Agent's production security controls, ported to Go for integration into fullsend's CLI entrypoints.

See: experiments/hermes-security-patterns/ for the Python prototypes and evaluation results.

Index

Constants

View Source
const SandboxHooksDir = sandbox.SandboxWorkspace + "/.claude/hooks"

SandboxHooksDir is the path where hook scripts are installed inside the sandbox. Must match sandbox.SandboxWorkspace + "/.claude/hooks".

Variables

View Source
var CanaryPostToolHook []byte
View Source
var CanaryPreToolHook []byte
View Source
var ContextSuppressPostToolHook []byte
View Source
var SSRFPreToolHook []byte
View Source
var ScannableFiles = map[string]bool{
	"agents.md":               true,
	".cursorrules":            true,
	"claude.md":               true,
	".claude.md":              true,
	"soul.md":                 true,
	".hermes.md":              true,
	"hermes.md":               true,
	"gemini.md":               true,
	".gemini.md":              true,
	"copilot-instructions.md": true,
	"skill.md":                true,
	"plugin.json":             true,
	".lsp.json":               true,
}

ScannableFiles is the set of filenames that should be scanned for prompt injection before being loaded into agent context.

View Source
var SecretRedactPostToolHook []byte
View Source
var TirithCheckHook []byte
View Source
var ToolAllowlistPreToolHook []byte
View Source
var UnicodePostToolHook []byte

Functions

func AppendFinding

func AppendFinding(path string, tf TracedFinding) error

AppendFinding writes a traced finding as a JSON line to the given file path. It computes a SHA-256 hash chain: each entry's hash covers the previous entry's hash and the current entry's payload, making the log tamper-evident.

func DestroyMLScanner

func DestroyMLScanner()

DestroyMLScanner is a no-op stub when ONNX runtime is not available.

func GenerateClaudeSettings

func GenerateClaudeSettings(hooks ClaudeSandboxHooks) ([]byte, error)

GenerateClaudeSettings produces a .claude/settings.json with security hooks configured according to hooks. Returns the JSON bytes.

func GenerateTraceID

func GenerateTraceID() string

GenerateTraceID returns a UUID v4 string for correlating security findings across scanning phases (host pre-step, sandbox pre-agent, runtime hooks, post-agent output scan).

func HasCriticalFindings added in v0.2.0

func HasCriticalFindings(findings []Finding) bool

HasCriticalFindings reports whether any finding has critical severity.

func HookFiles

func HookFiles(hooks ClaudeSandboxHooks) map[string][]byte

HookFiles returns a map of filename -> content for all enabled hook scripts.

func IsShellSafeTraceID added in v0.28.0

func IsShellSafeTraceID(id string) bool

IsShellSafeTraceID returns true if the trace ID consists only of lowercase hex and dashes in UUID shape — the property that makes it safe for shell interpolation. Unlike IsValidTraceID it accepts any version/variant, because a trace id adopted from an inbound W3C traceparent (issue #2779) is not necessarily UUID v4. The charset restriction is the entire safety argument; version bits carry no interpolation risk.

func IsValidTraceID

func IsValidTraceID(id string) bool

IsValidTraceID returns true if the trace ID is safe for shell interpolation.

func MLScanAvailable

func MLScanAvailable() bool

MLScanAvailable reports whether the native ONNX ML scanner is compiled in.

func ShouldScan

func ShouldScan(filename string) bool

ShouldScan reports whether a filename should be scanned for injection.

Types

type ChainVerification added in v0.19.0

type ChainVerification struct {
	Valid     bool
	Entries   int
	BrokenAt  int    // 0-indexed; -1 if valid
	BrokenMsg string // empty if valid
}

ChainVerification holds the result of verifying a findings JSONL file.

func VerifyChain added in v0.19.0

func VerifyChain(path string) (ChainVerification, error)

VerifyChain reads a findings JSONL file and verifies the hash chain integrity. Returns a ChainVerification indicating whether the chain is intact. Entries without hash fields (from older versions) are skipped.

type ClaudeSandboxHooks added in v0.14.0

type ClaudeSandboxHooks struct {
	// contains filtered or unexported fields
}

ClaudeSandboxHooks configures Claude Code PreToolUse/PostToolUse security hooks. A nil internal hook config uses the same defaults as an unset harness security block.

func ClaudeSandboxHooksFromHarness added in v0.14.0

func ClaudeSandboxHooksFromHarness(h *harness.Harness) ClaudeSandboxHooks

ClaudeSandboxHooksFromHarness extracts sandbox hook settings from a harness.

func (ClaudeSandboxHooks) TirithFailOn added in v0.14.0

func (c ClaudeSandboxHooks) TirithFailOn() string

TirithFailOn returns the Tirith severity threshold env value, or empty when unset.

func (ClaudeSandboxHooks) TirithRequired added in v0.14.0

func (c ClaudeSandboxHooks) TirithRequired() bool

TirithRequired reports whether Tirith Bash scanning should be required in the sandbox.

type ContextInjectionScanner

type ContextInjectionScanner struct {
	// contains filtered or unexported fields
}

ContextInjectionScanner detects prompt injection patterns in project context files (AGENTS.md, .cursorrules, CLAUDE.md, etc.) before they are loaded into an agent's system prompt. Adapted from Hermes Agent's prompt_builder.py injection scanning.

func NewContextInjectionScanner

func NewContextInjectionScanner() *ContextInjectionScanner

NewContextInjectionScanner creates a scanner with the default pattern set.

func (*ContextInjectionScanner) Name

func (c *ContextInjectionScanner) Name() string

func (*ContextInjectionScanner) Scan

type Finding

type Finding struct {
	Scanner  string // "secret_redactor", "ssrf_validator", "context_injection", "unicode_normalizer"
	Name     string // pattern name or category
	Severity string // "critical", "high", "medium"
	Detail   string // human-readable description
	Position int    // byte offset in original text, -1 if N/A
}

Finding represents a single security issue detected by a scanner.

type Pipeline

type Pipeline struct {
	// contains filtered or unexported fields
}

Pipeline chains multiple scanners in sequence. Each scanner's sanitized output feeds into the next scanner's input.

func InputPipeline

func InputPipeline() *Pipeline

InputPipeline returns the standard input scanning pipeline for untrusted text entering the agent. Order matters:

  1. UnicodeNormalizer — strip invisible chars, normalize fullwidth
  2. ContextInjectionScanner — detect prompt injection patterns

func NewPipeline

func NewPipeline(scanners ...Scanner) *Pipeline

NewPipeline creates a scanner pipeline from the given scanners. Scanners run in order; place normalizers first, detectors after.

func OutputPipeline

func OutputPipeline() *Pipeline

OutputPipeline returns the standard output scanning pipeline for agent-generated text before posting to the forge.

  1. UnicodeNormalizer — strip invisible chars, normalize fullwidth
  2. SecretRedactor — redact API keys, tokens, credentials

func (*Pipeline) Scan

func (p *Pipeline) Scan(text string) ScanResult

Scan runs all scanners in sequence. Returns the aggregate result. The pipeline is fail-open for sanitization (each scanner transforms the text) but fail-closed for safety (any scanner marking unsafe makes the whole result unsafe).

type SSRFValidator

type SSRFValidator struct {
	// contains filtered or unexported fields
}

SSRFValidator validates URLs against blocked networks, hostnames, and schemes to prevent Server-Side Request Forgery. Adapted from Hermes Agent's URL validation.

func NewSSRFValidator

func NewSSRFValidator() *SSRFValidator

NewSSRFValidator creates a validator with the default blocklists.

func (*SSRFValidator) Name

func (s *SSRFValidator) Name() string

func (*SSRFValidator) Scan

func (s *SSRFValidator) Scan(text string) ScanResult

Scan implements the Scanner interface. Extracts URLs from text and validates each one. Uses regex-based extraction (matching the Python hook's URL_PATTERN) to handle URLs embedded in markdown, JSON, etc.

func (*SSRFValidator) ValidateRedirectChain

func (s *SSRFValidator) ValidateRedirectChain(urls []string) ScanResult

ValidateRedirectChain checks each URL in a redirect chain.

func (*SSRFValidator) ValidateURL

func (s *SSRFValidator) ValidateURL(rawURL string, resolveDNS bool) ScanResult

ValidateURL checks a single URL for SSRF risk. Set resolveDNS to true to resolve hostnames and check resolved IPs.

type ScanResult

type ScanResult struct {
	Safe      bool
	Findings  []Finding
	Sanitized string // cleaned/redacted version of input (empty if unchanged)
}

ScanResult holds the outcome of a security scan.

func RunMLScan

func RunMLScan(_ string, _ bool) ScanResult

RunMLScan is a no-op stub when ONNX runtime is not available.

type Scanner

type Scanner interface {
	// Name returns the scanner identifier.
	Name() string

	// Scan checks text for security issues. Returns a ScanResult with
	// findings and optionally a sanitized version of the input.
	Scan(text string) ScanResult
}

Scanner is the interface for all security scanners.

type SecretRedactor

type SecretRedactor struct {
	// contains filtered or unexported fields
}

SecretRedactor scans text for API keys, tokens, credentials, and sensitive patterns, replacing them with masked versions. Adapted from Hermes Agent's agent/redact.py.

func NewSecretRedactor

func NewSecretRedactor() *SecretRedactor

NewSecretRedactor creates a redactor with the default pattern set.

func (*SecretRedactor) Name

func (s *SecretRedactor) Name() string

func (*SecretRedactor) Scan

func (s *SecretRedactor) Scan(text string) ScanResult

type TracedFinding

type TracedFinding struct {
	TraceID   string `json:"trace_id"`
	Timestamp string `json:"timestamp"`
	Phase     string `json:"phase"` // "host_input", "sandbox_context", "hook_pretool", "hook_posttool", "host_output"
	PrevHash  string `json:"prev_hash"`
	Hash      string `json:"hash"`
	Finding
}

TracedFinding is a Finding enriched with trace and phase metadata for the JSONL audit log. PrevHash and Hash form a SHA-256 chain: each entry's Hash covers PrevHash and the rest of the entry, making tampering detectable.

type UnicodeNormalizer

type UnicodeNormalizer struct{}

UnicodeNormalizer strips invisible Unicode characters and normalizes fullwidth characters to prevent command obfuscation and hidden payload injection. Adapted from Hermes Agent's approval.py.

func NewUnicodeNormalizer

func NewUnicodeNormalizer() *UnicodeNormalizer

NewUnicodeNormalizer creates a UnicodeNormalizer.

func (*UnicodeNormalizer) Name

func (u *UnicodeNormalizer) Name() string

func (*UnicodeNormalizer) Scan

func (u *UnicodeNormalizer) Scan(text string) ScanResult

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL