Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ doubleRange: leftEndpoint=('(' | '[') SPACE? DOUBLE SPACE? ':' SPACE? DOUBLE SPA
dateRange: leftEndpoint=('(' | '[') SPACE? DATE SPACE? ':' SPACE? DATE SPACE? rightEndpoint=(')' | ']') ;
dateTimeRange: leftEndpoint=('(' | '[') SPACE? DATETIME SPACE? ':' SPACE? DATETIME SPACE? rightEndpoint=(')' | ']') ;
versionRange: leftEndpoint=('(' | '[') SPACE? VERSION_NUMBER SPACE? ':' SPACE? VERSION_NUMBER SPACE? rightEndpoint=(')' | ']') ;
STRING: '\'' (~('\'' | '\r' | '\n'))* '\'' ; // todo NAE-1997: escape???
STRING: '\'' ( '\\' . | ~('\'' | '\\' | '\r' | '\n') )* '\'' | '"' ( '\\' . | ~('"' | '\\' | '\r' | '\n') )* '"';
INT: DIGIT+ ;
DOUBLE: DIGIT+ '.' DIGIT+ ;
DATETIME: DATE 'T' ([01] DIGIT | '2' [0-3]) ':' [0-5] DIGIT ':' [0-5] DIGIT ('.' DIGIT+)? ; // 2020-03-03T20:00:00.055 // todo NAE-1997: format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import com.netgrif.application.engine.petrinet.domain.QPetriNet;
import com.netgrif.application.engine.petrinet.domain.version.QVersion;
import com.netgrif.application.engine.petrinet.domain.version.Version;
import com.netgrif.application.engine.pfql.domain.antlr4.QueryLangBaseListener;

Check failure on line 8 in src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java

View workflow job for this annotation

GitHub Actions / Build

package com.netgrif.application.engine.pfql.domain.antlr4 does not exist

Check failure on line 8 in src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java

View workflow job for this annotation

GitHub Actions / Test

package com.netgrif.application.engine.pfql.domain.antlr4 does not exist
import com.netgrif.application.engine.pfql.domain.antlr4.QueryLangLexer;

Check failure on line 9 in src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java

View workflow job for this annotation

GitHub Actions / Build

package com.netgrif.application.engine.pfql.domain.antlr4 does not exist

Check failure on line 9 in src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java

View workflow job for this annotation

GitHub Actions / Test

package com.netgrif.application.engine.pfql.domain.antlr4 does not exist
import com.netgrif.application.engine.pfql.domain.antlr4.QueryLangParser;

Check failure on line 10 in src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java

View workflow job for this annotation

GitHub Actions / Build

package com.netgrif.application.engine.pfql.domain.antlr4 does not exist

Check failure on line 10 in src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java

View workflow job for this annotation

GitHub Actions / Test

package com.netgrif.application.engine.pfql.domain.antlr4 does not exist
import com.netgrif.application.engine.pfql.domain.enums.ComparisonType;
import com.netgrif.application.engine.pfql.service.QueryLangErrorListener;
import com.netgrif.application.engine.pfql.service.QueryLangEvaluator;
Expand Down Expand Up @@ -180,7 +180,7 @@
}

public static String getStringValue(String queryLangString) {
return queryLangString.replace("'", "");
return queryLangString.replaceAll("^[\"']+|[\"']+$", "");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate files =="
fd -a 'SearchUtils.java|QueryLang.g4|QueryLangEvaluator.java' . | sed 's#^\./##'

echo
echo "== SearchUtils relevant section =="
file=$(fd 'SearchUtils.java' . | head -n 1)
if [ -n "$file" ]; then
  wc -l "$file"
  sed -n '130,210p' "$file" | cat -n
fi

echo
echo "== QueryLang parser/evaluator relevant sections =="
for f in $(fd 'QueryLang.g4|QueryLangEvaluator.java' .); do
  echo "--- $f"
  wc -l "$f"
  sed -n '320,360p' "$f" | cat +n
  sed -n '120,175p' "$f" | cat +n
done

echo
echo "== search for STRING literal handling / eval methods =="
rg -n "getString|QueryLangString|QueryLang|STRING|Escape|replaceAll|O\\\\'|\\\\\\'" src/main || true

echo
echo "== read-only deterministic probe: Java source literal token value extraction would be needed; inspect whether any unescape helper exists =="
rg -n "parse.*String|unescape|\\\\\\\\|replaceFirst|QueryLangString" src/main/java/com/netgrif/application/engine/pfql || true

Repository: netgrif/application-engine

Length of output: 4771


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== QueryLang.g4 string tokens =="
sed -n '320,360p' src/main/java/com/netgrif/application/engine/pfql/domain/antlr4/QueryLang.g4

echo
echo "== QueryLang.g4 parse/string rule annotations =="
rg -n "(@|STRING|queryLangString|EscapedCharacter)" src/main/java/com/netgrif/application/engine/pfql/domain/antlr4/QueryLang.g4

echo
echo "== QueryLangEvaluator string handling =="
sed -n '120,175p' src/main/java/com/netgrif/application/engine/pfql/service/QueryLangEvaluator.java

echo
echo "== SearchUtils predicate builders using getStringValue =="
rg -n "getStringValue\\(" src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java -A 8 -B 8

echo
echo "== unescape/substring literals and helper usages =="
rg -n "queryString|queryLangString|getStringValue|getString\\(|unescape|replaceFirst\\(\"^[\"']|\"\"\\)|\"\\\\\\\\\"" src/main/java/com/netgrif/application/engine/pfql/service src/main/java/com/netgrif/application/engine/pfql/domain/antlr4 -C 3

echo
echo "== read-only behavioral probe: Java lexer token text model for escaped string =="
python3 - <<'PY'
import re
text = r"['O\\'Reilly'\""
# Simulate Java-style source-text tokenization where escaped quotes are part of the token text.
# This does not execute Java code; it checks whether stripping delimiters alone leaves backslash.
s = "O\\'Reilly"
stripped = re.sub(r"^[\"']+|[\"']+$", "", "'" + s + "'")
print("lexer_token_text:", "'" + s + "'")
print("getStringValue_result:", stripped)
print("contains_backslash:", "\\" in stripped)
print("expected_after_decode_Java:", r"O'Reilly")
PY

Repository: netgrif/application-engine

Length of output: 44866


Decode STRING escape sequences before predicate construction.

QueryLang.g4 accepts escaped characters in STRING literals, but SearchUtils.getStringValue() only strips delimiters at src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java:182-183. This makes valid queries like 'O\'Reilly' reach the evaluator as O\'Reilly instead of O'Reilly. Decode escaped characters when extracting literal values, or narrow the lexer rule and call sites so that backslashes are not valid in string payloads. Add an end-to-end regression test for escaped quotes and backslash escapes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java`
at line 183, Update SearchUtils.getStringValue() to decode the escape sequences
accepted by QueryLang.g4 after removing the surrounding quote delimiters, so
escaped quotes and backslashes reach predicate evaluation as their literal
characters. Preserve unescaped string handling and add an end-to-end regression
test covering escaped quotes and backslash escapes.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove only the matching outer quote pair.

Line 183 removes every consecutive ' or " character at each boundary. It can remove quote characters that belong to the value. The valid PFQL literal '"foo"' becomes foo, not "foo".

Check the first and last characters and remove exactly one matching pair.

Proposed delimiter fix
-        return queryLangString.replaceAll("^[\"']+|[\"']+$", "");
+        if (queryLangString.length() < 2) {
+            return queryLangString;
+        }
+        char delimiter = queryLangString.charAt(0);
+        if ((delimiter != '\'' && delimiter != '"')
+                || queryLangString.charAt(queryLangString.length() - 1) != delimiter) {
+            return queryLangString;
+        }
+        return queryLangString.substring(1, queryLangString.length() - 1);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return queryLangString.replaceAll("^[\"']+|[\"']+$", "");
if (queryLangString.length() < 2) {
return queryLangString;
}
char delimiter = queryLangString.charAt(0);
if ((delimiter != '\'' && delimiter != '"')
|| queryLangString.charAt(queryLangString.length() - 1) != delimiter) {
return queryLangString;
}
return queryLangString.substring(1, queryLangString.length() - 1);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/netgrif/application/engine/pfql/service/utils/SearchUtils.java`
at line 183, Update the quote-stripping logic in SearchUtils to remove exactly
one outer pair only when the first and last characters are matching single or
double quotes. Preserve all remaining quote characters, including repeated or
mismatched boundary quotes, instead of using a regex that removes consecutive
delimiters.

}

public static ObjectId getObjectIdValue(String queryLangString) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ public void testSearchService() throws InterruptedException {
assertEquals(PageImpl.class, cases.getClass());
assertEquals(10, ((Page<Case>) cases).getTotalElements());

Object cases2 = searchService.search("cases: processIdentifier eq \"query_test\" page 1 size 5 sort by title desc");
assertNotNull(cases2);
assertEquals(PageImpl.class, cases2.getClass());
assertEquals(10, ((Page<Case>) cases2).getTotalElements());

Object case3 = searchService.search("case: processIdentifier eq 'query_test' and data.number_0.value == 3");
assertNotNull(case3);
assertEquals(Case.class, case3.getClass());
Expand All @@ -103,6 +108,21 @@ public void testSearchService() throws InterruptedException {
assertEquals(Case.class, case4.getClass());
assertEquals("4", ((Case) case4).getFieldValue("text_0"));

Object case4_2 = searchService.search("case: processIdentifier eq \"query_test\" and data.text_0.value == \"4\"");
assertNotNull(case4_2);
assertEquals(Case.class, case4_2.getClass());
assertEquals("4", ((Case) case4_2).getFieldValue("text_0"));

Object case4_3 = searchService.search("case: processIdentifier eq 'query_test' and data.text_0.value == \"4\"");
assertNotNull(case4_3);
assertEquals(Case.class, case4_3.getClass());
assertEquals("4", ((Case) case4_3).getFieldValue("text_0"));

Object case4_4 = searchService.search("case: processIdentifier eq \"query_test\" and data.text_0.value == '4'");
assertNotNull(case4_4);
assertEquals(Case.class, case4_4.getClass());
assertEquals("4", ((Case) case4_4).getFieldValue("text_0"));
Comment thread
Retoocs marked this conversation as resolved.

Object case5 = searchService.search("case: processIdentifier eq 'query_test' and data.boolean_0.value == true");
assertNotNull(case5);
assertEquals(Case.class, case5.getClass());
Expand Down
Loading