@@ -71,7 +71,7 @@ public async Task CreateIndexAsync(string index, int vectorSize, CancellationTok
7171 if ( ! this . _isReady ) { await this . InitAsync ( cancellationToken ) . ConfigureAwait ( false ) ; }
7272
7373 index = NormalizeIndexName ( index ) ;
74- if ( ! IsValidSqlIdentifier ( index ) )
74+ if ( ! IsSafeTableName ( index ) )
7575 {
7676 this . _log . LogError ( "Rejected SQL index name {index} due to invalid characters" , index ) ;
7777 throw new ArgumentException ( $ "The index '{ index } ' contains invalid characters and cannot be used as a SQL identifier.") ;
@@ -138,7 +138,7 @@ public async Task DeleteAsync(string index, MemoryRecord record, CancellationTok
138138 if ( ! this . _isReady ) { await this . InitAsync ( cancellationToken ) . ConfigureAwait ( false ) ; }
139139
140140 index = NormalizeIndexName ( index ) ;
141- if ( ! IsValidSqlIdentifier ( index ) )
141+ if ( ! IsSafeTableName ( index ) )
142142 {
143143 this . _log . LogError ( "Rejected SQL index name {index} due to invalid characters" , index ) ;
144144 throw new ArgumentException ( $ "The index '{ index } ' contains invalid characters and cannot be used as a SQL identifier.") ;
@@ -196,7 +196,7 @@ public async Task DeleteIndexAsync(string index, CancellationToken cancellationT
196196 if ( ! this . _isReady ) { await this . InitAsync ( cancellationToken ) . ConfigureAwait ( false ) ; }
197197
198198 index = NormalizeIndexName ( index ) ;
199- if ( ! IsValidSqlIdentifier ( index ) )
199+ if ( ! IsSafeTableName ( index ) )
200200 {
201201 this . _log . LogError ( "Rejected SQL index name {index} due to invalid characters" , index ) ;
202202 throw new ArgumentException ( $ "The index '{ index } ' contains invalid characters and cannot be used as a SQL identifier.") ;
@@ -280,7 +280,7 @@ public async IAsyncEnumerable<MemoryRecord> GetListAsync(
280280 if ( ! this . _isReady ) { await this . InitAsync ( cancellationToken ) . ConfigureAwait ( false ) ; }
281281
282282 index = NormalizeIndexName ( index ) ;
283- if ( ! IsValidSqlIdentifier ( index ) )
283+ if ( ! IsSafeTableName ( index ) )
284284 {
285285 this . _log . LogError ( "Rejected SQL index name {index} due to invalid characters" , index ) ;
286286 throw new ArgumentException ( $ "The index '{ index } ' contains invalid characters and cannot be used as a SQL identifier.") ;
@@ -354,7 +354,7 @@ SELECT TOP (@limit)
354354 if ( ! this . _isReady ) { await this . InitAsync ( cancellationToken ) . ConfigureAwait ( false ) ; }
355355
356356 index = NormalizeIndexName ( index ) ;
357- if ( ! IsValidSqlIdentifier ( index ) )
357+ if ( ! IsSafeTableName ( index ) )
358358 {
359359 this . _log . LogError ( "Rejected SQL index name {index} due to invalid characters" , index ) ;
360360 throw new ArgumentException ( $ "The index '{ index } ' contains invalid characters and cannot be used as a SQL identifier.") ;
@@ -475,7 +475,7 @@ public async IAsyncEnumerable<string> UpsertBatchAsync(string index, IEnumerable
475475 if ( ! this . _isReady ) { await this . InitAsync ( cancellationToken ) . ConfigureAwait ( false ) ; }
476476
477477 index = NormalizeIndexName ( index ) ;
478- if ( ! IsValidSqlIdentifier ( index ) )
478+ if ( ! IsSafeTableName ( index ) )
479479 {
480480 this . _log . LogError ( "Rejected SQL index name {index} due to invalid characters" , index ) ;
481481 throw new ArgumentException ( $ "The index '{ index } ' contains invalid characters and cannot be used as a SQL identifier.") ;
@@ -585,24 +585,6 @@ public void Dispose()
585585 // Note: "_" is allowed in SQL Server, but we normalize it to "-" for consistency with other DBs
586586 private static readonly Regex s_replaceIndexNameCharsRegex = new ( @"[\s|\\|/|.|_|:]" ) ;
587587 private const string ValidSeparator = "-" ;
588-
589- /// <summary>
590-
591- /// Validate that string is safe to use as a SQL identifier
592-
593- /// </summary>
594-
595- private static bool IsValidSqlIdentifier ( string identifier )
596- {
597-
598- // Only allow alphanumeric and underscores
599- if ( string . IsNullOrWhiteSpace ( identifier ) ) return false ;
600-
601- // Identifiers in SQL Server can be up to 128 characters
602- if ( identifier . Length > 128 ) return false ;
603- // Must start with a letter or underscore, rest letters/numbers/underscores
604- return System . Text . RegularExpressions . Regex . IsMatch ( identifier , @"^[A-Za-z_][A-Za-z0-9_]{0,127}$" ) ;
605- }
606588
607589 /// <summary>
608590 /// Prepare instance, ensuring tables exist and reusable info is cached.
@@ -816,4 +798,23 @@ private static string NormalizeIndexName(string index)
816798 }
817799
818800 #endregion
801+ /// <summary>
802+ /// Strictly validates that an index/table name is safe to use in SQL.
803+ /// Only allows ASCII letters, digits, and underscores. Disallows reserved SQL keywords.
804+ /// </summary>
805+ private static bool IsSafeTableName ( string index )
806+ {
807+ if ( string . IsNullOrWhiteSpace ( index ) ) return false ;
808+ // Only allow a-z, A-Z, 0-9, underscore. Reject anything else.
809+ if ( ! Regex . IsMatch ( index , @"^[a-zA-Z0-9_]+$" ) ) return false ;
810+
811+ // Optionally, prevent reserved SQL keywords (add more as needed)
812+ string [ ] ReservedKeywords = new [ ] {
813+ "select" , "insert" , "update" , "delete" , "drop" , "alter" , "truncate" , "create" , "table" , "from" , "where" , "union"
814+ } ;
815+ // case-insensitive check
816+ if ( ReservedKeywords . Any ( keyword => string . Equals ( index , keyword , StringComparison . OrdinalIgnoreCase ) ) )
817+ return false ;
818+ return true ;
819+ }
819820}
0 commit comments