Help us shape the future of Go! Take the 2026 Go Developer Survey

security

package
v0.5.2 Latest Latest
Warning

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

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

Documentation

Overview

Package security provides query sanitization and data masking functionality.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type MaskPattern

type MaskPattern struct {
	Name        string
	Pattern     string
	Replacement string
	Enabled     bool
}

MaskPattern represents a PII masking pattern.

func DefaultPIIPatterns

func DefaultPIIPatterns() []MaskPattern

DefaultPIIPatterns returns commonly used PII masking patterns.

type PIIMasker

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

PIIMasker masks personally identifiable information in query results.

func NewPIIMasker

func NewPIIMasker(patterns []MaskPattern, enabled bool) (*PIIMasker, error)

NewPIIMasker creates a new PII masker with configured patterns.

func (*PIIMasker) MaskData

func (pm *PIIMasker) MaskData(data string) string

MaskData masks PII data in a string.

func (*PIIMasker) MaskValue

func (pm *PIIMasker) MaskValue(value interface{}) interface{}

MaskValue masks PII in any value (handles different types).

type QueryModifier

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

QueryModifier modifies queries to add safety constraints.

func NewQueryModifier

func NewQueryModifier(maxRowLimit int) *QueryModifier

NewQueryModifier creates a new query modifier.

func (*QueryModifier) AddRowLimit

func (qm *QueryModifier) AddRowLimit(query string) (string, error)

AddRowLimit ensures the query carries a top-level "LIMIT N" no larger than maxRowLimit.

The modifier is dialect-agnostic and uses string manipulation only — it does not pull in a SQL parser. Behaviour:

  • Trailing whitespace and a single trailing ';' are tolerated and stripped.
  • If a trailing "LIMIT N [OFFSET M]" already exists with N <= maxRowLimit, the query is returned unchanged.
  • If N exceeds the cap, only the numeric token is replaced; OFFSET and anything before LIMIT are preserved.
  • Otherwise " LIMIT maxRowLimit" is appended.

On MSSQL the adapter strips this LIMIT and rewrites it as SELECT TOP N (see pkg/adapter/mssql/mssql.go adaptQueryForVersion). Subquery LIMITs inside the query body are left alone — only the outermost cap is managed.

type QueryValidator

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

QueryValidator validates SQL queries for security purposes.

Design note — why we do not use a third-party SQL parser:

Previously this package used xwb1989/sqlparser (MySQL dialect, unmaintained since 2018) with a regex denylist fallback. The fallback was bypassable via comment/whitespace tricks (e.g. "SELECT 1; EX/**/EC xp_cmdshell …" — the regex would not match "EX/**/EC" but SQL Server treats /**/ as whitespace and executes the statement). Switching to vitess or cockroachdb parsers only moves the problem: every dialect-aware parser has corner cases where it cannot parse a legitimate target-DB query, and any "fall through to regex" relaxation re-introduces the same bypass class.

We do not actually need an AST. The security goal is to classify the statement type (SELECT/WITH allowed, everything else rejected) and to reject multi-statement payloads. Both checks are reliable on a properly stripped query (no comments, no string literals). This file does that with a small custom tokeniser — easier to audit and free of parser-dialect gotchas.

The allowedKeywords / blockedKeywords slices are retained on the struct for API + config-payload backwards compatibility (the cloud control plane and existing coremcp.yaml files still reference them) but they are no longer consulted by the validator.

func NewQueryValidator

func NewQueryValidator(allowedKeywords, blockedKeywords []string) *QueryValidator

NewQueryValidator creates a new query validator.

The allowedKeywords / blockedKeywords parameters are accepted but ignored — see the QueryValidator type doc for why. The signature is preserved so the MCP server and the WebSocket config_sync code path do not have to change.

func (*QueryValidator) ValidateQuery

func (qv *QueryValidator) ValidateQuery(query string) error

ValidateQuery decides whether a SQL query is safe to forward to a source.

Posture: fail-closed. Four layers:

  1. Reject multi-statement payloads — any ';' outside strings/comments, other than a single trailing one, is fatal. This catches stacked-query attacks regardless of dialect.
  2. Classify the leading statement keyword (SELECT / WITH allowed; every other recognised keyword is rejected with a specific error; unknown leading tokens are rejected generically).
  3. The leading keyword is read from the query AFTER stripping comments and string literals, so payloads like "/* SELECT */ DROP TABLE x" or "SELECT 'safe'; DELETE FROM users" cannot lie about their shape.
  4. For SELECT/WITH bodies, scan the stripped text for write/exfiltration tokens that cannot legitimately appear in a read-only query (SELECT…INTO, OPENROWSET, OPENQUERY, OPENDATASOURCE).

This validator does NOT introspect CTE bodies for DML keywords. For PostgreSQL, a CTE may legally contain "DELETE … RETURNING" — the line of defence there is the DB user's permission set (the README requires a SELECT-only user, and the cloud-direct adapter additionally pins the session to default_transaction_read_only). MSSQL CTEs are SELECT-only at the grammar level, so the primary target is not affected.

Jump to

Keyboard shortcuts

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