Documentation
¶
Overview ¶
Package sqlguard validates PostgreSQL statements against explicitly registered safety rules without requiring a database connection or driver.
An Engine parses the complete input with the PostgreSQL 17 grammar before it invokes any Rule. It visits top-level statements in input order and, for each statement, visits the root followed by statement-bearing CTEs depth-first in declaration order. Rules run in registration order for every visited statement, and validation stops at the first rejection.
A minimal setup is:
engine, err := sqlguard.NewEngine(sqlguard.EngineOptions{}, applicationRule)
if err != nil {
return err
}
if err := engine.Validate(ctx, query); err != nil {
return err
}
Parser failures and rule violations expose only bounded metadata through ParseError and Violation. Their messages and unwrap chains never retain the submitted SQL or raw parser diagnostics.
Observability is disabled by default. EngineOptions can independently enable Metrics and Logger implementations. For every terminal result, Engine calls each enabled implementation synchronously with bounded ValidationEvent metadata. Sink errors and panics are contained independently and never change the validation result or weaken enforce behavior. Events never contain SQL, arguments, errors, parser diagnostics, or caller-context values.
Engine is safe for concurrent validation after construction. The same Rule, Metrics, or Logger instance may be called concurrently, so implementations must be immutable or protect their own state. Context cancellation is checked before and after parsing and before every rule call. The synchronous CGO parser cannot be interrupted while its C call is in progress; cancellation is observed when parsing returns.
Example ¶
package main
import (
"context"
"errors"
"fmt"
sqlguard "github.com/almostinf/postgres-sqlguard"
"github.com/almostinf/postgres-sqlguard/pkg/rules"
)
func main() {
engine, err := sqlguard.NewEngine(
sqlguard.EngineOptions{},
rules.NewUpdateRequiresWhere(),
)
if err != nil {
fmt.Println("configure sqlguard:", err)
return
}
err = engine.Validate(
context.Background(),
"UPDATE accounts SET active = false",
)
var violation *sqlguard.Violation
if errors.As(err, &violation) {
fmt.Println("rejected by", violation.RuleID())
}
}
Output: rejected by update_requires_where
Index ¶
- Variables
- type Engine
- type EngineOptions
- type Kind
- type Logger
- type Metrics
- type Node
- func (n Node) Bool(name string) (bool, bool)
- func (n Node) Bytes(name string) ([]byte, bool)
- func (n Node) Child(name string) (Node, bool)
- func (n Node) Children(name string) []Node
- func (n Node) Enum(name string) (string, bool)
- func (n Node) Float(name string) (float64, bool)
- func (n Node) Int(name string) (int64, bool)
- func (n Node) Kind() Kind
- func (n Node) String(name string) (string, bool)
- func (n Node) Uint(name string) (uint64, bool)
- func (n Node) Walk(visit func(Node) bool)
- type ParseError
- type ParseErrorCategory
- type Prepared
- type Rule
- type RuleResult
- type Statement
- type ValidationEvent
- type ValidationMode
- type ValidationOutcome
- type Validator
- type Violation
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidPrepared = errors.New("sqlguard: invalid prepared value")
ErrInvalidPrepared indicates that prepared validation received a zero or otherwise invalid Prepared value. It contains no SQL or parsed structure.
Functions ¶
This section is empty.
Types ¶
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is an immutable Validator configured with independently optional observability implementations and the ordered Rules supplied explicitly at construction. It has no global registry or implicit default rules. Once constructed, an Engine is safe for concurrent use when its rules and observability implementations satisfy their concurrency contracts.
func NewEngine ¶
func NewEngine(options EngineOptions, rules ...Rule) (*Engine, error)
NewEngine constructs an Engine from options and rules in registration order. Zero-valued options disable observability. A nil Metrics or Logger interface disables that sink, while an interface containing a typed nil is invalid. It returns no partially configured Engine when any option or registration is invalid.
func (*Engine) Prepare ¶
Prepare synchronously parses the complete SQL input once without evaluating rules. A successful preparation emits no terminal validation outcome. Parse and context failures are emitted once through the Engine's observability implementations before the error is returned.
func (*Engine) Validate ¶
Validate prepares the complete SQL input and validates the resulting parsed representation. It preserves the same errors, traversal, context behavior, and single terminal outcome as calling Prepare followed by ValidatePrepared.
func (*Engine) ValidatePrepared ¶
ValidatePrepared evaluates every registered rule against a successfully prepared value in deterministic statement and registration order. It checks the caller context before prepared-value validity and before each rule, returns the first failure, and emits exactly one terminal outcome.
type EngineOptions ¶
type EngineOptions struct {
// Metrics records terminal validation events. A nil value disables metrics.
Metrics Metrics
// Logger logs terminal validation events. A nil value disables logging.
Logger Logger
}
EngineOptions configures independently optional observability implementations for an Engine. Its zero value disables both metrics and logging. A nil interface disables its sink; an interface containing a typed nil is invalid and causes NewEngine to fail.
type Logger ¶
type Logger interface {
// LogValidation logs one terminal validation event.
LogValidation(event ValidationEvent) error
}
Logger logs terminal validation events. An Engine calls an enabled Logger implementation synchronously once per validation and contains any returned error or panic. An Engine may call the same implementation concurrently, so implementations must either be immutable or synchronize their own state.
type Metrics ¶
type Metrics interface {
// RecordValidation records one terminal validation event.
RecordValidation(event ValidationEvent) error
}
Metrics records terminal validation events. An Engine calls an enabled Metrics implementation synchronously once per validation and contains any returned error or panic. An Engine may call the same implementation concurrently, so implementations must either be immutable or synchronize their own state.
type Node ¶
type Node struct {
// contains filtered or unexported fields
}
Node is an immutable view of one structural PostgreSQL AST node.
type ParseError ¶
type ParseError struct {
// contains filtered or unexported fields
}
ParseError reports that PostgreSQL parsing failed before rule evaluation. It contains only a bounded category and never retains backend diagnostics.
func (*ParseError) Category ¶
func (e *ParseError) Category() ParseErrorCategory
Category returns the bounded parser-failure category.
func (*ParseError) Error ¶
func (*ParseError) Error() string
Error returns a constant message that cannot disclose parser input.
type ParseErrorCategory ¶
type ParseErrorCategory string
ParseErrorCategory identifies a bounded class of parsing failure. Categories are safe for programmatic handling and never contain parser diagnostics.
const ( // ParseErrorUnknown indicates a parsing failure without a more specific // public category. ParseErrorUnknown ParseErrorCategory = "unknown" // ParseErrorSyntax indicates that PostgreSQL rejected the input syntax. ParseErrorSyntax ParseErrorCategory = "syntax" )
type Prepared ¶
type Prepared struct {
// contains filtered or unexported fields
}
Prepared is an opaque immutable representation of completely parsed SQL. A successfully prepared value may be copied and validated concurrently by any Engine. The zero value is invalid and is rejected by ValidatePrepared.
type Rule ¶
type Rule interface {
// ID returns a stable identifier used for programmatic error handling.
ID() string
// Evaluate inspects one immutable parsed statement and returns a bounded
// decision without constructing or returning an error.
Evaluate(ctx context.Context, statement Statement) RuleResult
}
Rule evaluates one parsed statement. The same Rule instance may be invoked concurrently by separate validation calls, so implementations must either be immutable or synchronize their own state.
type RuleResult ¶
type RuleResult struct {
// contains filtered or unexported fields
}
RuleResult is the bounded outcome of Rule evaluation. Its zero value rejects validation so accidentally omitted decisions fail closed.
func Reject ¶
func Reject() RuleResult
Reject returns a result that stops validation with a policy violation.
func (RuleResult) Rejected ¶
func (r RuleResult) Rejected() bool
Rejected reports whether validation must stop for this result.
type Statement ¶
type Statement struct {
// contains filtered or unexported fields
}
Statement is an immutable view of one statement root. Its values are valid for the duration of a Rule call and may be copied, but rules must not retain them after Evaluate returns.
type ValidationEvent ¶
type ValidationEvent struct {
// contains filtered or unexported fields
}
ValidationEvent is immutable bounded metadata for one terminal validation result. It never contains SQL, arguments, errors, parser diagnostics, or caller-context values.
func (ValidationEvent) Mode ¶
func (e ValidationEvent) Mode() ValidationMode
Mode returns the bounded validation execution mode.
func (ValidationEvent) Outcome ¶
func (e ValidationEvent) Outcome() ValidationOutcome
Outcome returns the bounded terminal validation result.
func (ValidationEvent) RuleID ¶
func (e ValidationEvent) RuleID() string
RuleID returns the stable rejecting rule identifier for a policy violation and an empty string for every other outcome.
type ValidationMode ¶
type ValidationMode string
ValidationMode identifies a bounded validation execution mode for observability implementations.
const ( // ValidationModeEnforce identifies validation that rejects unsafe or // unparseable input. ValidationModeEnforce ValidationMode = "enforce" )
type ValidationOutcome ¶
type ValidationOutcome string
ValidationOutcome identifies a bounded terminal validation result for observability implementations.
const ( // ValidationOutcomeAllowed indicates that parsing and all registered rule // evaluations succeeded. ValidationOutcomeAllowed ValidationOutcome = "allowed" // ValidationOutcomePolicyViolation indicates that a registered rule // rejected a statement. ValidationOutcomePolicyViolation ValidationOutcome = "policy_violation" // ValidationOutcomeParserFailure indicates that the PostgreSQL parser // rejected the complete input. ValidationOutcomeParserFailure ValidationOutcome = "parser_failure" // ValidationOutcomeCanceled indicates that validation stopped because the // caller context was canceled or its deadline expired. ValidationOutcomeCanceled ValidationOutcome = "canceled" // ValidationOutcomeInvalidPrepared indicates that prepared validation // received a zero or otherwise invalid Prepared value. ValidationOutcomeInvalidPrepared ValidationOutcome = "invalid_prepared" )
type Validator ¶
type Validator interface {
// Validate checks the complete SQL input and returns the first failure.
Validate(ctx context.Context, sql string) error
}
Validator checks SQL against a configured policy without requiring a database connection or a database-driver dependency.
type Violation ¶
type Violation struct {
// contains filtered or unexported fields
}
Violation reports that a validation rule rejected a statement. It stores only the stable identifier of the responsible rule.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
example
|
|
|
pgx
Package pgxexample demonstrates how to place a sqlguard validator in front of a narrow pgx execution boundary.
|
Package pgxexample demonstrates how to place a sqlguard validator in front of a narrow pgx execution boundary. |
|
pgx-prepared
Package pgxprepared demonstrates a bounded cache of SQLGuard prepared values in front of pgx-style Exec, Query, and QueryRow operations.
|
Package pgxprepared demonstrates a bounded cache of SQLGuard prepared values in front of pgx-style Exec, Query, and QueryRow operations. |
|
internal
|
|
|
parser
Package parser contains the internal PostgreSQL parsing boundary.
|
Package parser contains the internal PostgreSQL parsing boundary. |
|
testutil
Package testutil contains helpers shared by postgres-sqlguard tests.
|
Package testutil contains helpers shared by postgres-sqlguard tests. |
|
testutil/cmd/benchcharts
command
Command benchcharts generates deterministic SVG charts from release benchmark evidence.
|
Command benchcharts generates deterministic SVG charts from release benchmark evidence. |
|
testutil/cmd/sizeprobe
command
Package main provides the representative executable used to compare parser-backend footprint.
|
Package main provides the representative executable used to compare parser-backend footprint. |
|
pkg
|
|
|
observability/prometheus
Package prometheus provides the official Prometheus metrics integration for postgres-sqlguard validation outcomes.
|
Package prometheus provides the official Prometheus metrics integration for postgres-sqlguard validation outcomes. |
|
observability/slog
Package slog provides the official log/slog integration for postgres-sqlguard validation outcomes.
|
Package slog provides the official log/slog integration for postgres-sqlguard validation outcomes. |
|
rules
Package rules provides opt-in PostgreSQL-aware validation rules for use with sqlguard.
|
Package rules provides opt-in PostgreSQL-aware validation rules for use with sqlguard. |