Documentation
¶
Index ¶
- func Format(input string, opts FormatOptions) (string, error)
- func FormatQuery(q *Query, opts FormatOptions) string
- func HighlightANSIText(input string) string
- func IsSafeIdentifier(s string) bool
- func Lint(input string, cfg LintConfig) (*Query, []LintIssue)
- func OutputName(item SelectItem) string
- func Validate(q *Query, schema Schema) error
- type AggregateExpr
- type AggregateFunc
- type Analysis
- type Assignment
- type CTE
- type CatalogOption
- type CompareExpr
- type CompareOp
- type CompileOptions
- type CompiledSQL
- type Config
- type DataClass
- type DeleteBuilder
- type Dialect
- type Entity
- type Expr
- func And(exprs ...Expr) Expr
- func Eq(field string, value any) Expr
- func Gt(field string, value any) Expr
- func Gte(field string, value any) Expr
- func In(field string, values ...any) Expr
- func IsNotNull(field string) Expr
- func IsNull(field string) Expr
- func Lt(field string, value any) Expr
- func Lte(field string, value any) Expr
- func Ne(field string, value any) Expr
- func Not(expr Expr) Expr
- func Or(exprs ...Expr) Expr
- type Field
- type FieldPolicy
- type FieldRef
- type FieldRequirement
- type FieldType
- type FieldUsage
- type FormatOptions
- type FormatStyle
- type HighlightStyle
- type InsertBuilder
- type Join
- type JoinRequirement
- type JoinType
- type LintConfig
- type LintIssue
- type LogicalExpr
- type LogicalOp
- type NotExpr
- type Operation
- type Order
- type ParseError
- type Policy
- type PolicyIssue
- type Query
- type RequirementSet
- type ResolvedSource
- type Row
- type Schema
- type ScopeFunc
- type SelectItem
- type UpdateBuilder
- type Value
- type ValueType
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Format ¶
func Format(input string, opts FormatOptions) (string, error)
Format parses input and returns canonical GuardSQL text.
func FormatQuery ¶
func FormatQuery(q *Query, opts FormatOptions) string
FormatQuery formats a parsed GuardSQL query.
func HighlightANSIText ¶
HighlightANSIText applies terminal ANSI colors to GuardSQL keywords and literals. It is best-effort and intended for display, not parsing.
func IsSafeIdentifier ¶ added in v0.3.0
IsSafeIdentifier reports whether s is a valid unquoted SQL identifier: an ASCII letter or underscore, followed by ASCII letters, digits, or underscores, up to 128 bytes.
Identifiers (table and column names) cannot be bound as query parameters — most drivers only parameterize values — so they must be emitted into SQL text. Restricting them to this charset (and rejecting quotes, whitespace, and non-ASCII homoglyphs) makes identifier-based SQL injection structurally impossible, independent of quoting. Databases whose schema uses only unquoted-style identifiers can therefore be locked down completely.
func Lint ¶
func Lint(input string, cfg LintConfig) (*Query, []LintIssue)
Lint parses and validates a query, then applies static SaaS safety checks.
func OutputName ¶
func OutputName(item SelectItem) string
OutputName returns the result column name for a SELECT item.
Types ¶
type AggregateExpr ¶
type AggregateExpr struct {
Func AggregateFunc
Field string
Star bool
}
AggregateExpr is an aggregate function call in SELECT.
type AggregateFunc ¶
type AggregateFunc string
AggregateFunc identifies a supported aggregate function.
const ( AggCount AggregateFunc = "COUNT" AggSum AggregateFunc = "SUM" AggAvg AggregateFunc = "AVG" AggMin AggregateFunc = "MIN" AggMax AggregateFunc = "MAX" )
type Analysis ¶
type Analysis struct {
Operation Operation
Sources []ResolvedSource
Fields []FieldRequirement
Joins []JoinRequirement
Functions []AggregateFunc
CTEs []Analysis
NestedSources []Analysis
HasAggregate bool
}
Analysis is the resolved, read-only summary of a parsed query.
func Analyze ¶
Analyze resolves a parsed query against a schema and returns the resources, fields, functions, and joins the query requires. It does not mutate the query.
func (Analysis) Requirements ¶
func (a Analysis) Requirements() RequirementSet
Requirements returns a de-duplicated requirement set for authorization and policy adapters.
type Assignment ¶ added in v0.3.0
Assignment sets a column to a value in an INSERT or UPDATE.
type CatalogOption ¶ added in v0.3.0
type CatalogOption func(*catalogOptions)
CatalogOption customizes FromCatalog.
func WithNonFilterable ¶ added in v0.3.0
func WithNonFilterable(entity string, fields ...string) CatalogOption
WithNonFilterable marks the named fields of an entity as not Filterable, so they cannot appear in WHERE. FromCatalog returns an error if the entity or a field is not in the catalog.
func WithWritable ¶ added in v0.3.0
func WithWritable(entity string, fields ...string) CatalogOption
WithWritable marks the named fields of an entity as Writable, making them valid INSERT/UPDATE assignment targets. Fields are read-only by default. FromCatalog returns an error if the entity or a field is not in the catalog.
type CompareExpr ¶
CompareExpr compares one field to one or more literal values.
type CompileOptions ¶ added in v0.3.0
type CompileOptions struct {
// Dialect selects the target SQL dialect. Required.
Dialect Dialect
// Schema resolves and re-quotes every identifier, and identifies JSON
// columns. Required: the compiler emits only identifiers present in the
// schema, so an unknown field is an error rather than passthrough.
Schema Schema
// ScopeSchema, when set, is the schema used to resolve the columns of the
// RowScope predicate. It lets a scope reference a column that Schema hides
// from the caller (for example an internal-class tenant_id). It defaults to
// Schema. Only the scope predicate resolves against it; the user's query
// never does. Config sets it to the full, unstripped schema.
ScopeSchema Schema
// StrictIdentifiers, when true, requires every schema identifier to be a
// safe unquoted identifier (see IsSafeIdentifier) before any SQL is emitted.
// Because identifiers cannot be parameterized, this makes identifier-based
// injection structurally impossible. Config sets this by default.
StrictIdentifiers bool
// RowScope, when set, returns a mandatory scope predicate for the queried
// entity (derived from trusted session context, e.g. tenant_id = <session>).
// The predicate is ANDed at the top level of the compiled WHERE, so no
// user-supplied filter can widen it. Returning (nil, nil) explicitly exempts
// an entity; returning an error refuses compilation. See ScopeFunc.
RowScope ScopeFunc
// RequireRowScope, when true, refuses to compile unless RowScope is set,
// making unscoped access impossible (fail-closed). RowScope may still exempt
// individual entities by returning (nil, nil).
RequireRowScope bool
}
CompileOptions configures compilation.
type CompiledSQL ¶ added in v0.3.0
CompiledSQL is parameterized SQL emitted from a validated AST. The SQL string never contains inlined user literals: every value is a bind parameter in Args, in positional order.
func Compile ¶ added in v0.3.0
func Compile(q *Query, opts CompileOptions) (CompiledSQL, error)
Compile transpiles a validated read query into parameterized SQL for the target dialect. It regenerates SQL from the AST rather than passing user text through: only constructs the AST can represent and the emitter supports are produced, and every literal becomes a bind parameter. The query should have passed Validate and CheckPolicy first; Compile independently re-resolves all identifiers against the schema as defense in depth.
Joins, CTEs, and nested query sources are not yet supported.
type Config ¶ added in v0.3.0
type Config struct {
// Schema describes the entities and fields, including each field's Class.
Schema Schema `json:"schema"`
// Dialect is the target SQL dialect for Compile.
Dialect Dialect `json:"dialect,omitempty"`
// AllowPII permits querying fields classed ClassPII. Default false.
AllowPII bool `json:"allowPII,omitempty"`
// AllowInternal permits querying fields classed ClassInternal (platform
// data not meant for customers). Default false.
AllowInternal bool `json:"allowInternal,omitempty"`
// AllowSecret permits querying fields classed ClassSecret. Reserve this for
// trusted, application-authored SQL; never enable it for customer-supplied
// SQL. Default false.
AllowSecret bool `json:"allowSecret,omitempty"`
// AllowUnsafeIdentifiers disables the default requirement that every entity
// and field identifier match the unquoted SQL identifier charset. Leave
// false (the default) to lock identifiers down; set true only for a database
// that genuinely relies on quoted identifiers.
AllowUnsafeIdentifiers bool `json:"allowUnsafeIdentifiers,omitempty"`
// AllowUnfilteredMutations permits UPDATE and DELETE without a WHERE clause
// (a full-table write). Default false: unfiltered mutations are rejected
// even for trusted callers, since they are almost always a mistake.
AllowUnfilteredMutations bool `json:"allowUnfilteredMutations,omitempty"`
// RowScope enforces tenant/ownership isolation (IDOR defense): it returns a
// mandatory predicate for each queried entity, derived from trusted session
// context, that is ANDed into every compiled SELECT/UPDATE/DELETE. It is
// per-request and not serialized. See ScopeFunc and ScopeEquals.
RowScope ScopeFunc `json:"-"`
// RequireRowScope refuses to compile unless RowScope is set (fail-closed).
RequireRowScope bool `json:"-"`
// Policy holds optional additional AST-level limits (depth, node count,
// required LIMIT, allowed operations, and so on) applied during Check and
// used to authorize mutations.
Policy Policy `json:"-"`
}
Config is a declarative description of a queryable database together with the rules for safely querying it: the schema (entities, fields, types, and per-field data classification), the target SQL dialect, and safety defaults.
It is the recommended high-level entry point; the lower-level Parse, Validate, CheckPolicy, and Compile primitives remain available. Config applies secure defaults: secret columns are never queryable, internal columns are queryable only when AllowInternal is set, PII columns only when AllowPII is set, and every identifier must be a safe unquoted identifier unless AllowUnsafeIdentifiers is set.
Config is JSON-serializable so an application can load "what the database is and how it may be queried" from a file.
func NewTrustedConfig ¶ added in v0.3.0
NewTrustedConfig returns a Config for internal, application-authored SQL: it may read and mutate, and may access secret, internal and PII columns. Identifier lockdown and value parameterization still apply, and mutation targets must still be columns marked Writable — trust removes access restrictions, not the safety of regenerating parameterized SQL from the validated AST or the write-allowlist. Never use this profile for SQL that originates outside the application.
func NewUntrustedConfig ¶ added in v0.3.0
NewUntrustedConfig returns a Config hardened for third-party or customer-supplied SQL (the Metabase-style case): read-only, secret and PII columns stripped from the schema, identifier lockdown on, and a required LIMIT. Tighten further via the returned Config's Policy (MaxLimit, MaxDepth, and so on).
type DataClass ¶ added in v0.3.0
type DataClass string
DataClass classifies the sensitivity of a field.
const ( // ClassPublic is ordinary, queryable data (the zero value). ClassPublic DataClass = "" // ClassPII is personal data, queryable only when Config.AllowPII is set. ClassPII DataClass = "pii" // ClassInternal is platform-only data that customer-facing queries must // not see. It is queryable only when Config.AllowInternal is set. ClassInternal DataClass = "internal" // ClassSecret is a credential/secret column that is never queryable. ClassSecret DataClass = "secret" //nolint:gosec // G101: classification label, not a credential )
Data classifications.
type DeleteBuilder ¶ added in v0.3.0
type DeleteBuilder struct {
// contains filtered or unexported fields
}
DeleteBuilder builds a parameterized DELETE. Construct with Delete.
func Delete ¶ added in v0.3.0
func Delete(table string) *DeleteBuilder
Delete starts a DELETE from table.
func (*DeleteBuilder) Compile ¶ added in v0.3.0
func (b *DeleteBuilder) Compile(cfg Config) (CompiledSQL, error)
Compile validates and compiles the DELETE to parameterized SQL.
func (*DeleteBuilder) Where ¶ added in v0.3.0
func (b *DeleteBuilder) Where(expr Expr) *DeleteBuilder
Where sets the filter. Required unless Config.AllowUnfilteredMutations is set.
type Dialect ¶ added in v0.3.0
type Dialect string
Dialect identifies a target SQL dialect for compilation.
type Expr ¶
type Expr interface {
// contains filtered or unexported methods
}
Expr is implemented by all filter expression nodes.
func And ¶ added in v0.3.0
And combines expressions with AND. Nil expressions are ignored; And of none returns nil.
type Field ¶
type Field struct {
Name string `json:"name"`
Type FieldType `json:"type"`
Selectable bool `json:"selectable"`
Filterable bool `json:"filterable"`
Sortable bool `json:"sortable"`
// Writable governs whether the field may be a target of an INSERT or UPDATE
// assignment. Like the read capabilities, it is deny-by-default: a field is
// writable only when explicitly marked, so mutation targets form an explicit
// allowlist. This is the mass-assignment defense — a caller cannot write a
// column (for example role or is_admin) unless the schema permits it.
Writable bool `json:"writable,omitempty"`
// Class is the field's data classification. Secret fields are never
// queryable by untrusted callers, internal fields are queryable only when
// a Config sets AllowInternal, and PII fields only when it sets AllowPII.
// The zero value (ClassPublic) is ordinary, queryable data. A field has a
// single Class; when several sensitivities apply, choose the most
// restrictive: secret, then internal, then PII.
Class DataClass `json:"class,omitempty"`
}
Field describes a queryable/projectable field.
type FieldPolicy ¶
type FieldPolicy struct {
Selectable bool
Filterable bool
Sortable bool
Groupable bool
Aggregatable bool
AggregateFuncs []AggregateFunc
Joinable bool
}
FieldPolicy controls how one field may be used in a query.
type FieldRef ¶ added in v0.3.0
FieldRef is a field name resolved against a schema entity. For a plain column JSONPath is empty; for a dotted reference into a FieldJSON column (e.g. payload.finding_info.title) Name is the base column ("payload") and JSONPath is the extraction path (["finding_info","title"]).
func (FieldRef) IsJSONPath ¶ added in v0.3.0
IsJSONPath reports whether the reference extracts a path from a JSON column.
type FieldRequirement ¶
type FieldRequirement struct {
Entity string
Field string
Source string
Usage FieldUsage
Aggregate AggregateFunc
OutputName string
}
FieldRequirement describes a concrete field operation in a query.
type FieldType ¶
type FieldType string
FieldType is a coarse type used by validators and backends.
const ( FieldString FieldType = "string" FieldNumber FieldType = "number" FieldBool FieldType = "bool" FieldTime FieldType = "time" // FieldJSON is a JSON/JSONB document column. A JSON field may be addressed // with a dotted path (e.g. payload.finding_info.title); the compiler emits // dialect-specific JSON extraction for the path. Any path under a // selectable JSON field is permitted. FieldJSON FieldType = "json" )
type FieldUsage ¶
type FieldUsage string
FieldUsage describes how a query uses a field.
const ( FieldUsageSelect FieldUsage = "select" FieldUsageFilter FieldUsage = "filter" FieldUsageSort FieldUsage = "sort" FieldUsageGroup FieldUsage = "group" FieldUsageAggregate FieldUsage = "aggregate" FieldUsageJoin FieldUsage = "join" FieldUsageHaving FieldUsage = "having" )
type FormatOptions ¶
type FormatOptions struct {
Style FormatStyle
Indent string
Highlight HighlightStyle
}
FormatOptions configures GuardSQL formatting.
func DefaultFormatOptions ¶
func DefaultFormatOptions() FormatOptions
DefaultFormatOptions returns conservative multiline formatting options.
type FormatStyle ¶
type FormatStyle string
FormatStyle controls whether a query is emitted on one line or as a human-readable multiline statement.
const ( FormatMultiline FormatStyle = "multiline" FormatSingleLine FormatStyle = "singleline" )
type HighlightStyle ¶
type HighlightStyle string
HighlightStyle controls optional syntax highlighting.
const ( HighlightNone HighlightStyle = "none" HighlightANSI HighlightStyle = "ansi" )
type InsertBuilder ¶ added in v0.3.0
type InsertBuilder struct {
// contains filtered or unexported fields
}
InsertBuilder builds a parameterized INSERT. Construct with Insert.
func Insert ¶ added in v0.3.0
func Insert(table string) *InsertBuilder
Insert starts an INSERT into table.
func (*InsertBuilder) Compile ¶ added in v0.3.0
func (b *InsertBuilder) Compile(cfg Config) (CompiledSQL, error)
Compile validates and compiles the INSERT to parameterized SQL.
func (*InsertBuilder) Set ¶ added in v0.3.0
func (b *InsertBuilder) Set(column string, value any) *InsertBuilder
Set adds a column assignment.
type JoinRequirement ¶
type JoinRequirement struct {
Type JoinType
LeftEntity string
RightEntity string
RightSource string
}
JoinRequirement describes a source-to-source join used by a query.
type LintConfig ¶
type LintConfig struct {
Schema Schema
AllowedOps []Operation
MaxDepth int
MaxNodes int
MaxInValues int
RequireLimit bool
}
LintConfig defines static policy checks for user-authored GuardSQL.
type LintIssue ¶
type LintIssue struct {
Message string
}
LintIssue is a syntax, schema, or policy problem found in a query.
type LogicalExpr ¶
LogicalExpr combines two expressions with AND or OR.
type Operation ¶
type Operation string
Operation identifies the CRUD operation represented by a query.
type ParseError ¶
ParseError is returned for invalid syntax.
func (*ParseError) Error ¶
func (e *ParseError) Error() string
type Policy ¶
type Policy struct {
AllowedOps []Operation
AllowedEntities []string
// StrictAllowlists makes AllowedEntities and Fields strict allowlists
// even when empty: an entity not in AllowedEntities is refused, and an
// entity with no entry in Fields has no usable fields.
StrictAllowlists bool
AllowedJoinTypes []JoinType
AllowedFunctions []AggregateFunc
Fields map[string]map[string]FieldPolicy
AllowStar bool
AllowCTEs bool
AllowNestedSources bool
RequireLimit bool
MaxLimit int
MaxDepth int
MaxNodes int
MaxInValues int
MaxSelectItems int
MaxOrderFields int
MaxGroupFields int
MaxJoins int
MaxCTEs int
MaxSubqueryDepth int
}
Policy defines AST-level safety rules for a GuardSQL query. It is intended for structural query controls; application authorization systems can compile their decisions into this policy shape before execution.
By default an empty AllowedEntities or Fields adds no restriction, so a zero Policy only applies the checks that are set. An authorization result must not be read that way: set StrictAllowlists (authz.PolicyFromDecision does) so that nothing allowed means nothing is allowed.
func SafeAnalyticsPolicy ¶
SafeAnalyticsPolicy returns a deny-by-default read-only policy derived from a schema. Host applications should tighten limits and authorization further for each deployment.
type PolicyIssue ¶
type PolicyIssue struct {
Message string
}
PolicyIssue is a policy violation found in a parsed query.
func CheckAnalysisPolicy ¶
func CheckAnalysisPolicy(analysis Analysis, policy Policy) []PolicyIssue
CheckAnalysisPolicy applies policy to resolved analysis requirements. It is preferred for operational authorization because aliases, CTEs, and nested sources have already been resolved into concrete resource and field usage.
func CheckPolicy ¶
func CheckPolicy(q *Query, policy Policy) []PolicyIssue
CheckPolicy applies AST-level policy checks to a parsed GuardSQL query. It does not mutate the query.
type Query ¶
type Query struct {
Operation Operation
With []CTE
Select []SelectItem
From string
FromQuery *Query
FromAlias string
Joins []Join
Where Expr
GroupBy []string
Having Expr
Prioritize []string
OrderBy []Order
Limit int
}
Query is the parsed representation of a GuardSQL query.
type RequirementSet ¶
type RequirementSet struct {
Operations []Operation
Entities []string
Fields []FieldRequirement
Joins []JoinRequirement
Functions []AggregateFunc
}
RequirementSet is a compact authorization and policy input derived from Analysis.
type ResolvedSource ¶
type ResolvedSource struct {
Name string
Alias string
Entity string
Nested bool
CTE bool
JoinType JoinType
}
ResolvedSource identifies a query source after schema and CTE resolution.
type Schema ¶
type Schema struct {
Entities map[string]Entity `json:"entities"`
MaxLimit int `json:"maxLimit,omitempty"`
}
Schema describes the entities and fields a user is allowed to query.
func FromCatalog ¶ added in v0.3.0
func FromCatalog(c *datacatalog.Catalog, opts ...CatalogOption) (Schema, error)
FromCatalog builds a Schema from a datacatalog Catalog, mapping the catalog's classification onto Field.Class so that Config.effectiveSchema blocks exactly what datacatalog's AllowedInQuery blocks.
The catalog is validated first and its validation error is returned unchanged, so an unclassified field can never enter a schema. Every field is Selectable, Filterable, and Sortable and not Writable unless an option says otherwise (see WithWritable and WithNonFilterable).
A guardsql field has a single Class, so when several datacatalog attributes apply the most restrictive for query purposes wins: secret, then internal visibility, then PII, then public. Use AllowSecret, AllowInternal, and AllowPII on Config to admit each class (NewUntrustedConfig admits none, NewTrustedConfig admits all).
Entity and field names are lower-cased as by Schema.Normalize; two names that differ only in case are an error.
func (Schema) ValidateIdentifiers ¶ added in v0.3.0
ValidateIdentifiers reports an error if any entity or field name is not a safe unquoted SQL identifier (see IsSafeIdentifier). Because identifiers are emitted into SQL text rather than bound as parameters, enforcing this over the whole schema removes identifier-based injection as a possibility.
type ScopeFunc ¶ added in v0.3.0
ScopeFunc returns a mandatory row-scope predicate for a queried entity. It is how an application enforces tenant/ownership isolation (IDOR defense) at the GuardSQL layer: the predicate is built from trusted session context and ANDed into every compiled statement so a user's own filter cannot widen scope.
Return an Expr (typically built with Eq/In/And) to require it, (nil, nil) to exempt the entity, or an error to refuse compilation.
func ScopeEquals ¶ added in v0.3.0
ScopeEquals returns a ScopeFunc that requires column = value on every queried entity, using value from trusted session context (e.g. the tenant ID). If a queried entity has no such column, compilation fails (fail-closed), so a missing scope column can never silently return unscoped data. For per-entity exemptions or multiple scope columns, write a ScopeFunc directly.
type SelectItem ¶
type SelectItem struct {
Field string
Star bool
Aggregate *AggregateExpr
Alias string
}
SelectItem is a projected field. Star is true for SELECT *.
type UpdateBuilder ¶ added in v0.3.0
type UpdateBuilder struct {
// contains filtered or unexported fields
}
UpdateBuilder builds a parameterized UPDATE. Construct with Update.
func Update ¶ added in v0.3.0
func Update(table string) *UpdateBuilder
Update starts an UPDATE of table.
func (*UpdateBuilder) Compile ¶ added in v0.3.0
func (b *UpdateBuilder) Compile(cfg Config) (CompiledSQL, error)
Compile validates and compiles the UPDATE to parameterized SQL.
func (*UpdateBuilder) Set ¶ added in v0.3.0
func (b *UpdateBuilder) Set(column string, value any) *UpdateBuilder
Set adds a column assignment.
func (*UpdateBuilder) Where ¶ added in v0.3.0
func (b *UpdateBuilder) Where(expr Expr) *UpdateBuilder
Where sets the filter. Required unless Config.AllowUnfilteredMutations is set. Build conditions with Eq, In, And, Or, Not, and the comparison helpers.