Skip to content

Commit c771349

Browse files
authored
Allow square brackets [ and ] in BasicPostgresSecurityValidator (#254)
1 parent bf7e428 commit c771349

File tree

2 files changed

+26
-13
lines changed

2 files changed

+26
-13
lines changed

document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/BasicPostgresSecurityValidator.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@ public class BasicPostgresSecurityValidator implements PostgresSecurityValidator
1414
/**
1515
* Default pattern for PostgreSQL column/table identifiers.
1616
*
17-
* <p>Pattern: {@code ^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*$}
17+
* <p>Pattern: {@code ^[a-zA-Z_][a-zA-Z0-9_\[\]-]*(\.[a-zA-Z_][a-zA-Z0-9_\[\]-]*)*$}
1818
*
1919
* <p><b>Allowed:</b>
2020
*
2121
* <ul>
2222
* <li>Must start with: letter (a-z, A-Z) or underscore (_)
23-
* <li>Can contain: letters (a-z, A-Z), digits (0-9), underscores (_), dots (.) for nested field
24-
* notation
23+
* <li>Can contain: letters (a-z, A-Z), digits (0-9), underscores (_), hyphens (-), square
24+
* brackets ([]), dots (.) for nested field notation
2525
* <li>Examples: {@code "myColumn"}, {@code "user_id"}, {@code "_internal"}, {@code "TABLE1"},
26-
* {@code "field.name"}, {@code "nested.field.name"}
26+
* {@code "field.name"}, {@code "nested.field.name"}, {@code "array_field[]"}, {@code
27+
* "DISTINCT_COUNT_API_id_[]"}
2728
* </ul>
2829
*
2930
* <p><b>Not allowed:</b>
@@ -32,7 +33,6 @@ public class BasicPostgresSecurityValidator implements PostgresSecurityValidator
3233
* <li>Starting with numbers: {@code "123column"}
3334
* <li>Starting or ending with dots: {@code ".field"}, {@code "field."}
3435
* <li>Consecutive dots: {@code "field..name"}
35-
* <li>Special characters: {@code "col-name"}
3636
* <li>Spaces: {@code "my column"}, {@code "field OR 1=1"}
3737
* <li>Quotes: {@code "field\"name"}, {@code "field'name"}
3838
* <li>Semicolons: {@code "col;DROP"}
@@ -43,27 +43,20 @@ public class BasicPostgresSecurityValidator implements PostgresSecurityValidator
4343
* Documentation</a>
4444
*/
4545
private static final String DEFAULT_IDENTIFIER_PATTERN =
46-
"^[a-zA-Z_][a-zA-Z0-9_-]*(\\.[a-zA-Z_][a-zA-Z0-9_-]*)*$";
46+
"^[a-zA-Z_][a-zA-Z0-9_\\[\\]-]*(\\.[a-zA-Z_][a-zA-Z0-9_\\[\\]-]*)*$";
4747

48-
/** Default instance with hardcoded values for convenient static access. */
4948
private static final BasicPostgresSecurityValidator DEFAULT =
5049
new BasicPostgresSecurityValidator(
5150
DEFAULT_MAX_IDENTIFIER_LENGTH,
5251
DEFAULT_MAX_JSON_FIELD_LENGTH,
5352
DEFAULT_MAX_JSON_PATH_DEPTH,
5453
DEFAULT_IDENTIFIER_PATTERN);
5554

56-
// Instance variables for configured limits
5755
private final Pattern validIdentifier;
5856
private final int maxIdentifierLength;
5957
private final int maxJsonFieldLength;
6058
private final int maxJsonPathDepth;
6159

62-
/**
63-
* Returns the default validator instance with hardcoded values.
64-
*
65-
* @return the default validator instance
66-
*/
6760
public static BasicPostgresSecurityValidator getDefault() {
6861
return DEFAULT;
6962
}

document-store/src/test/java/org/hypertrace/core/documentstore/postgres/utils/PostgresSecurityValidatorTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,4 +301,24 @@ void testAttackScenarioNestedQuotes() {
301301
SecurityException.class, () -> validator.validateJsonPath(List.of("field'\"'; DROP")));
302302
assertTrue(ex.getMessage().contains("invalid characters"));
303303
}
304+
305+
@Test
306+
void testValidIdentifierWithSquareBrackets() {
307+
assertDoesNotThrow(
308+
() -> validator.validateIdentifier("DISTINCT_COUNT_API_riskScoreCategory_[]"));
309+
}
310+
311+
@Test
312+
void testValidIdentifierWithSquareBracketsSimple() {
313+
assertDoesNotThrow(() -> validator.validateIdentifier("field_name[]"));
314+
assertDoesNotThrow(() -> validator.validateIdentifier("array_field[]"));
315+
}
316+
317+
@Test
318+
void testValidIdentifierWithSquareBracketsComplex() {
319+
assertDoesNotThrow(() -> validator.validateIdentifier("DISTINCT_COUNT_API_id_[]"));
320+
assertDoesNotThrow(() -> validator.validateIdentifier("array_field[0]"));
321+
assertDoesNotThrow(() -> validator.validateIdentifier("field[key]"));
322+
assertDoesNotThrow(() -> validator.validateIdentifier("nested[array][index]"));
323+
}
304324
}

0 commit comments

Comments
 (0)