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 ¶
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.
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:
- Reject multi-statement payloads — any ';' outside strings/comments, other than a single trailing one, is fatal. This catches stacked-query attacks regardless of dialect.
- Classify the leading statement keyword (SELECT / WITH allowed; every other recognised keyword is rejected with a specific error; unknown leading tokens are rejected generically).
- 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.
- 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.