{"record":{"id":"bd70aa1588efff70","repo":"spring-projects/spring-ai","slug":"not-allowed-filter-identifier-name-bd70aa","errorCode":null,"errorMessage":"Not allowed filter identifier name: ","messagePattern":"Not allowed filter identifier name: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/redis/RedisFilterExpressionConverter.java","lineNumber":68,"sourceCode":"\n\t@Override\n\tprotected void doStartGroup(Group group, StringBuilder context) {\n\t\tcontext.append(\"(\");\n\t}\n\n\t@Override\n\tprotected void doEndGroup(Group group, StringBuilder context) {\n\t\tcontext.append(\")\");\n\t}\n\n\t@Override\n\tprotected void doKey(Key key, StringBuilder context) {\n\t\tvar identifier = key.key();\n\t\t// RediSearch field names are bare identifiers in the @field: query syntax\n\t\t// and have no escaping mechanism. Validate against the configured metadata\n\t\t// fields to prevent query injection through crafted field names.\n\t\tif (!this.metadataFields.containsKey(identifier)) {\n\t\t\tthrow new IllegalArgumentException(\"Not allowed filter identifier name: \" + identifier);\n\t\t}\n\t\tcontext.append(\"@\").append(identifier).append(\":\");\n\t}\n\n\t@Override\n\tprotected void doExpression(Expression expression, StringBuilder context) {\n\t\tswitch (expression.type()) {\n\t\t\tcase NIN:\n\t\t\t\tdoExpression(negate(ExpressionType.IN, expression), context);\n\t\t\t\tbreak;\n\t\t\tcase NE:\n\t\t\t\tdoExpression(negate(ExpressionType.EQ, expression), context);\n\t\t\t\tbreak;\n\t\t\tcase AND:\n\t\t\t\tdoBinaryOperation(\" \", expression, context);\n\t\t\t\tbreak;\n\t\t\tcase OR:\n\t\t\t\tdoBinaryOperation(\" | \", expression, context);","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/redis/RedisFilterExpressionConverter.java#L50-L86","documentation":"RedisFilterExpressionConverter.doKey validates that every filter field name is one of the configured metadata fields before emitting the @field: RediSearch query token. RediSearch query syntax has no escaping mechanism for field names, so unknown identifiers are rejected with IllegalArgumentException 'Not allowed filter identifier name: <name>' to prevent query injection through crafted field names.","triggerScenarios":"Using a FilterExpression whose key is not registered in RedisVectorStore's MetadataField configuration, e.g. Filter.expr(\"category\") when only tags/price metadata fields were declared via metadataFields(...).","commonSituations":"Adding a new metadata key to documents but forgetting to register it in the store builder; typos in field names; switching stores without updating the allowed-field list.","solutions":["Register the field in RedisVectorStore.builder().metadataFields(MetadataField.tag(\"category\"), ...)","Correct the field-name typo in the filter expression to match a registered metadata field","Keep a single shared constant list of metadata fields used by both document writing and filters","Catch IllegalArgumentException from search if filters are user-supplied and sanitize them first"],"exampleFix":"// before\nRedisVectorStore.builder(jedis, model).metadataFields(MetadataField.tag(\"type\")).build();\n// filter uses Filter.expr(\"category\") -> error\n// after\nRedisVectorStore.builder(jedis, model).metadataFields(MetadataField.tag(\"type\"), MetadataField.tag(\"category\")).build();","handlingStrategy":"validation","validationCode":"if (!registeredMetadataFields.containsKey(filterKey)) throw new IllegalArgumentException(\"Filter field not registered: \" + filterKey);","typeGuard":"static boolean isAllowedField(String key, Map<String,MetadataField> fields) { return key != null && fields.containsKey(key); }","tryCatchPattern":"try { vectorStore.similaritySearch(request); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith(\"Not allowed filter identifier\")) { /* reject/sanitize user filter */ } else throw e; }","preventionTips":["Whitelist user-supplied filter keys against registered metadata fields before searching","Keep one shared constants class for metadata field names","Add tests asserting every filter key used in the app is registered"],"tags":["redis","filter-expression","injection-prevention","vector-store","spring-ai"],"backgroundTag":"invalid-identifier","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}