CVE-2022-24815 (published 2022-04-11T20:15Z)

JHipster is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures. SQL Injection vulnerability in entities for applications generated with the option "reactive with Spring WebFlux" enabled and an SQL database using r2dbc. Applications created without "reactive with Spring WebFlux" and applications with NoSQL databases are not affected. Users who have generated a microservice Gateway using the affected version may be impacted as Gateways are reactive by default. Currently, SQL injection is possible in the findAllBy(Pageable pageable, Criteria criteria) method of an entity repository class generated in these applications as the where clause using Criteria for queries are not sanitized and user input is passed on as it is by the criteria. This issue has been patched in v7.8.1. Users unable to upgrade should be careful when combining criterias and conditions as the root of the issue lies in the `EntityManager.java` class when creating the where clause via `Conditions.just(criteria.toString())`. `just` accepts the literal string provided. Criteria\'s `toString` method returns a plain string and this combination is vulnerable to sql injection as the string is not sanitized and will contain whatever used passed as input using any plain SQL.

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 8.1, HIGH (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-89
cwe_name: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/jhipster/generator-jhipster
commit_details: repo_url: https://github.com/jhipster/generator-jhipster
author: Frederik Hahne
author_date: 2022-04-05 22:21:54+02:00
msg: use conditions instead of criteria as workaround

closes #18269
repository: repo_url: https://github.com/jhipster/generator-jhipster
repo_name: jhipster/generator-jhipster
description: JHipster is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.
repo_language: TypeScript
owner: jhipster
file_changes: filename: EntityRepositoryInternalImpl_reactive.java.ejs
change_type: ModificationType.MODIFY
diff: @@ -50,6 +50,9 @@ import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
 import org.springframework.data.r2dbc.repository.support.SimpleR2dbcRepository;
 import org.springframework.data.relational.core.query.Criteria;
 import org.springframework.data.relational.core.sql.Column;
+import org.springframework.data.relational.core.sql.Comparison;
+import org.springframework.data.relational.core.sql.Condition;
+import org.springframework.data.relational.core.sql.Conditions;
 import org.springframework.data.relational.core.sql.Expression;
 import org.springframework.data.relational.core.sql.Select;
 import org.springframework.data.relational.core.sql.SelectBuilder.SelectFromAndJoin<% if (reactiveEagerRelations.length > 0) { %>Condition<% } %>;
@@ -121,15 +124,10 @@ _%>
 
     @Override
     public Flux<<%= persistClass %>> findAllBy(Pageable pageable) {
-        return findAllBy(pageable, null);
+        return createQuery(pageable, null).all();
     }
 
-    @Override
-    public Flux<<%= persistClass %>> findAllBy(Pageable pageable, Criteria criteria) {
-        return createQuery(pageable, criteria).all();
-    }
-
-    RowsFetchSpec<<%= persistClass %>> createQuery(Pageable pageable, Criteria criteria) {
+    RowsFetchSpec<<%= persistClass %>> createQuery(Pageable pageable, Condition whereClause) {
         List<Expression> columns = <%= entityClass %>SqlHelper.getColumns(entityTable, EntityManager.ENTITY_ALIAS);
 <%_ reactiveEagerRelations.forEach(function(rel) { _%>
         columns.addAll(<%= rel.otherEntityNameCapitalized %>SqlHelper.getColumns(<%= rel.relationshipName %>Table, "<%= rel.relationshipName %>"));
@@ -138,19 +136,20 @@ _%>
 <%_ reactiveEagerRelations.forEach(function(rel) { _%>
             .leftOuterJoin(<%= rel.relationshipName %>Table).on(Column.create("<%= rel.joinColumnNames[0] %>", entityTable)).equals(Column.create("<%= rel.otherEntity.primaryKey.fields[0].columnName %>", <%= rel.relationshipName %>Table ))
 <%_ }); _%>;
-
-        String select = entityManager.createSelect(selectFrom, <%= persistClass %>.class, pageable, criteria);
+        // we do not support Criteria here for now as of https://github.com/jhipster/generator-jhipster/issues/18269
+        String select = entityManager.createSelect(selectFrom, <%= persistClass %>.class, pageable, whereClause);
         return db.sql(select).map(this::process);
     }
 
     @Override
     public Flux<<%= persistClass %>> findAll() {
-        return findAllBy(null, null);
+        return findAllBy(null);
     }
 
     @Override
     public Mono<<%= persistClass %>> findById(<%= primaryKey.type %> id) {
-        return createQuery(null, where(EntityManager.ENTITY_ALIAS + ".<%= primaryKey.fields[0].columnName %>").is(id)).one();
+        Comparison whereClause = Conditions.isEqual(entityTable.column("<%= primaryKey.fields[0].columnName %>"), Conditions.just(id.toString()));
+        return createQuery(null, whereClause).one();
     }
 
 <%_ if (implementsEagerLoadApis) { _%>

programming_language: Java
filename: EntityRepository_reactive.java.ejs
change_type: ModificationType.MODIFY
diff: @@ -144,8 +144,8 @@ interface <%= entityClass %>RepositoryInternal {
     Flux<<%= persistClass %>> findAll();
 
     Mono<<%= persistClass %>> findById(<%= primaryKey.type %> id);
-
-    Flux<<%= persistClass %>> findAllBy(Pageable pageable, Criteria criteria);
+    // this is not supported at the moment because of https://github.com/jhipster/generator-jhipster/issues/18269
+    // Flux<<%= persistClass %>> findAllBy(Pageable pageable, Criteria criteria);
 
   <%_ if (implementsEagerLoadApis) { _%>
 

programming_language: TypeScript
filename: EntityManager.java.ejs
change_type: ModificationType.MODIFY
diff: @@ -33,6 +33,7 @@ import org.springframework.data.r2dbc.core.StatementMapper;
 import org.springframework.data.r2dbc.mapping.OutboundRow;
 import org.springframework.data.r2dbc.query.UpdateMapper;
 import org.springframework.data.relational.core.query.Criteria;
+import org.springframework.data.relational.core.sql.Condition;
 import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
 import org.springframework.data.relational.core.sql.Conditions;
 import org.springframework.data.relational.core.sql.OrderByField;
@@ -93,19 +94,11 @@ public class EntityManager {
      * @param pageable page parameter, or null, if everything needs to be returned
      * @return sql select statement
      */
-    public String createSelect(SelectFromAndJoin selectFrom, Class<?> entityType, Pageable pageable, Criteria criteria) {
+    public String createSelect(SelectFromAndJoin selectFrom, Class<?> entityType, Pageable pageable) {
         if (pageable != null) {
-            if (criteria != null) {
-                return createSelectImpl(selectFrom.limitOffset(pageable.getPageSize(), pageable.getOffset()).where(Conditions.just(criteria.toString())), entityType, pageable.getSort());
-            } else {
-                return createSelectImpl(selectFrom.limitOffset(pageable.getPageSize(), pageable.getOffset()), entityType, pageable.getSort());
-            }
+            return createSelectImpl(selectFrom.limitOffset(pageable.getPageSize(), pageable.getOffset()), entityType, pageable.getSort());
         } else {
-            if (criteria != null) {
-                return createSelectImpl(selectFrom.where(Conditions.just(criteria.toString())), entityType, null);
-            } else {
-                return createSelectImpl(selectFrom, entityType, null);
-            }
+            return createSelectImpl(selectFrom, entityType, null);
         }
     }
 
@@ -116,19 +109,19 @@ public class EntityManager {
      * @param pageable page parameter, or null, if everything needs to be returned
      * @return sql select statement
      */
-    public String createSelect(SelectFromAndJoinCondition selectFrom, Class<?> entityType, Pageable pageable, Criteria criteria) {
+    public String createSelect(SelectFromAndJoinCondition selectFrom, Class<?> entityType, Pageable pageable, Condition where) {
         if (pageable != null) {
-            if (criteria != null) {
-                return createSelectImpl(selectFrom.limitOffset(pageable.getPageSize(), pageable.getOffset()).where(Conditions.just(criteria.toString())), entityType, pageable.getSort());
-            } else {
-                return createSelectImpl(selectFrom.limitOffset(pageable.getPageSize(), pageable.getOffset()), entityType, pageable.getSort());
-            }
+            return createSelectImpl(selectFrom.limitOffset(pageable.getPageSize(), pageable.getOffset()), entityType, pageable.getSort());
         } else {
-            if (criteria != null) {
-                return createSelectImpl(selectFrom.where(Conditions.just(criteria.toString())), entityType, null);
-            } else {
-                return createSelectImpl(selectFrom, entityType, null);
-            }
+            return createSelectImpl(selectFrom.where(where), entityType, null);
+        }
+    }
+
+    public String createSelect(SelectFromAndJoin selectFrom, Class<?> entityType, Pageable pageable, Condition where) {
+        if (pageable != null) {
+            return createSelectImpl(selectFrom.limitOffset(pageable.getPageSize(), pageable.getOffset()).where(where), entityType, pageable.getSort());
+        } else {
+            return createSelectImpl(selectFrom.where(where), entityType, null);
         }
     }
 

programming_language: Java