validator

package
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package validator provides input quality checks for GTD entries and related user-facing fields. All checks use simple string operations (no complex backtracking regexes) to prevent ReDoS on adversarial input.

Index

Constants

View Source
const (
	// MaxTagLen is the maximum byte length for a single tag.
	MaxTagLen = 100
	// MaxTagCount is the maximum number of tags allowed in one call.
	MaxTagCount = 20
	// MaxFieldLen is the maximum byte length for text fields (title, context, decision, rationale).
	MaxFieldLen = 5000
)
View Source
const KindGeneral = "general"

KindGeneral is the default task kind used when none is supplied; centralised here so MCP / HTTP handlers don't duplicate the string literal (goconst).

View Source
const MaxBranchNameLen = 255

MaxBranchNameLen is the maximum rune count allowed for a task's branch_name field. Not a git limit (git itself allows much longer names) — a UX/DB-hygiene cap shared by the MCP and HTTP entry points.

Variables

View Source
var GitHubPRURLRe = regexp.MustCompile(`^https://github\.com/[^/]+/[^/]+/pull/\d+(/)?$`)

GitHubPRURLRe matches GitHub PR URLs of the form https://github.com/{owner}/{repo}/pull/{number}[/] SECURITY: only used for format validation — no HTTP fetch is ever made. Single canonical definition shared by handler and mcp packages to avoid drift.

View Source
var RepoNameRe = regexp.MustCompile(`^[a-zA-Z0-9_.\-]{1,100}$`)

RepoNameRe enforces a safe slug format for project repo_name values. Previously defined only inside internal/mcp/tools_gtd.go — hoisted here so MCP tools, HTTP handlers, and the store layer (PG + SQLite) share a single source of truth for the format instead of drifting independently.

Not a security boundary by itself (repo_name is never interpolated into a subprocess argv or filesystem path — see RepoSlugRe in repo_regex.go for the slug that IS used in `gh -R <slug>`), but keeps the column semantically queryable and rejects control characters / path-traversal sequences from entering the DB.

View Source
var RepoSlugRe = regexp.MustCompile(`^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$`)

RepoSlugRe validates owner/repo style GitHub slugs at the reconcile boundary where the slug flows into `gh -R <slug>` CLI invocation. Rejects whitespace, shell metas, path traversal, control chars.

SECURITY: must match before passing the slug to a subprocess command. GitHub itself permits ASCII letters, digits, hyphens, underscores, and dots in both owner and repo segments; rejecting anything else is conservative but eliminates shell-injection / path-traversal / newline-smuggling vectors.

View Source
var ValidTaskKinds = []string{KindGeneral, "fix-pr", "feature", "refactor", "research", "chore"}

ValidTaskKinds is the allowlist of accepted task kind values. The CHECK constraint in the DB migration (000044) is a secondary defence; this allowlist is the primary client-side gate.

Functions

func CheckCommandField

func CheckCommandField(name, value string) string

CheckCommandField rejects strings that contain ASCII control characters (< 0x20, excluding tab \t=0x09), carriage return \r, newline \n, or null byte \x00. These characters are adversarial in shell command or expected- output fields: a prompt-injected agent could store "\ngit push" to make the LLM interpret the second line as a separate shell instruction.

Returns a non-empty reason when the value is unsafe, empty string otherwise.

func CheckDecisionNoise

func CheckDecisionNoise(title, ctx, decision, rationale string) string

CheckDecisionNoise validates the four required text fields of log_decision. Returns a non-empty reason when any field is noisy, empty string otherwise.

func CheckField

func CheckField(name, value string) string

CheckField returns a non-empty reason when value violates noise heuristics for a named text field (byte-length cap, <script> tag, markdown fence). An empty return means the value is acceptable. Shared by both the MCP and HTTP entry points for log_decision / set_session_handoff / worksession title fields — this is the single implementation; callers must not duplicate the <script> / fence detection logic.

func CheckHandoffNoise

func CheckHandoffNoise(intent, contextSummary string) string

CheckHandoffNoise validates the text fields of set_session_handoff. Returns a non-empty reason when any field is noisy, empty string otherwise.

func CheckKindFields

func CheckKindFields(kind, description string) []string

CheckKindFields verifies that description contains the per-kind required markers. Returns a slice of warning strings; empty means no issues. All checks use plain strings.Contains — no backtracking regex.

func CheckTaskInput

func CheckTaskInput(description, kind string) []string

CheckTaskInput is the single source of truth for "which quality checks run against a task's description" — CheckVagueness (field fixed at "description", matching every call site's existing warning-message text) composed with CheckKindFields. Every entry point that creates or accepts a task (HTTP CreateTask, MCP add_task, HTTP/MCP proposal-accept single + batch) MUST route through this function instead of inlining the same two calls, so a future entry point can't silently drift from what the others check.

Pure function — no I/O, no env reads. Returns a slice of warning strings; empty means no issues detected.

func CheckVagueness

func CheckVagueness(field, text, kind string) []string

CheckVagueness inspects text for vague markers and patterns. field is the human-readable field name used in warning messages (e.g. "description").

kind == "chore" skips all checks — chore tasks have no required body structure.

Returns a slice of warning strings; empty means no issues detected. All checks use plain string operations (no backtracking regex) to avoid ReDoS.

func IsValidKind

func IsValidKind(kind string) bool

IsValidKind reports whether kind is a known task kind.

func IsValidRepoName

func IsValidRepoName(name string) bool

IsValidRepoName reports whether name is acceptable as a project repo_name value. Empty string is valid — repo_name is optional; callers that require a non-empty value must check that separately.

func SanitizeTags

func SanitizeTags(raw []string) ([]string, string)

SanitizeTags accepts a slice of raw tag strings (already split from a comma-separated input) and returns a cleaned slice. Rules applied per tag:

  • Tags longer than MaxTagLen bytes are dropped.
  • Characters outside [\w\-_./] are stripped; if the result is empty the tag is dropped.

If the input slice exceeds MaxTagCount entries, a nil slice and a rejection reason are returned so the caller can respond with -32602 (MCP) or 400 (HTTP).

func StrictModeEnabled

func StrictModeEnabled() bool

StrictModeEnabled reports whether WBT_STRICT_VAGUENESS is set to a truthy value in the server environment, as understood by strconv.ParseBool ("1", "t", "T", "true", "True", "TRUE"). Never sourced from tool arguments or request bodies (user-controlled) — this is the single, server-side read of the env var; every HTTP handler and MCP tool that gates task creation/acceptance on CheckTaskInput's warnings reads the gate decision through this function instead of duplicating the os.Getenv + ParseBool pair.

func ValidateBranchName

func ValidateBranchName(s string) string

ValidateBranchName checks the invariants shared by the MCP (tools_gtd.go) and HTTP (gtd_handler.go) layers for a task's branch_name field:

  • Length is counted in runes, not bytes. A 255-character CJK branch name is well within git's own limits but would trip a byte-length check because each character is 2-3 bytes in UTF-8 — counting bytes here previously caused the MCP and HTTP paths to disagree on the same input (sprint 8-7 gap E).
  • ASCII control characters (< 0x20), DEL (0x7F), and Unicode category C ("Other": Cc control, Cf format like U+200B zero-width space / U+FEFF BOM, Co private-use, Cs surrogate) are rejected outright.

Returns a non-empty, user-facing error message on violation, or an empty string when s is acceptable — including the empty string itself; callers decide separately whether branch_name is required.

Types

This section is empty.

Jump to

Keyboard shortcuts

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