diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/BasicPostgresSecurityValidator.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/BasicPostgresSecurityValidator.java index 4f3178f7..7a337a27 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/BasicPostgresSecurityValidator.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/BasicPostgresSecurityValidator.java @@ -14,16 +14,17 @@ public class BasicPostgresSecurityValidator implements PostgresSecurityValidator /** * Default pattern for PostgreSQL column/table identifiers. * - *

Pattern: {@code ^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*$} + *

Pattern: {@code ^[a-zA-Z_][a-zA-Z0-9_\[\]-]*(\.[a-zA-Z_][a-zA-Z0-9_\[\]-]*)*$} * *

Allowed: * *

* *

Not allowed: @@ -32,7 +33,6 @@ public class BasicPostgresSecurityValidator implements PostgresSecurityValidator *

  • Starting with numbers: {@code "123column"} *
  • Starting or ending with dots: {@code ".field"}, {@code "field."} *
  • Consecutive dots: {@code "field..name"} - *
  • Special characters: {@code "col-name"} *
  • Spaces: {@code "my column"}, {@code "field OR 1=1"} *
  • Quotes: {@code "field\"name"}, {@code "field'name"} *
  • Semicolons: {@code "col;DROP"} @@ -43,9 +43,8 @@ public class BasicPostgresSecurityValidator implements PostgresSecurityValidator * Documentation */ private static final String DEFAULT_IDENTIFIER_PATTERN = - "^[a-zA-Z_][a-zA-Z0-9_-]*(\\.[a-zA-Z_][a-zA-Z0-9_-]*)*$"; + "^[a-zA-Z_][a-zA-Z0-9_\\[\\]-]*(\\.[a-zA-Z_][a-zA-Z0-9_\\[\\]-]*)*$"; - /** Default instance with hardcoded values for convenient static access. */ private static final BasicPostgresSecurityValidator DEFAULT = new BasicPostgresSecurityValidator( DEFAULT_MAX_IDENTIFIER_LENGTH, @@ -53,17 +52,11 @@ public class BasicPostgresSecurityValidator implements PostgresSecurityValidator DEFAULT_MAX_JSON_PATH_DEPTH, DEFAULT_IDENTIFIER_PATTERN); - // Instance variables for configured limits private final Pattern validIdentifier; private final int maxIdentifierLength; private final int maxJsonFieldLength; private final int maxJsonPathDepth; - /** - * Returns the default validator instance with hardcoded values. - * - * @return the default validator instance - */ public static BasicPostgresSecurityValidator getDefault() { return DEFAULT; } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/utils/PostgresSecurityValidatorTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/utils/PostgresSecurityValidatorTest.java index 1abad456..31ee56f5 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/utils/PostgresSecurityValidatorTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/utils/PostgresSecurityValidatorTest.java @@ -301,4 +301,24 @@ void testAttackScenarioNestedQuotes() { SecurityException.class, () -> validator.validateJsonPath(List.of("field'\"'; DROP"))); assertTrue(ex.getMessage().contains("invalid characters")); } + + @Test + void testValidIdentifierWithSquareBrackets() { + assertDoesNotThrow( + () -> validator.validateIdentifier("DISTINCT_COUNT_API_riskScoreCategory_[]")); + } + + @Test + void testValidIdentifierWithSquareBracketsSimple() { + assertDoesNotThrow(() -> validator.validateIdentifier("field_name[]")); + assertDoesNotThrow(() -> validator.validateIdentifier("array_field[]")); + } + + @Test + void testValidIdentifierWithSquareBracketsComplex() { + assertDoesNotThrow(() -> validator.validateIdentifier("DISTINCT_COUNT_API_id_[]")); + assertDoesNotThrow(() -> validator.validateIdentifier("array_field[0]")); + assertDoesNotThrow(() -> validator.validateIdentifier("field[key]")); + assertDoesNotThrow(() -> validator.validateIdentifier("nested[array][index]")); + } }