-
Notifications
You must be signed in to change notification settings - Fork 7
[NAE-2243] Dynamic date resolution in Elastic query #376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/7.0.0-rev10
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField; | ||
| import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; | ||
| import com.netgrif.application.engine.configuration.properties.DataConfigurationProperties; | ||
| import com.netgrif.application.engine.elastic.service.interfaces.IElasticQueryFactory; | ||
| import com.netgrif.application.engine.objects.auth.domain.LoggedUser; | ||
| import com.netgrif.application.engine.objects.elastic.domain.ElasticCase; | ||
| import com.netgrif.application.engine.elastic.domain.ElasticCaseRepository; | ||
|
|
@@ -39,6 +40,8 @@ | |
| import org.springframework.data.elasticsearch.core.query.Order; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.time.LocalDateTime; | ||
| import java.util.*; | ||
| import java.util.function.BinaryOperator; | ||
| import java.util.stream.Collectors; | ||
|
|
@@ -86,6 +89,9 @@ public void setElasticProperties(DataConfigurationProperties.ElasticsearchProper | |
| this.elasticProperties = elasticProperties; | ||
| } | ||
|
|
||
| @Autowired | ||
| protected IElasticQueryFactory queryFactory; | ||
|
|
||
| @Override | ||
| public void remove(String caseId) { | ||
| executors.execute(caseId, () -> { | ||
|
|
@@ -430,7 +436,17 @@ protected void buildStringQuery(CaseSearchRequest request, BoolQuery.Builder que | |
| return; | ||
| } | ||
|
|
||
| String populatedQuery = request.query.replaceAll(ElasticQueryConstants.USER_ID_TEMPLATE, user.getId().toString()); | ||
| String populatedQuery; | ||
| if (request.dynamicQuery) { | ||
| populatedQuery = queryFactory.populateQuery(request.query, new HashMap<>(Map.of( | ||
| ElasticQueryConstants.DYNAMIC_USER_ID_TEMPLATE, user.getId().toString(), | ||
| ElasticQueryConstants.LOCAL_DATE_NOW_TEMPLATE, LocalDateTime.now(), | ||
| ElasticQueryConstants.LOCAL_DATE_TODAY_TEMPLATE, LocalDate.now(), | ||
| ElasticQueryConstants.LOGGED_USER_TEMPLATE, user | ||
| ))); | ||
| } else { | ||
| populatedQuery = request.query.replaceAll(ElasticQueryConstants.USER_ID_TEMPLATE, user.getId().toString()); | ||
| } | ||
|
Comment on lines
+439
to
+449
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainCritical: Remote code execution via Groovy templating of user input request.query is user-controlled. Rendering it with Groovy (via queryFactory) executes arbitrary Groovy code in-process. This is an RCE vector. Immediate mitigations:
Minor: Use replace instead of replaceAll for a fixed literal. Would you like me to push a safe placeholder-based implementation and wire it here? 🏁 Script executed: Length of output: 94 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 1435 🏁 Script executed: Length of output: 1859 🏁 Script executed: Length of output: 453 🏁 Script executed: Length of output: 503 🏁 Script executed: Length of output: 1336 🏁 Script executed: Length of output: 4468 🏁 Script executed: Length of output: 9906 🏁 Script executed: Length of output: 4336 🏁 Script executed: Length of output: 371 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 16879 🏁 Script executed: Length of output: 1508 🏁 Script executed: Length of output: 1116 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 2857 🏁 Script executed: Length of output: 920 🏁 Script executed: Length of output: 0 Critical: Remote code execution via Groovy template injection ElasticQueryFactory.populateQuery() passes user-controlled request.query directly to Groovy's SimpleTemplateEngine, which executes arbitrary code in template expressions. An authenticated attacker can exploit this by sending: Required fixes:
This is an in-process code execution vulnerability requiring immediate remediation. 🤖 Prompt for AI Agents |
||
|
|
||
| query.must(QueryStringQuery.of(builder -> builder.query(populatedQuery).allowLeadingWildcard(true).analyzeWildcard(true))._toQuery()); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package com.netgrif.application.engine.elastic.service; | ||
|
|
||
| import com.netgrif.application.engine.elastic.service.interfaces.IElasticQueryFactory; | ||
| import groovy.text.SimpleTemplateEngine; | ||
| import groovy.text.Template; | ||
| import lombok.Getter; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.codehaus.groovy.control.CompilationFailedException; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| @Slf4j | ||
| @Service | ||
| public class ElasticQueryFactory implements IElasticQueryFactory { | ||
|
|
||
| private final SimpleTemplateEngine templateEngine; | ||
| @Getter | ||
| private final Map<String, Object> context; | ||
|
|
||
| public ElasticQueryFactory() { | ||
| this.templateEngine = new SimpleTemplateEngine(); | ||
| this.context = new HashMap<>(); | ||
| } | ||
|
|
||
| @Override | ||
| public String populateQuery(String query, Map<String, Object> queryContext) { | ||
| String populatedQuery = query; | ||
| try { | ||
| queryContext.putAll(context); | ||
| Template template = templateEngine.createTemplate(query); | ||
| populatedQuery = template.make(queryContext).toString(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be a security risk. I think it needs to be discussed. |
||
| } catch (CompilationFailedException | ClassNotFoundException | IOException e) { | ||
| log.error("Cannot populate template from string query", e); | ||
| } | ||
| return populatedQuery; | ||
| } | ||
|
Comment on lines
+19
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blocker: Groovy template execution on untrusted input = RCE SimpleTemplateEngine evaluates Groovy inside ${...}. With user-supplied query strings this enables arbitrary code execution. Replace with safe placeholder substitution and remove Groovy. Also avoid mutating inputs and leaking mutable context. Apply this safe, dependency-free implementation:
If richer functions are required (e.g., plusDays), implement explicit helpers rather than evaluating code. I can extend this to support plusDays/plusHours safely if needed. 🤖 Prompt for AI Agents |
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.netgrif.application.engine.elastic.service.interfaces; | ||
|
|
||
| import java.util.Map; | ||
|
|
||
| public interface IElasticQueryFactory { | ||
| String populateQuery(String query, Map<String, Object> queryContext); | ||
| } | ||
|
Comment on lines
+5
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Constrain the template inputs Accepting an arbitrary Map broadens attack surface and makes validation hard. Prefer a typed context (e.g., QueryVariables with me, today, now, loggedUserId) to whitelist allowed values. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,8 @@ public class CaseSearchRequest implements Serializable { | |
|
|
||
| public String query; | ||
|
|
||
| public boolean dynamicQuery; | ||
|
|
||
|
Comment on lines
+47
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dynamicQuery is never parsed in the Map constructor Requests built via the Map-based ctor will silently ignore the flag. Parse it to avoid unexpected defaults. Apply this diff inside the constructor: Also applies to: 64-106 |
||
| @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) | ||
| public List<String> id; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.netgrif.application.engine.elastic; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.ApplicationEngine; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.TestHelper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.adapter.spring.auth.domain.AuthorityImpl | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.adapter.spring.workflow.domain.QCase | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.auth.service.UserService | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.elastic.domain.ElasticQueryConstants | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.elastic.service.interfaces.IElasticQueryFactory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.objects.auth.domain.AbstractUser | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.objects.auth.domain.ActorTransformer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.objects.auth.domain.Authority | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.objects.auth.domain.User | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.objects.auth.domain.enums.UserState | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.objects.petrinet.domain.PetriNet | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.objects.petrinet.domain.VersionType | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.netgrif.application.engine.startup.ImportHelper; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.junit.jupiter.api.BeforeEach | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.junit.jupiter.api.Test; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.junit.jupiter.api.extension.ExtendWith; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.boot.test.context.SpringBootTest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.security.core.Authentication | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.security.core.context.SecurityContextHolder; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.test.context.ActiveProfiles; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.test.context.junit.jupiter.SpringExtension | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.test.web.servlet.MockMvc | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.test.web.servlet.setup.MockMvcBuilders | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.context.WebApplicationContext | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.sql.Timestamp | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.Duration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.LocalDate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.LocalDateTime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @ExtendWith(SpringExtension.class) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @ActiveProfiles(["test"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @SpringBootTest( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| classes = ApplicationEngine.class | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @AutoConfigureMockMvc | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class ElasticQueryFactoryTest { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static final String USER_EMAIL = "test@mail.sk" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static final String USER_PASSWORD = "password" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Autowired | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private TestHelper testHelper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Autowired | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| protected IElasticQueryFactory queryFactory; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Autowired | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private UserService userService | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Autowired | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private ImportHelper importHelper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Autowired | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private WebApplicationContext wac | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private MockMvc mvc | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Map<String, Authority> auths | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @BeforeEach | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| void before() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| testHelper.truncateDbs() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mvc = MockMvcBuilders | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .webAppContextSetup(wac) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .apply(springSecurity()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .build() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| importHelper.createUser(new User(firstName: "Test", lastName: "Integration", email: USER_EMAIL, password: USER_PASSWORD, state: UserState.ACTIVE), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [auths.get("user"), auths.get("admin")] as Authority[], [] as ProcessRole[]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| void dynamicQueryTest() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AbstractUser realUser = userService.findByEmail(USER_EMAIL, null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(ActorTransformer.toLoggedUser(realUser), ActorTransformer.toLoggedUser(realUser).getPassword(), ActorTransformer.toLoggedUser(realUser).getAuthoritySet() as Set<AuthorityImpl>); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SecurityContextHolder.getContext().setAuthentication(token) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def user = userService.getLoggedUser() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def context = new HashMap<>(Map.of( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ElasticQueryConstants.DYNAMIC_USER_ID_TEMPLATE, user.getId().toString(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ElasticQueryConstants.LOCAL_DATE_NOW_TEMPLATE, LocalDateTime.now(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ElasticQueryConstants.LOCAL_DATE_TODAY_TEMPLATE, LocalDate.now(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ElasticQueryConstants.LOGGED_USER_TEMPLATE, user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String query1 = '${String.valueOf(java.sql.Timestamp.valueOf(now.plus(java.time.Duration.parse("P1D"))).getTime())}' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String resultQuery1 = String.valueOf(Timestamp.valueOf(LocalDateTime.now() + Duration.parse("P1D")).getTime()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert queryFactory.populateQuery(query1, context) <= resultQuery1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String query2 = 'creationDate:${today.toString()}'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String res2 = LocalDate.now().toString() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String resultQuery2 = "creationDate:" + res2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert queryFactory.populateQuery(query2, context) == resultQuery2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String query3 = 'author:${loggedUser.id}' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String resultQuery3 = "author:" + user.id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert queryFactory.populateQuery(query3, context) == resultQuery3; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String query4 = 'author:${me}'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String resultQuery4 = "author:" + user.id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert queryFactory.populateQuery(query4, context) == resultQuery4; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+101
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests assume executing code in templates; compare numbers as strings If we switch to safe placeholders, these tests must change. Also, compare numeric values as numbers, not strings. Apply these minimal fixes now: If you must keep arithmetic, I can add explicit helper functions (e.g., ${nowPlusDays(1)}) and update tests accordingly. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider a reserved prefix to avoid collisions
Using very generic variable names increases accidental collisions. Consider names like "__ctx_me", "__ctx_now", "__ctx_today", "__ctx_userId".
🤖 Prompt for AI Agents