CVE-2022-24815 (Other, published 2022-04-11T20:15Z)
Description: value: 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 v3 base score: 8.1)
Weakness: CWE-89 \u2014 Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
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.
Repository: https://github.com/jhipster/generator-jhipster
Files changed:
generators/entity-server/templates/src/main/java/package/repository/EntityRepositoryInternalImpl_reactive.java.ejs
generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs
generators/server/templates/src/main/java/package/repository/EntityManager.java.ejs
Commit message: use conditions instead of criteria as workaround

closes #18269


Vulnerable code:
        return findAllBy(pageable, null);
    @Override
    public Flux<<%= persistClass %>> findAllBy(Pageable pageable, Criteria criteria) {
        return createQuery(pageable, criteria).all();
    }

    RowsFetchSpec<<%= persistClass %>> createQuery(Pageable pageable, Criteria criteria) {

        String select = entityManager.createSelect(selectFrom, <%= persistClass %>.class, pageable, criteria);
        return findAllBy(null, null);
        return createQuery(null, where(EntityManager.ENTITY_ALIAS + ".<%= primaryKey.fields[0].columnName %>").is(id)).one();

    Flux<<%= persistClass %>> findAllBy(Pageable pageable, Criteria criteria);
    public String createSelect(SelectFromAndJoin selectFrom, Class<?> entityType, Pageable pageable, Criteria criteria) {
            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());
            }
            if (criteria != null) {
                return createSelectImpl(selectFrom.where(Conditions.just(criteria.toString())), entityType, null);
            } else {
                return createSelectImpl(selectFrom, entityType, null);
            }
    public String createSelect(SelectFromAndJoinCondition selectFrom, Class<?> entityType, Pageable pageable, Criteria criteria) {
            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());
            }
            if (criteria != null) {
                return createSelectImpl(selectFrom.where(Conditions.just(criteria.toString())), entityType, null);
            } else {
                return createSelectImpl(selectFrom, entityType, null);
            }

Fixed code:
import org.springframework.data.relational.core.sql.Comparison;
import org.springframework.data.relational.core.sql.Condition;
import org.springframework.data.relational.core.sql.Conditions;
        return createQuery(pageable, null).all();
    RowsFetchSpec<<%= persistClass %>> createQuery(Pageable pageable, Condition whereClause) {
        // 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 findAllBy(null);
        Comparison whereClause = Conditions.isEqual(entityTable.column("<%= primaryKey.fields[0].columnName %>"), Conditions.just(id.toString()));
        return createQuery(null, whereClause).one();
    // this is not supported at the moment because of https://github.com/jhipster/generator-jhipster/issues/18269
    // Flux<<%= persistClass %>> findAllBy(Pageable pageable, Criteria criteria);
import org.springframework.data.relational.core.sql.Condition;
    public String createSelect(SelectFromAndJoin selectFrom, Class<?> entityType, Pageable pageable) {
            return createSelectImpl(selectFrom.limitOffset(pageable.getPageSize(), pageable.getOffset()), entityType, pageable.getSort());
            return createSelectImpl(selectFrom, entityType, null);
    public String createSelect(SelectFromAndJoinCondition selectFrom, Class<?> entityType, Pageable pageable, Condition where) {
            return createSelectImpl(selectFrom.limitOffset(pageable.getPageSize(), pageable.getOffset()), entityType, pageable.getSort());
            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);