Skip to content

Conversation

@tuplle
Copy link
Member

@tuplle tuplle commented Dec 18, 2025

Description

Implements the sanitation of the fullText value for Elasticsearch queries. Search request objects have sanitation implemented in their deserialization.

Implements NAE-2310

Dependencies

No new dependencies were introduced.

Blocking Pull requests

There are no dependencies on other PR

How Has Been This Tested?

Manualy testing with calling request of case and task search.
The sanitation function has a test:

  • com.netgrif.application.engine.elastic.ElasticsearchQuerySanitizerTest.java

Test Configuration

Name Tested on
OS Linux
Runtime OpenJDK 11
Dependency Manager Maven 3.9
Framework version Spring
Run parameters
Other configuration

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My changes have been checked, personally or remotely, with @...
  • I have commented my code, particularly in hard-to-understand areas
  • I have resolved all conflicts with the target branch of the PR
  • I have updated and synced my code with the target branch
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing tests pass locally with my changes:
    • Lint test
    • Unit tests
    • Integration tests
  • I have checked my contribution with code analysis tools:
  • I have made corresponding changes to the documentation:
    • Developer documentation
    • User Guides
    • Migration Guides

Summary by CodeRabbit

  • New Features

    • Full-text search inputs are now automatically sanitized to escape or remove reserved Elasticsearch characters and operators, with optional exclusions.
    • Single-item vs. list search request parsing for case and task searches now consistently applies sanitization during deserialization.
  • Tests

    • Added comprehensive tests covering sanitization behavior, exclusions, edge cases, and mixed inputs.

✏️ Tip: You can customize this high-level summary in your review settings.

- Introduced `ElasticsearchQuerySanitizer` for escaping and removing reserved characters during query sanitization.
- Updated relevant request parsing classes to apply sanitization on `fullText` fields.
- Added unit tests for `ElasticsearchQuerySanitizer` to ensure robust functionality.
- Ensures proper contextual deserialization based on the type of the target property.
- Added detailed Javadoc for `ElasticsearchQuerySanitizer` methods.
- Updated custom deserializers to sanitize `fullText` fields in `TaskSearchRequest` and `CaseSearchRequest` using `ElasticsearchQuerySanitizer`.
@tuplle tuplle self-assigned this Dec 18, 2025
@tuplle tuplle added bugfix A change that fixes a bug improvement A change that improves on an existing feature critical A change that is critical to a release and must not be omitted labels Dec 18, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 18, 2025

Walkthrough

Adds a new ElasticsearchQuerySanitizer and applies it to full-text fields during request construction and deserialization; introduces case/task-specific single-item-as-list deserializers, enhances SingleItemAsListDeserializer helpers for contextual typing and wrapper detection, updates JsonDeserialize annotations, and adds unit tests for the sanitizer.

Changes

Cohort / File(s) Summary
Core Sanitizer
src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java
New public utility providing static sanitization for Elasticsearch query strings; defines reserved characters/keywords and public sanitize(String) / sanitize(String, String[]).
Search Request Integration
src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/CaseSearchRequest.java, src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/ElasticTaskSearchRequest.java
Import and usage of ElasticsearchQuerySanitizer.sanitize() for the fullText field during request construction; minor stream/formatting tweak in ElasticTaskSearchRequest (added .collect()).
Custom Deserializers
src/main/java/com/netgrif/application/engine/workflow/utils/CaseSearchRequestSingleItemAsListDeserializer.java, src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java
New contextual deserializers extending SingleItemAsListDeserializer; override createContextual/deserialize and sanitize each deserialized item's fullText when wrapper types are detected.
Deserializer Reference Updates
src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleCaseSearchRequestAsList.java, src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/singleaslist/SingleTaskSearchRequestAsList.java, src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleElasticTaskSearchRequestAsList.java
Updated @JsonDeserialize annotations and imports to use the case-/task-specific single-item-as-list deserializers instead of the generic SingleItemAsListDeserializer.
SingleItemAsListDeserializer Enhancements
src/main/java/com/netgrif/application/engine/utils/SingleItemAsListDeserializer.java
Added getItemClass(DeserializationContext, BeanProperty) helper for contextual typing and isWrapperClass(Object, Class, Class) utility to detect wrapper instances.
Tests
src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java
New JUnit 5 test class covering sanitizer behavior: reserved chars/keywords, exclusions, null/blank handling, and complex inputs.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Jackson as "Jackson\n(JsonDeserializer)"
    participant Sanitizer as "ElasticsearchQuerySanitizer"
    participant RequestObj as "SearchRequest\n(Case/Task)"
    participant ES as "Elasticsearch"

    Client->>Jackson: POST JSON (single item or list) with fullText
    Jackson->>Jackson: parse JSON -> produce wrapper or list of items
    alt item(s) contain fullText
        Jackson->>Sanitizer: sanitize(fullText)
        Sanitizer-->>Jackson: sanitized fullText
    end
    Jackson->>RequestObj: instantiate item(s) with sanitized fullText
    RequestObj->>ES: execute search using RequestObj.fullText
    ES-->>Client: return results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect ElasticsearchQuerySanitizer.RESERVED_KEYWORDS and escape/remove mappings for correctness against Elasticsearch query syntax.
  • Review SingleItemAsListDeserializer.getItemClass and isWrapperClass reflection logic for safety and edge-case handling.
  • Verify CaseSearchRequestSingleItemAsListDeserializer and TaskSearchRequestSingleItemAsListDeserializer detect wrapper types correctly and only sanitize intended classes.
  • Confirm ElasticTaskSearchRequest stream .collect() produces the expected List and handles nulls.
  • Run and validate ElasticsearchQuerySanitizerTest coverage and assertions.

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.76% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: implementing sanitation of Elasticsearch fulltext query input, which aligns with the primary objective of sanitizing fullText values across the codebase.

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f95ede and 5fa7cef.

📒 Files selected for processing (2)
  • src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1 hunks)
  • src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.
📚 Learning: 2025-12-18T19:20:32.658Z
Learnt from: Smotanka
Repo: netgrif/application-engine PR: 401
File: src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java:57-66
Timestamp: 2025-12-18T19:20:32.658Z
Learning: When performing multiple string replacements in Java files (e.g., sanitization/escaping logic like ElasticsearchQuerySanitizer), prefer using StringUtils.replaceEach() over chaining multiple StringUtils.replace() calls. This avoids cascading replacements where the result of one replacement triggers another replacement and potentially corrupts the final output. Apply this pattern to other similar sanitization code across the codebase as appropriate.

Applied to files:

  • src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java
📚 Learning: 2025-12-18T19:20:32.658Z
Learnt from: Smotanka
Repo: netgrif/application-engine PR: 401
File: src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java:57-66
Timestamp: 2025-12-18T19:20:32.658Z
Learning: In the netgrif/application-engine project, when performing multiple string replacements for sanitization or escaping, use `StringUtils.replaceEach()` instead of chained `StringUtils.replace()` calls to prevent cascading replacements where the output of one replacement gets replaced again, which can corrupt the result.

Applied to files:

  • src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java
🧬 Code graph analysis (1)
src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1)
src/main/java/com/netgrif/application/engine/utils/SingleItemAsListDeserializer.java (1)
  • SingleItemAsListDeserializer (17-84)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (2)
src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java (1)

1-205: Excellent test coverage!

The test suite is comprehensive and well-structured. All previous review feedback has been successfully addressed:

  • ✅ Spring Boot test context removed (simple unit tests appropriate for static utility)
  • ✅ Comprehensive edge case coverage (null, empty, blank, reserved/non-reserved characters)
  • ✅ Exclusion logic thoroughly tested (null, empty, non-existent keywords)
  • ✅ Complex real-world scenarios included
  • ✅ Clear naming conventions and focused test methods

The test suite effectively validates the sanitizer's behavior across all critical scenarios, providing confidence in the implementation.

src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1)

28-72: LGTM! Clean refactoring and proper sanitization implementation.

The refactored if-else logic (lines 62-67) is significantly more readable than the previous nested ternary operator, addressing prior feedback effectively.

Implementation strengths:

  • Proper delegation to parent deserializer with post-processing
  • Safe list extraction with clear instanceof checks
  • Null-safe sanitization (ElasticsearchQuerySanitizer.sanitize handles null input)
  • Empty list initialization prevents edge case issues
  • Consistent pattern with CaseSearchRequestSingleItemAsListDeserializer
  • Comprehensive Javadoc documentation

Safety considerations verified:

  • Collections.emptyList() is reassigned, not mutated (safe)
  • forEach on empty list is a no-op (safe fallback)
  • Field mutation is intentional per PR design
  • Maintains parent's exception contract

The implementation correctly sanitizes fullText fields for both TaskSearchRequest and ElasticTaskSearchRequest wrappers, aligning with the PR's objective of preventing injection attacks in Elasticsearch queries.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f13e26 and 43d3a78.

📒 Files selected for processing (8)
  • src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java (1 hunks)
  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/CaseSearchRequest.java (3 hunks)
  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/ElasticTaskSearchRequest.java (3 hunks)
  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleCaseSearchRequestAsList.java (1 hunks)
  • src/main/java/com/netgrif/application/engine/workflow/utils/CaseSearchRequestSingleItemAsListDeserializer.java (1 hunks)
  • src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1 hunks)
  • src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/singleaslist/SingleTaskSearchRequestAsList.java (1 hunks)
  • src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/CaseSearchRequest.java
📚 Learning: 2025-08-19T20:13:40.087Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.

Applied to files:

  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/CaseSearchRequest.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/ElasticTaskSearchRequest.java
📚 Learning: 2025-10-20T11:44:44.907Z
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.

Applied to files:

  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/ElasticTaskSearchRequest.java
🧬 Code graph analysis (2)
src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleCaseSearchRequestAsList.java (2)
src/main/java/com/netgrif/application/engine/workflow/utils/CaseSearchRequestSingleItemAsListDeserializer.java (1)
  • CaseSearchRequestSingleItemAsListDeserializer (32-88)
src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/singleaslist/SingleTaskSearchRequestAsList.java (1)
  • JsonDeserialize (8-10)
src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/singleaslist/SingleTaskSearchRequestAsList.java (2)
src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1)
  • TaskSearchRequestSingleItemAsListDeserializer (28-84)
src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleCaseSearchRequestAsList.java (1)
  • JsonDeserialize (8-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: Build
🔇 Additional comments (8)
src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/ElasticTaskSearchRequest.java (2)

23-33: Good catch on the missing stream termination.

The addition of .collect(Collectors.toList()) fixes a bug where the stream was not being collected into the useCase list. Without this terminal operation, the mapping would not have been executed and useCase would remain uninitialized.


47-50: Sanitization applied correctly and consistently.

The fullText sanitization matches the approach in CaseSearchRequest. The logic is consistent and correct.

src/main/java/com/netgrif/application/engine/workflow/web/requestbodies/singleaslist/SingleTaskSearchRequestAsList.java (1)

5-10: Deserializer reference updated correctly.

The change from SingleItemAsListDeserializer to TaskSearchRequestSingleItemAsListDeserializer enables fullText sanitization during deserialization. This is consistent with the PR's objective.

src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleCaseSearchRequestAsList.java (1)

6-10: Deserializer reference updated correctly.

The change from SingleItemAsListDeserializer to CaseSearchRequestSingleItemAsListDeserializer is consistent with the pattern used in SingleTaskSearchRequestAsList.

src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1)

64-72: Verify null safety in sanitization.

Similar to CaseSearchRequestSingleItemAsListDeserializer, this code sanitizes request.fullText without an explicit null check. While the sanitizer handles null inputs, consider adding a defensive check or documenting the null-safety contract.

src/main/java/com/netgrif/application/engine/workflow/utils/CaseSearchRequestSingleItemAsListDeserializer.java (1)

68-76: No defensive null check needed—sanitize() already handles nulls safely.

The ElasticsearchQuerySanitizer.sanitize() method explicitly checks for null at line 58 (if (query == null || query.isBlank())), making the code null-safe as written. No additional check in the deserializer is required.

src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java (1)

74-78: Verify if character-by-character escaping is the intended behavior for Elasticsearch query sanitization.

The code escapes each character in reserved strings individually (e.g., "AND" → "\A\N\D"). This character-by-character decomposition approach differs from Elasticsearch's standard escaping model, where reserved characters are typically escaped individually within the complete term (e.g., "AND" → "\AND" or handled by quoting/replacement). Verify whether this approach is intentional or if the sanitizer should instead escape each character only once or replace reserved keywords entirely.

src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/CaseSearchRequest.java (1)

78-81: Sanitization applied correctly.

The fullText field is properly sanitized using ElasticsearchQuerySanitizer.sanitize(). The intermediate variable originalFullText is good for debugging purposes. Escaped characters in Elasticsearch query_string queries are treated literally rather than as operators, so sanitization prevents special characters from being interpreted as query syntax operators while still allowing users to search for those literal values.

The query field already exists on line 44 and receives unsanitized input, providing users an alternative path for advanced Elasticsearch query syntax when needed.

- Enhanced `ElasticsearchQuerySanitizerTest` with additional unit tests for various edge cases.
- Simplified `ElasticsearchQuerySanitizer` implementation for better readability and efficiency.
@coderabbitai coderabbitai bot added the Medium label Dec 18, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/main/java/com/netgrif/application/engine/workflow/utils/CaseSearchRequestSingleItemAsListDeserializer.java (1)

44-67: Code duplication with TaskSearchRequestSingleItemAsListDeserializer persists.

This class remains structurally identical to TaskSearchRequestSingleItemAsListDeserializer, differing only in type references. While the current implementation is correct and functional, the duplication increases maintenance burden and risk of inconsistencies.

Consider extracting the common sanitization logic into a generic base class or using a parameterized approach as suggested in previous reviews.

src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1)

40-63: Code duplication with CaseSearchRequestSingleItemAsListDeserializer persists.

This class is structurally identical to CaseSearchRequestSingleItemAsListDeserializer, differing only in type references. The duplication concern raised in previous reviews remains unaddressed.

While functionally correct, consider refactoring to reduce maintenance burden.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43d3a78 and 17bf9a0.

📒 Files selected for processing (5)
  • src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java (1 hunks)
  • src/main/java/com/netgrif/application/engine/utils/SingleItemAsListDeserializer.java (3 hunks)
  • src/main/java/com/netgrif/application/engine/workflow/utils/CaseSearchRequestSingleItemAsListDeserializer.java (1 hunks)
  • src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1 hunks)
  • src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.
📚 Learning: 2025-12-18T19:20:32.658Z
Learnt from: Smotanka
Repo: netgrif/application-engine PR: 401
File: src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java:57-66
Timestamp: 2025-12-18T19:20:32.658Z
Learning: When performing multiple string replacements in Java files (e.g., sanitization/escaping logic like ElasticsearchQuerySanitizer), prefer using StringUtils.replaceEach() over chaining multiple StringUtils.replace() calls. This avoids cascading replacements where the result of one replacement triggers another replacement and potentially corrupts the final output. Apply this pattern to other similar sanitization code across the codebase as appropriate.

Applied to files:

  • src/main/java/com/netgrif/application/engine/utils/SingleItemAsListDeserializer.java
  • src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java
  • src/main/java/com/netgrif/application/engine/workflow/utils/CaseSearchRequestSingleItemAsListDeserializer.java
  • src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java
📚 Learning: 2025-12-18T19:20:32.658Z
Learnt from: Smotanka
Repo: netgrif/application-engine PR: 401
File: src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java:57-66
Timestamp: 2025-12-18T19:20:32.658Z
Learning: In the netgrif/application-engine project, when performing multiple string replacements for sanitization or escaping, use `StringUtils.replaceEach()` instead of chained `StringUtils.replace()` calls to prevent cascading replacements where the output of one replacement gets replaced again, which can corrupt the result.

Applied to files:

  • src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java
📚 Learning: 2025-10-20T11:44:44.907Z
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.

Applied to files:

  • src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java
📚 Learning: 2025-08-19T20:13:40.087Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.

Applied to files:

  • src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java
🧬 Code graph analysis (2)
src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1)
src/main/java/com/netgrif/application/engine/utils/SingleItemAsListDeserializer.java (1)
  • SingleItemAsListDeserializer (17-84)
src/main/java/com/netgrif/application/engine/workflow/utils/CaseSearchRequestSingleItemAsListDeserializer.java (1)
src/main/java/com/netgrif/application/engine/utils/SingleItemAsListDeserializer.java (1)
  • SingleItemAsListDeserializer (17-84)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (13)
src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java (1)

1-9: LGTM - Test setup simplified.

The test class now uses plain JUnit 5 without Spring Boot test context, which is appropriate for testing static utility methods. This addresses the previous feedback about unnecessary test infrastructure.

src/main/java/com/netgrif/application/engine/utils/SingleItemAsListDeserializer.java (2)

12-15: LGTM - Imports support new reflection-based wrapper detection.

The new imports (ParameterizedType, Type, Objects) are required for the isWrapperClass utility method added at lines 74-83.


32-43: LGTM - Clean extraction of item class resolution.

The new getItemClass method properly extracts the raw class from the contextual JavaType. This refactoring improves clarity and allows subclasses to customize type resolution if needed.

src/main/java/com/netgrif/application/engine/workflow/utils/CaseSearchRequestSingleItemAsListDeserializer.java (2)

1-29: LGTM - Clear documentation and appropriate imports.

The class documentation clearly explains its purpose: handling single-item-or-list deserialization for CaseSearchRequest and ensuring fullText sanitization.


31-42: LGTM - Standard contextual deserializer pattern.

The constructors and createContextual method follow the correct pattern for Jackson contextual deserializers, properly delegating to the parent class and creating type-specific instances.

src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (2)

1-25: LGTM - Clear documentation and structure.

The class documentation clearly explains its purpose for handling TaskSearchRequest deserialization and sanitization.


27-38: LGTM - Standard contextual deserializer pattern.

The constructors and createContextual method correctly follow the Jackson contextual deserializer pattern.

src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java (6)

1-25: LGTM - Appropriate dependencies and clear documentation.

The class uses appropriate dependencies (Lombok for logging, Apache Commons Lang for string utilities) and provides clear documentation of its purpose.


27-29: LGTM - Appropriate reserved character list for query_string queries.

The reserved characters and keywords correctly cover Elasticsearch query_string syntax elements. Notable behaviors:

  • Space escaping: Spaces are escaped to \ , which is necessary to prevent unintended term splitting in query_string queries.
  • Angle bracket handling: > and < are replaced with escaped space to prevent range query injection.

This aligns with the confirmed usage in query_string queries (ElasticCaseService line 398, ElasticTaskService line 356).


31-44: LGTM - Clean delegation pattern.

The single-argument sanitize method appropriately delegates to the full implementation with null exclusions.


46-67: LGTM - Correct sanitization implementation with proper replacement strategy.

The sanitization logic correctly:

  • Returns null/blank inputs unchanged
  • Uses StringUtils.replaceEach() to avoid cascading replacement issues (per learning)
  • Applies exclusions when needed
  • Logs at trace level (appropriate for detailed debugging)

This implementation properly prevents query injection in Elasticsearch query_string queries.

Based on learnings: Uses StringUtils.replaceEach() to prevent cascading replacements.


69-82: LGTM - Correct escaping logic for Elasticsearch syntax.

The method correctly:

  • Escapes each character in reserved strings (e.g., "AND" → "\A\N\D")
  • Replaces angle brackets with escaped space to prevent range queries
  • Returns an immutable map to prevent accidental modification

The inline initialization of arrays at lines 27-28 ensures they cannot be null, so no null-checks are needed here.


84-95: LGTM - Safe exclusion logic with proper validation.

The method correctly:

  • Returns the full keyword map when no exclusions are provided
  • Creates a mutable copy for filtering
  • Only removes keywords that actually exist in the reserved map (line 90)
  • Returns an immutable view of the filtered result

This prevents issues with invalid exclusion keys while allowing legitimate structured query building.

- Updated `TaskSearchRequestSingleItemAsListDeserializer` to support `SingleElasticTaskSearchRequestAsList`.
- Applied `ElasticsearchQuerySanitizer` to sanitize `fullText` in elastic task search requests during deserialization.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17bf9a0 and df0c11e.

📒 Files selected for processing (2)
  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleElasticTaskSearchRequestAsList.java (1 hunks)
  • src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.
📚 Learning: 2025-12-18T19:20:32.658Z
Learnt from: Smotanka
Repo: netgrif/application-engine PR: 401
File: src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java:57-66
Timestamp: 2025-12-18T19:20:32.658Z
Learning: When performing multiple string replacements in Java files (e.g., sanitization/escaping logic like ElasticsearchQuerySanitizer), prefer using StringUtils.replaceEach() over chaining multiple StringUtils.replace() calls. This avoids cascading replacements where the result of one replacement triggers another replacement and potentially corrupts the final output. Apply this pattern to other similar sanitization code across the codebase as appropriate.

Applied to files:

  • src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java
  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleElasticTaskSearchRequestAsList.java
📚 Learning: 2025-10-20T11:44:44.907Z
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.

Applied to files:

  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleElasticTaskSearchRequestAsList.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleElasticTaskSearchRequestAsList.java
🧬 Code graph analysis (2)
src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1)
src/main/java/com/netgrif/application/engine/utils/SingleItemAsListDeserializer.java (1)
  • SingleItemAsListDeserializer (17-84)
src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleElasticTaskSearchRequestAsList.java (1)
src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1)
  • TaskSearchRequestSingleItemAsListDeserializer (28-73)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: task-list-completed
  • GitHub Check: Build
  • GitHub Check: task-list-completed
🔇 Additional comments (2)
src/main/java/com/netgrif/application/engine/workflow/utils/TaskSearchRequestSingleItemAsListDeserializer.java (1)

67-68: No null check needed—sanitizer handles null input gracefully.

The ElasticsearchQuerySanitizer.sanitize() method already handles null input with an explicit null check at line 58: if (query == null || query.isBlank()) { return query; }. No additional null checking at the call site is required.

Likely an incorrect or invalid review comment.

src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/singleaslist/SingleElasticTaskSearchRequestAsList.java (1)

6-8: Approve the deserializer change.

The new TaskSearchRequestSingleItemAsListDeserializer correctly enables fullText sanitization during deserialization while maintaining full backward compatibility. The deserializer preserves base deserialization functionality and applies sanitization as an additive security layer that is transparent to valid API inputs.

- Added new test cases in `ElasticsearchQuerySanitizerTest` to cover additional scenarios.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df0c11e and 5f95ede.

📒 Files selected for processing (1)
  • src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.
📚 Learning: 2025-12-18T19:20:32.658Z
Learnt from: Smotanka
Repo: netgrif/application-engine PR: 401
File: src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java:57-66
Timestamp: 2025-12-18T19:20:32.658Z
Learning: In the netgrif/application-engine project, when performing multiple string replacements for sanitization or escaping, use `StringUtils.replaceEach()` instead of chained `StringUtils.replace()` calls to prevent cascading replacements where the output of one replacement gets replaced again, which can corrupt the result.

Applied to files:

  • src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (2)
src/test/java/com/netgrif/application/engine/elastic/ElasticsearchQuerySanitizerTest.java (2)

1-203: Excellent test coverage and structure!

The test suite comprehensively covers the sanitization logic with appropriate edge cases, null handling, and exclusion scenarios. The removal of the Spring Boot test context (as addressed in previous feedback) makes these tests lightweight and fast.


94-98: No changes needed—blank query handling is intentional.

The sanitizer explicitly preserves blank or whitespace-only queries without modification (line 58-60: if (query == null || query.isBlank()) { return query; }). This design choice means blank queries bypass sanitization entirely, which is why " " returns unchanged. The space escaping shown in shouldEscapeSpaces applies only to non-blank queries that proceed through the sanitization logic. The test expectation is correct.

- Corrected method name typo in `ElasticsearchQuerySanitizerTest`.
- Refactored `TaskSearchRequestSingleItemAsListDeserializer` for better readability and initialization clarity.
Copy link
Member

@renczesstefan renczesstefan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review my comment.

@tuplle tuplle merged commit c4d948e into release/6.4.2 Dec 19, 2025
5 of 7 checks passed
@tuplle tuplle deleted the NAE-2310 branch December 19, 2025 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix A change that fixes a bug critical A change that is critical to a release and must not be omitted improvement A change that improves on an existing feature Medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants