FINERACT-2461: Refactor EmailReadPlatformServiceImpl to use Prepared Statements#5417
Conversation
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file |
There was a problem hiding this comment.
Why this removed?
Apologies, that was accidental during the refactor. I have restored the License Header in the latest push.
|
These changes really improved the code, but in my opinion it would be much more beneficial to rewrite it from jdbcTemplate to Spring Data JPA. |
4c731e2 to
7eb8e83
Compare
Thank you for the review! I agree that moving to Spring Data JPA is the better long-term architectural goal. However, since this PR is focused on a critical Security Fix (SQL Injection), I would prefer to keep the scope limited to jdbcTemplate for now to ensure a faster merge. I can open a separate Jira ticket to handle the JPA migration as a follow-up task. Does that sound reasonable? |
7eb8e83 to
948f2b5
Compare
| public Collection<EmailData> retrieveAllFailed(final SearchParameters searchParameters) { | ||
| final String sqlPlusLimit = (searchParameters.getLimit() > 0) ? " " + sqlGenerator.limit(searchParameters.getLimit()) : ""; | ||
| final String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum = ?" + sqlPlusLimit; | ||
| String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum = ?"; |
There was a problem hiding this comment.
I see a pattern here i think you can create helper function here
There was a problem hiding this comment.
I see a pattern here i think you can create helper function here
Good catch. I have refactored the duplicate logic into a private helper method retrieveEmailByStatus to reduce code repetition
7eb4a6d to
c925217
Compare
e78692f to
097d594
Compare
adamsaghy
left a comment
There was a problem hiding this comment.
This PR does NOT change the EmailReadPlatformServiceImpl.java to use prepared statements.
097d594 to
5946091
Compare
Apologies @adamsaghy, I believe I accidentally reverted the changes while fixing the license header. I have just force-pushed the correct version with the prepared statements and helper method. Please verify |
| args.add(status); | ||
|
|
||
| if (sqlPlusLimit != null) { | ||
| sql += sqlPlusLimit; |
There was a problem hiding this comment.
it is still using string concatenation...
There was a problem hiding this comment.
it is still using string concatenation...
Ah, I missed converting the sql variable to a StringBuilder. I'll update it to use sql.append(sqlPlusLimit) instead of string concatenation. Pushing the fix shortly.
There was a problem hiding this comment.
No, that is not the problem.
The PR title says: use prepared statements. However, you are not using that for the sqlPlusLimit... you are still just concatenate a string to the SQL query itself. Please rewrite it to use prepared statements only and provide the arguments only.
5946091 to
cb18b0e
Compare
| if (sqlPlusLimit != null) { | ||
| sql.append(sqlPlusLimit); | ||
| } |
There was a problem hiding this comment.
The problem is:
you are not concatenating to the SQL the following: LIMIT ? but you are concatenating: LIMIT <whatever the user provided in query param> which is wrong...
There was a problem hiding this comment.
The problem is: you are not concatenating to the SQL the following:
LIMIT ?but you are concatenating:LIMIT <whatever the user provided in query param>which is wrong...
The sqlGenerator.limit() method returns a literal string (e.g. 'LIMIT 10'). I've cleaned up the code to pass the Integer limit down, but sticking to the generator to ensure MSSQL compatibility.
cb18b0e to
23fb382
Compare
adamsaghy
left a comment
There was a problem hiding this comment.
I havent seen any existing test cases for this functionality.
Would you mind to write a unit test which tests thoroughly these changes?
You should focus whether the appropriate status is added to the sql and also the appropriate LIMIT added to the sql.
Done. I have added EmailReadPlatformServiceImplTest to verify the logic. The new tests cover: testRetrieveAllPendingWithLimit(): Verifies that the LIMIT clause is correctly appended when provided. testRetrieveAllSentWithoutLimit(): Verifies that no LIMIT clause is added when the limit is null/zero, and checks that the correct status_enum parameter is bound. I verified the tests locally: Test testRetrieveAllPendingWithLimit() PASSED Test testRetrieveAllSentWithoutLimit() PASSED |
| private JdbcTemplate jdbcTemplate; | ||
|
|
||
| @Mock | ||
| private DatabaseSpecificSQLGenerator sqlGenerator; |
There was a problem hiding this comment.
Please dont mock the sqlGenerator. Mocking this makes the tests are completely useless...
You can mock the DatabaseTypeResolver only... and you can easily test for Mysql and Postgres as well.
Please make sure to test for positive test cases, like limit is "10" and "1" and for negative where limit is "random text", "-5", "5.6"
There was a problem hiding this comment.
Please dont mock the
sqlGenerator. Mocking this makes the tests are completely useless...You can mock the
DatabaseTypeResolveronly... and you can easily test for Mysql and Postgres as well.Please make sure to test for positive test cases, like limit is "10" and "1" and for negative where limit is "random text", "-5", "5.6"
Thanks for the detailed feedback, Adam. I have updated the PR with the following changes:
Refactoring: EmailReadPlatformServiceImpl now passes the Integer limit directly to the helper method instead of concatenating strings. This ensures limit is strictly an Integer, preventing injection of random text or floats by the compiler itself.
Testing Strategy: I removed the mock for DatabaseSpecificSQLGenerator. The new tests use the real generator and mock only the underlying DatabaseTypeResolver to simulate database dialects accurately.
Test Coverage: I added comprehensive test cases:
testRetrieveAllPendingWithMySQL: Verifies LIMIT 10 syntax for MySQL.
testRetrieveAllSentWithPostgres: Verifies LIMIT 5 syntax for PostgreSQL.
testRetrieveWithNegativeLimit & testRetrieveWithZeroLimit: Verifies that invalid limits (-5, 0) result in no LIMIT clause being appended.
Status: All 5 tests passed locally.
986770c to
2c62dc8
Compare
2c62dc8 to
dcbb3b7
Compare
…Statements (apache#5417) * FINERACT-2461: Refactor EmailReadPlatformServiceImpl to use Prepared Statements * FINERACT-2461: Add unit tests for EmailReadPlatformServiceImpl
Description
Refactored
EmailReadPlatformServiceImpl.javato replace legacy SQL string concatenation with JDBC Prepared Statements.This change prevents potential SQL injection vulnerabilities by using
?placeholders and passing parameters dynamically viaJdbcTemplate.Resolves FINERACT-2461.
Also related to FINERACT-2459.
Changes
retrieveAllPending,retrieveAllSent, and other read methods to use?placeholders.List<Object>to pass parameters dynamically.Checklist