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:
*
*
* - Must start with: letter (a-z, A-Z) or underscore (_)
- *
- Can contain: letters (a-z, A-Z), digits (0-9), underscores (_), dots (.) for nested field
- * notation
+ *
- Can contain: letters (a-z, A-Z), digits (0-9), underscores (_), hyphens (-), square
+ * brackets ([]), dots (.) for nested field notation
*
- Examples: {@code "myColumn"}, {@code "user_id"}, {@code "_internal"}, {@code "TABLE1"},
- * {@code "field.name"}, {@code "nested.field.name"}
+ * {@code "field.name"}, {@code "nested.field.name"}, {@code "array_field[]"}, {@code
+ * "DISTINCT_COUNT_API_id_[]"}
*
*
* 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]"));
+ }
}