Skip to content

Relax Privilege Patterns#42

Open
yamaceay wants to merge 3 commits intoprevent-sql-injectionfrom
relax-privilege-patterns
Open

Relax Privilege Patterns#42
yamaceay wants to merge 3 commits intoprevent-sql-injectionfrom
relax-privilege-patterns

Conversation

@yamaceay
Copy link
Contributor

Overview

The goal is to modernize privilege pattern matching system to provide more flexible identifier handling while maintaining strict pattern ordering to prevent wrong matches.

Flexible Identifier Pattern

Previous Approach:

anyIdentifierPattern = `(?:"[^"]+"|[A-Za-z][A-Za-z0-9_]*)`
  • Strictly required identifiers to follow SQL naming rules (alphanumeric + underscore)
  • Special characters required explicit quoting in the spec

New Approach:

anyIdentifierPattern = `(?:"[^"]+"|[^\s]+)`
  • Accepts any non-whitespace characters
  • Allows identifiers like _SYS_STATISTICS, my-schema, role.name without quoting
  • Double-quote escaping is deferred to SQL command construction, e.g. CREATE / ALTER / DROP

Reusable Pattern Constants

const (
    anyIdentifierPattern = `(?:"[^"]+"|[^\s]+)`
    privilegeNamePattern = `[A-Za-z](?:[A-Za-z\s]*?[A-Za-z])?`
)

In key points:

  • DRY principle: Define once, use everywhere
  • Single source of truth for identifier matching
  • Easy to maintain and update

Pattern Ordering Strategy (Reconsidered)

Patterns are ordered from most specific to most permissive to prevent false matches:

  1. USERGROUP OPERATOR ON USERGROUP <name> - Multi-word specific keywords
  2. USAGE ON CLIENTSIDE ENCRYPTION COLUMN KEY <name> - Long keyword sequence
  3. <priv> ON REMOTE SOURCE <name> - Specific object type with keyword
  4. <priv> ON SCHEMA <name> - Specific object type with keyword
  5. <priv> ON <schema>.<object> - Dot separator provides uniqueness
  6. STRUCTURED PRIVILEGE <name> - Specific keyword sequence
  7. <priv> ON <object> - Generic with ON keyword
  8. <priv> - System privilege (no ON keyword) - Most permissive, MUST be last

With this ordering, we can ensure that more specific patterns match first, preventing the generic system privilege pattern from consuming privilege strings that should match more specific types.

Non-Greedy Privilege Name Matching

privilegeNamePattern = `[A-Za-z](?:[A-Za-z\s]*?[A-Za-z])?`

By way of using the *? (non-greedy) quantifier, we prevent the privilege name from consuming the WITH GRANT OPTION or WITH ADMIN OPTION suffixes.

Examples

# Before (required explicit quoting)
SELECT ON SCHEMA "_SYS_STATISTICS" WITH GRANT OPTION

# After (quotes optional)
SELECT ON SCHEMA _SYS_STATISTICS WITH GRANT OPTION
# Schema with special characters
CREATE ANY ON SCHEMA _SYS_REPO

# Usergroup with hyphens
USERGROUP OPERATOR ON USERGROUP my-special-group

# Object with dots in qualified name
SELECT ON _SYS_STATISTICS.STATISTICS_ALERTS

Identifiers are not quoted in the privilege specification. Just for reference, quoting only happens when constructing SQL commands (to prevent SQL injection in the easiest way possible):

// In role_client.go, dbschema_client.go, etc.
func getRoleName(schemaName, roleName string) string {
    roleNameEscaped := fmt.Sprintf(`"%s"`, utils.EscapeDoubleQuotes(roleName))
    // ...
}

Backward Compatibility and Testing

The hardest challenge is in ensuring the full backward compatibility:

  • Quoted identifiers still work: "my schema"
  • Standard identifiers still work: MY_SCHEMA
  • New: Special characters work: _SYS_STATISTICS, my-schema

Still, all existing tests pass with the new patterns.

@yamaceay yamaceay changed the base branch from main to prevent-sql-injection February 19, 2026 16:56
@yamaceay yamaceay force-pushed the prevent-sql-injection branch 3 times, most recently from 498804c to d81c9cc Compare February 25, 2026 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant