123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173 |
- /*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- package org.sonar.server.issue.index;
-
- import com.google.common.base.Preconditions;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Date;
- import java.util.List;
- import java.util.Map;
- import java.util.Objects;
- import java.util.Optional;
- import java.util.OptionalInt;
- import java.util.OptionalLong;
- import java.util.Set;
- import java.util.function.Consumer;
- import java.util.stream.Collectors;
- import java.util.stream.IntStream;
- import java.util.stream.Stream;
- import javax.annotation.CheckForNull;
- import javax.annotation.Nullable;
- import org.apache.commons.lang.StringUtils;
- import org.elasticsearch.action.search.SearchRequest;
- import org.elasticsearch.action.search.SearchResponse;
- import org.elasticsearch.index.query.BoolQueryBuilder;
- import org.elasticsearch.index.query.QueryBuilder;
- import org.elasticsearch.index.query.QueryBuilders;
- import org.elasticsearch.indices.TermsLookup;
- import org.elasticsearch.search.aggregations.AggregationBuilder;
- import org.elasticsearch.search.aggregations.AggregationBuilders;
- import org.elasticsearch.search.aggregations.BucketOrder;
- import org.elasticsearch.search.aggregations.HasAggregations;
- import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
- import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
- import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter;
- import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
- import org.elasticsearch.search.aggregations.bucket.histogram.LongBounds;
- import org.elasticsearch.search.aggregations.bucket.terms.IncludeExclude;
- import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
- import org.elasticsearch.search.aggregations.bucket.terms.Terms;
- import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
- import org.elasticsearch.search.aggregations.metrics.Min;
- import org.elasticsearch.search.aggregations.metrics.ParsedMax;
- import org.elasticsearch.search.aggregations.metrics.ParsedValueCount;
- import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
- import org.elasticsearch.search.builder.SearchSourceBuilder;
- import org.elasticsearch.search.sort.FieldSortBuilder;
- import org.joda.time.Duration;
- import org.sonar.api.issue.Issue;
- import org.sonar.api.rule.Severity;
- import org.sonar.api.rules.RuleType;
- import org.sonar.api.utils.DateUtils;
- import org.sonar.api.utils.System2;
- import org.sonar.core.util.stream.MoreCollectors;
- import org.sonar.server.es.BaseDoc;
- import org.sonar.server.es.EsClient;
- import org.sonar.server.es.EsUtils;
- import org.sonar.server.es.IndexType;
- import org.sonar.server.es.SearchOptions;
- import org.sonar.server.es.Sorting;
- import org.sonar.server.es.searchrequest.RequestFiltersComputer;
- import org.sonar.server.es.searchrequest.RequestFiltersComputer.AllFilters;
- import org.sonar.server.es.searchrequest.SimpleFieldTopAggregationDefinition;
- import org.sonar.server.es.searchrequest.SubAggregationHelper;
- import org.sonar.server.es.searchrequest.TopAggregationDefinition;
- import org.sonar.server.es.searchrequest.TopAggregationDefinition.SimpleFieldFilterScope;
- import org.sonar.server.es.searchrequest.TopAggregationHelper;
- import org.sonar.server.issue.index.IssueQuery.PeriodStart;
- import org.sonar.server.permission.index.AuthorizationDoc;
- import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
- import org.sonar.server.security.SecurityStandards;
- import org.sonar.server.security.SecurityStandards.SQCategory;
- import org.sonar.server.user.UserSession;
- import org.sonar.server.view.index.ViewIndexDefinition;
-
- import static com.google.common.base.Preconditions.checkState;
- import static java.lang.String.format;
- import static java.util.Collections.singletonList;
- import static java.util.stream.Collectors.toList;
- import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
- import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
- import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
- import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
- import static org.elasticsearch.index.query.QueryBuilders.termQuery;
- import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
- import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
- import static org.sonar.api.rules.RuleType.VULNERABILITY;
- import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
- import static org.sonar.server.es.BaseDoc.epochMillisToEpochSeconds;
- import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
- import static org.sonar.server.es.IndexType.FIELD_INDEX_TYPE;
- import static org.sonar.server.es.searchrequest.TopAggregationDefinition.NON_STICKY;
- import static org.sonar.server.es.searchrequest.TopAggregationDefinition.STICKY;
- import static org.sonar.server.es.searchrequest.TopAggregationHelper.NO_EXTRA_FILTER;
- import static org.sonar.server.es.searchrequest.TopAggregationHelper.NO_OTHER_SUBAGGREGATION;
- import static org.sonar.server.issue.index.IssueIndex.Facet.ASSIGNED_TO_ME;
- import static org.sonar.server.issue.index.IssueIndex.Facet.ASSIGNEES;
- import static org.sonar.server.issue.index.IssueIndex.Facet.AUTHOR;
- import static org.sonar.server.issue.index.IssueIndex.Facet.CREATED_AT;
- import static org.sonar.server.issue.index.IssueIndex.Facet.CWE;
- import static org.sonar.server.issue.index.IssueIndex.Facet.DIRECTORIES;
- import static org.sonar.server.issue.index.IssueIndex.Facet.FILES;
- import static org.sonar.server.issue.index.IssueIndex.Facet.LANGUAGES;
- import static org.sonar.server.issue.index.IssueIndex.Facet.MODULE_UUIDS;
- import static org.sonar.server.issue.index.IssueIndex.Facet.OWASP_TOP_10;
- import static org.sonar.server.issue.index.IssueIndex.Facet.PROJECT_UUIDS;
- import static org.sonar.server.issue.index.IssueIndex.Facet.RESOLUTIONS;
- import static org.sonar.server.issue.index.IssueIndex.Facet.RULES;
- import static org.sonar.server.issue.index.IssueIndex.Facet.SANS_TOP_25;
- import static org.sonar.server.issue.index.IssueIndex.Facet.SCOPES;
- import static org.sonar.server.issue.index.IssueIndex.Facet.SEVERITIES;
- import static org.sonar.server.issue.index.IssueIndex.Facet.SONARSOURCE_SECURITY;
- import static org.sonar.server.issue.index.IssueIndex.Facet.STATUSES;
- import static org.sonar.server.issue.index.IssueIndex.Facet.TAGS;
- import static org.sonar.server.issue.index.IssueIndex.Facet.TYPES;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_CWE;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_EFFORT;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_FILE_PATH;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_FUNC_CLOSED_AT;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_FUNC_UPDATED_AT;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IS_MAIN_BRANCH;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_KEY;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_LANGUAGE;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_LINE;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_RESOLUTION;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_RULE_UUID;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SANS_TOP_25;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SCOPE;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SQ_SECURITY_CATEGORY;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_STATUS;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_TAGS;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_TYPE;
- import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_VULNERABILITY_PROBABILITY;
- import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE;
- import static org.sonar.server.security.SecurityReviewRating.computePercent;
- import static org.sonar.server.security.SecurityReviewRating.computeRating;
- import static org.sonar.server.security.SecurityStandards.CWES_BY_CWE_TOP_25;
- import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_INSECURE_INTERACTION;
- import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES;
- import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_RISKY_RESOURCE;
- import static org.sonar.server.view.index.ViewIndexDefinition.TYPE_VIEW;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_AUTHOR;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AT;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CWE;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_DIRECTORIES;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FILES;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_LANGUAGES;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLUTIONS;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SCOPES;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SEVERITIES;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SONARSOURCE_SECURITY;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TAGS;
- import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TYPES;
-
- /**
- * The unique entry-point to interact with Elasticsearch index "issues".
- * All the requests are listed here.
- */
- public class IssueIndex {
-
- public static final String FACET_PROJECTS = "projects";
- public static final String FACET_ASSIGNED_TO_ME = "assigned_to_me";
- public static final String FACET_MODULES = "moduleUuids";
-
- private static final int DEFAULT_FACET_SIZE = 15;
- private static final int MAX_FACET_SIZE = 100;
- private static final String AGG_VULNERABILITIES = "vulnerabilities";
- private static final String AGG_SEVERITIES = "severities";
- private static final String AGG_TO_REVIEW_SECURITY_HOTSPOTS = "toReviewSecurityHotspots";
- private static final String AGG_IN_REVIEW_SECURITY_HOTSPOTS = "inReviewSecurityHotspots";
- private static final String AGG_REVIEWED_SECURITY_HOTSPOTS = "reviewedSecurityHotspots";
- private static final String AGG_CWES = "cwes";
- private static final BoolQueryBuilder NON_RESOLVED_VULNERABILITIES_FILTER = boolQuery()
- .filter(termQuery(FIELD_ISSUE_TYPE, VULNERABILITY.name()))
- .mustNot(existsQuery(FIELD_ISSUE_RESOLUTION));
- private static final BoolQueryBuilder IN_REVIEW_HOTSPOTS_FILTER = boolQuery()
- .filter(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()))
- .filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_IN_REVIEW))
- .mustNot(existsQuery(FIELD_ISSUE_RESOLUTION));
- private static final BoolQueryBuilder TO_REVIEW_HOTSPOTS_FILTER = boolQuery()
- .filter(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()))
- .filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_TO_REVIEW))
- .mustNot(existsQuery(FIELD_ISSUE_RESOLUTION));
- private static final BoolQueryBuilder REVIEWED_HOTSPOTS_FILTER = boolQuery()
- .filter(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()))
- .filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_REVIEWED))
- .filter(termQuery(FIELD_ISSUE_RESOLUTION, Issue.RESOLUTION_FIXED));
-
- private static final Object[] NO_SELECTED_VALUES = {0};
- private static final SimpleFieldTopAggregationDefinition EFFORT_TOP_AGGREGATION = new SimpleFieldTopAggregationDefinition(FIELD_ISSUE_EFFORT, NON_STICKY);
-
- public enum Facet {
- SEVERITIES(PARAM_SEVERITIES, FIELD_ISSUE_SEVERITY, STICKY, Severity.ALL.size()),
- STATUSES(PARAM_STATUSES, FIELD_ISSUE_STATUS, STICKY, Issue.STATUSES.size()),
- // Resolutions facet returns one more element than the number of resolutions to take into account unresolved issues
- RESOLUTIONS(PARAM_RESOLUTIONS, FIELD_ISSUE_RESOLUTION, STICKY, Issue.RESOLUTIONS.size() + 1),
- TYPES(PARAM_TYPES, FIELD_ISSUE_TYPE, STICKY, RuleType.values().length),
- SCOPES(PARAM_SCOPES, FIELD_ISSUE_SCOPE, STICKY, MAX_FACET_SIZE),
- LANGUAGES(PARAM_LANGUAGES, FIELD_ISSUE_LANGUAGE, STICKY, MAX_FACET_SIZE),
- RULES(PARAM_RULES, FIELD_ISSUE_RULE_UUID, STICKY, MAX_FACET_SIZE),
- TAGS(PARAM_TAGS, FIELD_ISSUE_TAGS, STICKY, MAX_FACET_SIZE),
- AUTHOR(PARAM_AUTHOR, FIELD_ISSUE_AUTHOR_LOGIN, STICKY, MAX_FACET_SIZE),
- PROJECT_UUIDS(FACET_PROJECTS, FIELD_ISSUE_PROJECT_UUID, STICKY, MAX_FACET_SIZE),
- MODULE_UUIDS(FACET_MODULES, FIELD_ISSUE_MODULE_UUID, STICKY, MAX_FACET_SIZE),
- FILES(PARAM_FILES, FIELD_ISSUE_FILE_PATH, STICKY, MAX_FACET_SIZE),
- DIRECTORIES(PARAM_DIRECTORIES, FIELD_ISSUE_DIRECTORY_PATH, STICKY, MAX_FACET_SIZE),
- ASSIGNEES(PARAM_ASSIGNEES, FIELD_ISSUE_ASSIGNEE_UUID, STICKY, MAX_FACET_SIZE),
- ASSIGNED_TO_ME(FACET_ASSIGNED_TO_ME, FIELD_ISSUE_ASSIGNEE_UUID, STICKY, 1),
- OWASP_TOP_10(PARAM_OWASP_TOP_10, FIELD_ISSUE_OWASP_TOP_10, STICKY, DEFAULT_FACET_SIZE),
- SANS_TOP_25(PARAM_SANS_TOP_25, FIELD_ISSUE_SANS_TOP_25, STICKY, DEFAULT_FACET_SIZE),
- CWE(PARAM_CWE, FIELD_ISSUE_CWE, STICKY, DEFAULT_FACET_SIZE),
- CREATED_AT(PARAM_CREATED_AT, FIELD_ISSUE_FUNC_CREATED_AT, NON_STICKY),
- SONARSOURCE_SECURITY(PARAM_SONARSOURCE_SECURITY, FIELD_ISSUE_SQ_SECURITY_CATEGORY, STICKY, DEFAULT_FACET_SIZE);
-
- private final String name;
- private final SimpleFieldTopAggregationDefinition topAggregation;
- private final Integer numberOfTerms;
-
- Facet(String name, String fieldName, boolean sticky, int numberOfTerms) {
- this.name = name;
- this.topAggregation = new SimpleFieldTopAggregationDefinition(fieldName, sticky);
- this.numberOfTerms = numberOfTerms;
- }
-
- Facet(String name, String fieldName, boolean sticky) {
- this.name = name;
- this.topAggregation = new SimpleFieldTopAggregationDefinition(fieldName, sticky);
- this.numberOfTerms = null;
- }
-
- public String getName() {
- return name;
- }
-
- public String getFieldName() {
- return topAggregation.getFilterScope().getFieldName();
- }
-
- public TopAggregationDefinition.FilterScope getFilterScope() {
- return topAggregation.getFilterScope();
- }
-
- public SimpleFieldTopAggregationDefinition getTopAggregationDef() {
- return topAggregation;
- }
-
- public int getNumberOfTerms() {
- checkState(numberOfTerms != null, "numberOfTerms should have been provided in constructor");
-
- return numberOfTerms;
- }
- }
-
- private static final Map<String, Facet> FACETS_BY_NAME = Arrays.stream(Facet.values())
- .collect(uniqueIndex(Facet::getName));
-
- private static final String SUBSTRING_MATCH_REGEXP = ".*%s.*";
- // TODO to be documented
- // TODO move to Facets ?
- private static final String FACET_SUFFIX_MISSING = "_missing";
- private static final String IS_ASSIGNED_FILTER = "__isAssigned";
- private static final SumAggregationBuilder EFFORT_AGGREGATION = AggregationBuilders.sum(FACET_MODE_EFFORT).field(FIELD_ISSUE_EFFORT);
- private static final BucketOrder EFFORT_AGGREGATION_ORDER = BucketOrder.aggregation(FACET_MODE_EFFORT, false);
- private static final Duration TWENTY_DAYS = Duration.standardDays(20L);
- private static final Duration TWENTY_WEEKS = Duration.standardDays(20L * 7L);
- private static final Duration TWENTY_MONTHS = Duration.standardDays(20L * 30L);
- private static final String AGG_COUNT = "count";
- private final Sorting sorting;
- private final EsClient client;
- private final System2 system;
- private final UserSession userSession;
- private final WebAuthorizationTypeSupport authorizationTypeSupport;
-
- public IssueIndex(EsClient client, System2 system, UserSession userSession, WebAuthorizationTypeSupport authorizationTypeSupport) {
- this.client = client;
- this.system = system;
- this.userSession = userSession;
- this.authorizationTypeSupport = authorizationTypeSupport;
-
- this.sorting = new Sorting();
- this.sorting.add(IssueQuery.SORT_BY_STATUS, FIELD_ISSUE_STATUS);
- this.sorting.add(IssueQuery.SORT_BY_STATUS, FIELD_ISSUE_KEY);
- this.sorting.add(IssueQuery.SORT_BY_SEVERITY, FIELD_ISSUE_SEVERITY_VALUE);
- this.sorting.add(IssueQuery.SORT_BY_SEVERITY, FIELD_ISSUE_KEY);
- this.sorting.add(IssueQuery.SORT_BY_CREATION_DATE, FIELD_ISSUE_FUNC_CREATED_AT);
- this.sorting.add(IssueQuery.SORT_BY_CREATION_DATE, FIELD_ISSUE_KEY);
- this.sorting.add(IssueQuery.SORT_BY_UPDATE_DATE, FIELD_ISSUE_FUNC_UPDATED_AT);
- this.sorting.add(IssueQuery.SORT_BY_UPDATE_DATE, FIELD_ISSUE_KEY);
- this.sorting.add(IssueQuery.SORT_BY_CLOSE_DATE, FIELD_ISSUE_FUNC_CLOSED_AT);
- this.sorting.add(IssueQuery.SORT_BY_CLOSE_DATE, FIELD_ISSUE_KEY);
- this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, FIELD_ISSUE_PROJECT_UUID);
- this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, FIELD_ISSUE_FILE_PATH);
- this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, FIELD_ISSUE_LINE);
- this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, FIELD_ISSUE_SEVERITY_VALUE).reverse();
- this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, FIELD_ISSUE_KEY);
- this.sorting.add(IssueQuery.SORT_HOTSPOTS, FIELD_ISSUE_VULNERABILITY_PROBABILITY).reverse();
- this.sorting.add(IssueQuery.SORT_HOTSPOTS, FIELD_ISSUE_SQ_SECURITY_CATEGORY);
- this.sorting.add(IssueQuery.SORT_HOTSPOTS, FIELD_ISSUE_RULE_UUID);
- this.sorting.add(IssueQuery.SORT_HOTSPOTS, FIELD_ISSUE_PROJECT_UUID);
- this.sorting.add(IssueQuery.SORT_HOTSPOTS, FIELD_ISSUE_FILE_PATH);
- this.sorting.add(IssueQuery.SORT_HOTSPOTS, FIELD_ISSUE_LINE);
- this.sorting.add(IssueQuery.SORT_HOTSPOTS, FIELD_ISSUE_KEY);
-
- // by default order by created date, project, file, line and issue key (in order to be deterministic when same ms)
- this.sorting.addDefault(FIELD_ISSUE_FUNC_CREATED_AT).reverse();
- this.sorting.addDefault(FIELD_ISSUE_PROJECT_UUID);
- this.sorting.addDefault(FIELD_ISSUE_FILE_PATH);
- this.sorting.addDefault(FIELD_ISSUE_LINE);
- this.sorting.addDefault(FIELD_ISSUE_KEY);
- }
-
- public SearchResponse search(IssueQuery query, SearchOptions options) {
- SearchRequest requestBuilder = EsClient.prepareSearch(TYPE_ISSUE.getMainType());
- SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
- requestBuilder.source(sourceBuilder);
-
- configureSorting(query, sourceBuilder);
- configurePagination(options, sourceBuilder);
- configureRouting(query, options, requestBuilder);
-
- AllFilters allFilters = createAllFilters(query);
- RequestFiltersComputer filterComputer = newFilterComputer(options, allFilters);
-
- configureTopAggregations(query, options, sourceBuilder, allFilters, filterComputer);
- configureQuery(sourceBuilder, filterComputer);
- configureTopFilters(sourceBuilder, filterComputer);
-
- sourceBuilder.fetchSource(false)
- .trackTotalHits(true);
-
- return client.search(requestBuilder);
- }
-
- private void configureTopAggregations(IssueQuery query, SearchOptions options, SearchSourceBuilder esRequest, AllFilters allFilters, RequestFiltersComputer filterComputer) {
- TopAggregationHelper aggregationHelper = newAggregationHelper(filterComputer, query);
-
- configureTopAggregations(aggregationHelper, query, options, allFilters, esRequest);
- }
-
- private static void configureQuery(SearchSourceBuilder esRequest, RequestFiltersComputer filterComputer) {
- QueryBuilder esQuery = filterComputer.getQueryFilters()
- .map(t -> (QueryBuilder) boolQuery().must(matchAllQuery()).filter(t))
- .orElse(matchAllQuery());
- esRequest.query(esQuery);
- }
-
- private static void configureTopFilters(SearchSourceBuilder esRequest, RequestFiltersComputer filterComputer) {
- filterComputer.getPostFilters().ifPresent(esRequest::postFilter);
- }
-
- /**
- * Optimization - do not send ES request to all shards when scope is restricted
- * to a set of projects. Because project UUID is used for routing, the request
- * can be sent to only the shards containing the specified projects.
- * Note that sticky facets may involve all projects, so this optimization must be
- * disabled when facets are enabled.
- */
- private static void configureRouting(IssueQuery query, SearchOptions options, SearchRequest searchRequest) {
- Collection<String> uuids = query.projectUuids();
- if (!uuids.isEmpty() && options.getFacets().isEmpty()) {
- searchRequest.routing(uuids.stream().map(AuthorizationDoc::idOf).toArray(String[]::new));
- }
- }
-
- private static void configurePagination(SearchOptions options, SearchSourceBuilder esSearch) {
- esSearch.from(options.getOffset()).size(options.getLimit());
- }
-
- private AllFilters createAllFilters(IssueQuery query) {
- AllFilters filters = RequestFiltersComputer.newAllFilters();
- filters.addFilter("__indexType", new SimpleFieldFilterScope(FIELD_INDEX_TYPE), termQuery(FIELD_INDEX_TYPE, TYPE_ISSUE.getName()));
- filters.addFilter("__authorization", new SimpleFieldFilterScope("parent"), createAuthorizationFilter());
-
- // Issue is assigned Filter
- if (Boolean.TRUE.equals(query.assigned())) {
- filters.addFilter(IS_ASSIGNED_FILTER, Facet.ASSIGNEES.getFilterScope(), existsQuery(FIELD_ISSUE_ASSIGNEE_UUID));
- } else if (Boolean.FALSE.equals(query.assigned())) {
- filters.addFilter(IS_ASSIGNED_FILTER, ASSIGNEES.getFilterScope(), boolQuery().mustNot(existsQuery(FIELD_ISSUE_ASSIGNEE_UUID)));
- }
-
- // Issue is Resolved Filter
- if (Boolean.TRUE.equals(query.resolved())) {
- filters.addFilter("__isResolved", Facet.RESOLUTIONS.getFilterScope(), existsQuery(FIELD_ISSUE_RESOLUTION));
- } else if (Boolean.FALSE.equals(query.resolved())) {
- filters.addFilter("__isResolved", Facet.RESOLUTIONS.getFilterScope(), boolQuery().mustNot(existsQuery(FIELD_ISSUE_RESOLUTION)));
- }
-
- // Field Filters
- filters.addFilter(FIELD_ISSUE_KEY, new SimpleFieldFilterScope(FIELD_ISSUE_KEY), createTermsFilter(FIELD_ISSUE_KEY, query.issueKeys()));
- filters.addFilter(FIELD_ISSUE_ASSIGNEE_UUID, ASSIGNEES.getFilterScope(), createTermsFilter(FIELD_ISSUE_ASSIGNEE_UUID, query.assignees()));
- filters.addFilter(FIELD_ISSUE_SCOPE, SCOPES.getFilterScope(), createTermsFilter(FIELD_ISSUE_SCOPE, query.scopes()));
- filters.addFilter(FIELD_ISSUE_LANGUAGE, LANGUAGES.getFilterScope(), createTermsFilter(FIELD_ISSUE_LANGUAGE, query.languages()));
- filters.addFilter(FIELD_ISSUE_TAGS, TAGS.getFilterScope(), createTermsFilter(FIELD_ISSUE_TAGS, query.tags()));
- filters.addFilter(FIELD_ISSUE_TYPE, TYPES.getFilterScope(), createTermsFilter(FIELD_ISSUE_TYPE, query.types()));
- filters.addFilter(
- FIELD_ISSUE_RESOLUTION, RESOLUTIONS.getFilterScope(),
- createTermsFilter(FIELD_ISSUE_RESOLUTION, query.resolutions()));
- filters.addFilter(
- FIELD_ISSUE_AUTHOR_LOGIN, AUTHOR.getFilterScope(),
- createTermsFilter(FIELD_ISSUE_AUTHOR_LOGIN, query.authors()));
- filters.addFilter(
- FIELD_ISSUE_RULE_UUID, RULES.getFilterScope(), createTermsFilter(
- FIELD_ISSUE_RULE_UUID,
- query.ruleUuids()));
- filters.addFilter(FIELD_ISSUE_STATUS, STATUSES.getFilterScope(), createTermsFilter(FIELD_ISSUE_STATUS, query.statuses()));
-
- // security category
- addSecurityCategoryFilter(FIELD_ISSUE_OWASP_TOP_10, OWASP_TOP_10, query.owaspTop10(), filters);
- addSecurityCategoryFilter(FIELD_ISSUE_SANS_TOP_25, SANS_TOP_25, query.sansTop25(), filters);
- addSecurityCategoryFilter(FIELD_ISSUE_CWE, CWE, query.cwe(), filters);
- addSecurityCategoryFilter(FIELD_ISSUE_SQ_SECURITY_CATEGORY, SONARSOURCE_SECURITY, query.sonarsourceSecurity(), filters);
-
- addSeverityFilter(query, filters);
-
- addComponentRelatedFilters(query, filters);
- addDatesFilter(filters, query);
- addCreatedAfterByProjectsFilter(filters, query);
- return filters;
- }
-
- private static void addSecurityCategoryFilter(String fieldName, Facet facet, Collection<String> values, AllFilters allFilters) {
- QueryBuilder securityCategoryFilter = createTermsFilter(fieldName, values);
- if (securityCategoryFilter != null) {
- allFilters.addFilter(
- fieldName,
- facet.getFilterScope(),
- boolQuery()
- .must(securityCategoryFilter)
- .must(termsQuery(FIELD_ISSUE_TYPE, VULNERABILITY.name(), SECURITY_HOTSPOT.name())));
- }
- }
-
- private static void addSeverityFilter(IssueQuery query, AllFilters allFilters) {
- QueryBuilder severityFieldFilter = createTermsFilter(FIELD_ISSUE_SEVERITY, query.severities());
- if (severityFieldFilter != null) {
- allFilters.addFilter(
- FIELD_ISSUE_SEVERITY,
- SEVERITIES.getFilterScope(),
- boolQuery()
- .must(severityFieldFilter)
- // Ignore severity of Security HotSpots
- .mustNot(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name())));
- }
- }
-
- private static void addComponentRelatedFilters(IssueQuery query, AllFilters filters) {
- addCommonComponentRelatedFilters(query, filters);
- if (query.viewUuids().isEmpty()) {
- addBranchComponentRelatedFilters(query, filters);
- } else {
- addViewRelatedFilters(query, filters);
- }
- }
-
- private static void addCommonComponentRelatedFilters(IssueQuery query, AllFilters filters) {
- filters.addFilter(FIELD_ISSUE_COMPONENT_UUID, new SimpleFieldFilterScope(FIELD_ISSUE_COMPONENT_UUID),
- createTermsFilter(FIELD_ISSUE_COMPONENT_UUID, query.componentUuids()));
-
- if (!Boolean.TRUE.equals(query.onComponentOnly())) {
- filters.addFilter(
- FIELD_ISSUE_PROJECT_UUID, new SimpleFieldFilterScope(FIELD_ISSUE_PROJECT_UUID),
- createTermsFilter(FIELD_ISSUE_PROJECT_UUID, query.projectUuids()));
- filters.addFilter(
- "__module", new SimpleFieldFilterScope(FIELD_ISSUE_MODULE_PATH),
- createTermsFilter(FIELD_ISSUE_MODULE_PATH, query.moduleRootUuids()));
- filters.addFilter(
- FIELD_ISSUE_MODULE_UUID, new SimpleFieldFilterScope(FIELD_ISSUE_MODULE_UUID),
- createTermsFilter(FIELD_ISSUE_MODULE_UUID, query.moduleUuids()));
- filters.addFilter(
- FIELD_ISSUE_DIRECTORY_PATH, new SimpleFieldFilterScope(FIELD_ISSUE_DIRECTORY_PATH),
- createTermsFilter(FIELD_ISSUE_DIRECTORY_PATH, query.directories()));
- filters.addFilter(
- FIELD_ISSUE_FILE_PATH, new SimpleFieldFilterScope(FIELD_ISSUE_FILE_PATH),
- createTermsFilter(FIELD_ISSUE_FILE_PATH, query.files()));
- }
- }
-
- private static void addBranchComponentRelatedFilters(IssueQuery query, AllFilters allFilters) {
- if (Boolean.TRUE.equals(query.onComponentOnly())) {
- return;
- }
- allFilters.addFilter(
- "__is_main_branch", new SimpleFieldFilterScope(FIELD_ISSUE_IS_MAIN_BRANCH),
- createTermFilter(FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(query.isMainBranch())));
- allFilters.addFilter(
- FIELD_ISSUE_BRANCH_UUID, new SimpleFieldFilterScope(FIELD_ISSUE_BRANCH_UUID),
- createTermFilter(FIELD_ISSUE_BRANCH_UUID, query.branchUuid()));
- }
-
- private static void addViewRelatedFilters(IssueQuery query, AllFilters allFilters) {
- if (Boolean.TRUE.equals(query.onComponentOnly())) {
- return;
- }
- Collection<String> viewUuids = query.viewUuids();
- String branchUuid = query.branchUuid();
- boolean onApplicationBranch = branchUuid != null && !viewUuids.isEmpty();
- if (onApplicationBranch) {
- allFilters.addFilter("__view", new SimpleFieldFilterScope("view"), createViewFilter(singletonList(query.branchUuid())));
- } else {
- allFilters.addFilter("__is_main_branch", new SimpleFieldFilterScope(FIELD_ISSUE_IS_MAIN_BRANCH), createTermFilter(FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(true)));
- allFilters.addFilter("__view", new SimpleFieldFilterScope("view"), createViewFilter(viewUuids));
- }
- }
-
- @CheckForNull
- private static QueryBuilder createViewFilter(Collection<String> viewUuids) {
- if (viewUuids.isEmpty()) {
- return null;
- }
-
- BoolQueryBuilder viewsFilter = boolQuery();
- for (String viewUuid : viewUuids) {
- IndexType.IndexMainType mainType = TYPE_VIEW;
- viewsFilter.should(QueryBuilders.termsLookupQuery(FIELD_ISSUE_BRANCH_UUID,
- new TermsLookup(
- mainType.getIndex().getName(),
- mainType.getType(),
- viewUuid,
- ViewIndexDefinition.FIELD_PROJECTS)));
- }
- return viewsFilter;
- }
-
- private static RequestFiltersComputer newFilterComputer(SearchOptions options, AllFilters allFilters) {
- Collection<String> facetNames = options.getFacets();
- Set<TopAggregationDefinition<?>> facets = Stream.concat(
- Stream.of(EFFORT_TOP_AGGREGATION),
- facetNames.stream()
- .map(FACETS_BY_NAME::get)
- .filter(Objects::nonNull)
- .map(Facet::getTopAggregationDef))
- .collect(MoreCollectors.toSet(facetNames.size()));
-
- return new RequestFiltersComputer(allFilters, facets);
- }
-
- private static TopAggregationHelper newAggregationHelper(RequestFiltersComputer filterComputer, IssueQuery query) {
- if (hasQueryEffortFacet(query)) {
- return new TopAggregationHelper(filterComputer, new SubAggregationHelper(EFFORT_AGGREGATION, EFFORT_AGGREGATION_ORDER));
- }
- return new TopAggregationHelper(filterComputer, new SubAggregationHelper());
- }
-
- private static AggregationBuilder addEffortAggregationIfNeeded(IssueQuery query, AggregationBuilder aggregation) {
- if (hasQueryEffortFacet(query)) {
- aggregation.subAggregation(EFFORT_AGGREGATION);
- }
- return aggregation;
- }
-
- private static boolean hasQueryEffortFacet(IssueQuery query) {
- return FACET_MODE_EFFORT.equals(query.facetMode());
- }
-
- @CheckForNull
- private static QueryBuilder createTermsFilter(String field, Collection<?> values) {
- return values.isEmpty() ? null : termsQuery(field, values);
- }
-
- @CheckForNull
- private static QueryBuilder createTermFilter(String field, @Nullable String value) {
- return value == null ? null : termQuery(field, value);
- }
-
- private void configureSorting(IssueQuery query, SearchSourceBuilder esRequest) {
- createSortBuilders(query).forEach(esRequest::sort);
- }
-
- private List<FieldSortBuilder> createSortBuilders(IssueQuery query) {
- String sortField = query.sort();
- if (sortField != null) {
- boolean asc = Boolean.TRUE.equals(query.asc());
- return sorting.fill(sortField, asc);
- }
- return sorting.fillDefault();
- }
-
- private QueryBuilder createAuthorizationFilter() {
- return authorizationTypeSupport.createQueryFilter();
- }
-
- private void addDatesFilter(AllFilters filters, IssueQuery query) {
- PeriodStart createdAfter = query.createdAfter();
- Date createdBefore = query.createdBefore();
-
- validateCreationDateBounds(createdBefore, createdAfter != null ? createdAfter.date() : null);
-
- if (createdAfter != null) {
- filters.addFilter(
- "__createdAfter", CREATED_AT.getFilterScope(),
- QueryBuilders
- .rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT)
- .from(BaseDoc.dateToEpochSeconds(createdAfter.date()), createdAfter.inclusive()));
- }
- if (createdBefore != null) {
- filters.addFilter(
- "__createdBefore", CREATED_AT.getFilterScope(),
- QueryBuilders
- .rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT)
- .lt(BaseDoc.dateToEpochSeconds(createdBefore)));
- }
- Date createdAt = query.createdAt();
- if (createdAt != null) {
- filters.addFilter(
- "__createdAt", CREATED_AT.getFilterScope(),
- termQuery(FIELD_ISSUE_FUNC_CREATED_AT, BaseDoc.dateToEpochSeconds(createdAt)));
- }
- }
-
- private static void addCreatedAfterByProjectsFilter(AllFilters allFilters, IssueQuery query) {
- Map<String, PeriodStart> createdAfterByProjectUuids = query.createdAfterByProjectUuids();
- BoolQueryBuilder boolQueryBuilder = boolQuery();
- createdAfterByProjectUuids.forEach((projectOrProjectBranchUuid, createdAfterDate) -> boolQueryBuilder.should(boolQuery()
- .filter(termQuery(FIELD_ISSUE_BRANCH_UUID, projectOrProjectBranchUuid))
- .filter(rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT).from(BaseDoc.dateToEpochSeconds(createdAfterDate.date()), createdAfterDate.inclusive()))));
-
- allFilters.addFilter("__created_after_by_project_uuids", new SimpleFieldFilterScope("createdAfterByProjectUuids"), boolQueryBuilder);
- }
-
- private void validateCreationDateBounds(@Nullable Date createdBefore, @Nullable Date createdAfter) {
- Preconditions.checkArgument(createdAfter == null || createdAfter.compareTo(new Date(system.now())) <= 0,
- "Start bound cannot be in the future");
- Preconditions.checkArgument(createdAfter == null || createdBefore == null || createdAfter.before(createdBefore),
- "Start bound cannot be larger or equal to end bound");
- }
-
- private void configureTopAggregations(TopAggregationHelper aggregationHelper, IssueQuery query, SearchOptions options,
- AllFilters queryFilters, SearchSourceBuilder esRequest) {
- addFacetIfNeeded(options, aggregationHelper, esRequest, STATUSES, NO_SELECTED_VALUES);
- addFacetIfNeeded(options, aggregationHelper, esRequest, PROJECT_UUIDS, query.projectUuids().toArray());
- addFacetIfNeeded(options, aggregationHelper, esRequest, MODULE_UUIDS, query.moduleUuids().toArray());
- addFacetIfNeeded(options, aggregationHelper, esRequest, DIRECTORIES, query.directories().toArray());
- addFacetIfNeeded(options, aggregationHelper, esRequest, FILES, query.files().toArray());
- addFacetIfNeeded(options, aggregationHelper, esRequest, SCOPES, query.scopes().toArray());
- addFacetIfNeeded(options, aggregationHelper, esRequest, LANGUAGES, query.languages().toArray());
- addFacetIfNeeded(options, aggregationHelper, esRequest, RULES, query.ruleUuids().toArray());
- addFacetIfNeeded(options, aggregationHelper, esRequest, AUTHOR, query.authors().toArray());
- addFacetIfNeeded(options, aggregationHelper, esRequest, TAGS, query.tags().toArray());
- addFacetIfNeeded(options, aggregationHelper, esRequest, TYPES, query.types().toArray());
-
- addSecurityCategoryFacetIfNeeded(PARAM_OWASP_TOP_10, OWASP_TOP_10, options, aggregationHelper, esRequest, query.owaspTop10().toArray());
- addSecurityCategoryFacetIfNeeded(PARAM_SANS_TOP_25, SANS_TOP_25, options, aggregationHelper, esRequest, query.sansTop25().toArray());
- addSecurityCategoryFacetIfNeeded(PARAM_CWE, CWE, options, aggregationHelper, esRequest, query.cwe().toArray());
- addSecurityCategoryFacetIfNeeded(PARAM_SONARSOURCE_SECURITY, SONARSOURCE_SECURITY, options, aggregationHelper, esRequest, query.sonarsourceSecurity().toArray());
-
- addSeverityFacetIfNeeded(options, aggregationHelper, esRequest);
- addResolutionFacetIfNeeded(options, query, aggregationHelper, esRequest);
- addAssigneesFacetIfNeeded(options, query, aggregationHelper, esRequest);
- addCreatedAtFacetIfNeeded(options, query, aggregationHelper, queryFilters, esRequest);
- addAssignedToMeFacetIfNeeded(options, aggregationHelper, esRequest);
- addEffortTopAggregation(aggregationHelper, esRequest);
- }
-
- private static void addFacetIfNeeded(SearchOptions options, TopAggregationHelper aggregationHelper,
- SearchSourceBuilder esRequest, Facet facet, Object[] selectedValues) {
- if (!options.getFacets().contains(facet.getName())) {
- return;
- }
-
- FilterAggregationBuilder topAggregation = aggregationHelper.buildTermTopAggregation(
- facet.getName(), facet.getTopAggregationDef(), facet.getNumberOfTerms(),
- NO_EXTRA_FILTER,
- t -> aggregationHelper.getSubAggregationHelper().buildSelectedItemsAggregation(facet.getName(), facet.getTopAggregationDef(), selectedValues)
- .ifPresent(t::subAggregation));
- esRequest.aggregation(topAggregation);
- }
-
- private static void addSecurityCategoryFacetIfNeeded(String param, Facet facet, SearchOptions options, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest,
- Object[] selectedValues) {
- if (!options.getFacets().contains(param)) {
- return;
- }
-
- AggregationBuilder aggregation = aggregationHelper.buildTermTopAggregation(
- facet.getName(), facet.getTopAggregationDef(), facet.getNumberOfTerms(),
- filter -> filter.must(termQuery(FIELD_ISSUE_TYPE, VULNERABILITY.name())),
- t -> aggregationHelper.getSubAggregationHelper().buildSelectedItemsAggregation(facet.getName(), facet.getTopAggregationDef(), selectedValues)
- .ifPresent(t::subAggregation));
- esRequest.aggregation(aggregation);
- }
-
- private static void addSeverityFacetIfNeeded(SearchOptions options, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) {
- if (!options.getFacets().contains(PARAM_SEVERITIES)) {
- return;
- }
-
- AggregationBuilder aggregation = aggregationHelper.buildTermTopAggregation(
- SEVERITIES.getName(), SEVERITIES.getTopAggregationDef(), SEVERITIES.getNumberOfTerms(),
- // Ignore severity of Security HotSpots
- filter -> filter.mustNot(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name())),
- NO_OTHER_SUBAGGREGATION);
- esRequest.aggregation(aggregation);
- }
-
- private static void addResolutionFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) {
- if (!options.getFacets().contains(PARAM_RESOLUTIONS)) {
- return;
- }
-
- AggregationBuilder aggregation = aggregationHelper.buildTermTopAggregation(
- RESOLUTIONS.getName(), RESOLUTIONS.getTopAggregationDef(), RESOLUTIONS.getNumberOfTerms(),
- NO_EXTRA_FILTER,
- t -> {
- // add aggregation of type "missing" to return count of unresolved issues in the facet
- t.subAggregation(
- addEffortAggregationIfNeeded(query, AggregationBuilders
- .missing(RESOLUTIONS.getName() + FACET_SUFFIX_MISSING)
- .field(RESOLUTIONS.getFieldName())));
- });
- esRequest.aggregation(aggregation);
- }
-
- private static void addAssigneesFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) {
- if (!options.getFacets().contains(PARAM_ASSIGNEES)) {
- return;
- }
-
- Consumer<FilterAggregationBuilder> assigneeAggregations = t -> {
- // optional second aggregation to return the issue count for selected assignees (if any)
- Object[] assignees = query.assignees().toArray();
- aggregationHelper.getSubAggregationHelper().buildSelectedItemsAggregation(ASSIGNEES.getName(), ASSIGNEES.getTopAggregationDef(), assignees)
- .ifPresent(t::subAggregation);
-
- // third aggregation to always return the count of unassigned in the assignee facet
- t.subAggregation(addEffortAggregationIfNeeded(query, AggregationBuilders
- .missing(ASSIGNEES.getName() + FACET_SUFFIX_MISSING)
- .field(ASSIGNEES.getFieldName())));
- };
-
- AggregationBuilder aggregation = aggregationHelper.buildTermTopAggregation(
- ASSIGNEES.getName(), ASSIGNEES.getTopAggregationDef(), ASSIGNEES.getNumberOfTerms(),
- NO_EXTRA_FILTER, assigneeAggregations);
- esRequest.aggregation(aggregation);
- }
-
- private void addCreatedAtFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, AllFilters allFilters,
- SearchSourceBuilder esRequest) {
- if (options.getFacets().contains(PARAM_CREATED_AT)) {
- getCreatedAtFacet(query, aggregationHelper, allFilters).ifPresent(esRequest::aggregation);
- }
- }
-
- private Optional<AggregationBuilder> getCreatedAtFacet(IssueQuery query, TopAggregationHelper aggregationHelper, AllFilters allFilters) {
- long startTime;
- boolean startInclusive;
- PeriodStart createdAfter = query.createdAfter();
- if (createdAfter == null) {
- OptionalLong minDate = getMinCreatedAt(allFilters);
- if (!minDate.isPresent()) {
- return Optional.empty();
- }
- startTime = minDate.getAsLong();
- startInclusive = true;
- } else {
- startTime = createdAfter.date().getTime();
- startInclusive = createdAfter.inclusive();
- }
- Date createdBefore = query.createdBefore();
- long endTime = createdBefore == null ? system.now() : createdBefore.getTime();
-
- Duration timeSpan = new Duration(startTime, endTime);
- DateHistogramInterval bucketSize = computeDateHistogramBucketSize(timeSpan);
-
- FilterAggregationBuilder topAggregation = aggregationHelper.buildTopAggregation(
- CREATED_AT.getName(),
- CREATED_AT.getTopAggregationDef(),
- NO_EXTRA_FILTER,
- t -> {
- AggregationBuilder dateHistogram = AggregationBuilders.dateHistogram(CREATED_AT.getName())
- .field(CREATED_AT.getFieldName())
- .dateHistogramInterval(bucketSize)
- .minDocCount(0L)
- .format(DateUtils.DATETIME_FORMAT)
- .timeZone(Optional.ofNullable(query.timeZone()).orElse(system.getDefaultTimeZone().toZoneId()))
- // ES dateHistogram bounds are inclusive while createdBefore parameter is exclusive
- .extendedBounds(new LongBounds(startInclusive ? startTime : (startTime + 1), endTime - 1L));
- addEffortAggregationIfNeeded(query, dateHistogram);
- t.subAggregation(dateHistogram);
- });
-
- return Optional.of(topAggregation);
- }
-
- private static DateHistogramInterval computeDateHistogramBucketSize(Duration timeSpan) {
- if (timeSpan.isShorterThan(TWENTY_DAYS)) {
- return DateHistogramInterval.DAY;
- }
- if (timeSpan.isShorterThan(TWENTY_WEEKS)) {
- return DateHistogramInterval.WEEK;
- }
- if (timeSpan.isShorterThan(TWENTY_MONTHS)) {
- return DateHistogramInterval.MONTH;
- }
- return DateHistogramInterval.YEAR;
- }
-
- private OptionalLong getMinCreatedAt(AllFilters filters) {
- String facetNameAndField = CREATED_AT.getFieldName();
-
- SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
- .size(0);
- BoolQueryBuilder esFilter = boolQuery();
- filters.stream().filter(Objects::nonNull).forEach(esFilter::must);
- if (esFilter.hasClauses()) {
- sourceBuilder.query(QueryBuilders.boolQuery().filter(esFilter));
- }
- sourceBuilder.aggregation(AggregationBuilders.min(facetNameAndField).field(facetNameAndField));
-
- SearchRequest request = EsClient.prepareSearch(TYPE_ISSUE.getMainType())
- .source(sourceBuilder);
-
- Min minValue = client.search(request).getAggregations().get(facetNameAndField);
- double actualValue = minValue.getValue();
- if (Double.isInfinite(actualValue)) {
- return OptionalLong.empty();
- }
- return OptionalLong.of((long) actualValue);
- }
-
- private void addAssignedToMeFacetIfNeeded(SearchOptions options, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) {
- String uuid = userSession.getUuid();
- if (options.getFacets().contains(ASSIGNED_TO_ME.getName()) && !StringUtils.isEmpty(uuid)) {
- AggregationBuilder aggregation = aggregationHelper.buildTermTopAggregation(
- ASSIGNED_TO_ME.getName(),
- ASSIGNED_TO_ME.getTopAggregationDef(),
- ASSIGNED_TO_ME.getNumberOfTerms(),
- NO_EXTRA_FILTER,
- t -> {
- // add sub-aggregation to return issue count for current user
- aggregationHelper.getSubAggregationHelper()
- .buildSelectedItemsAggregation(ASSIGNED_TO_ME.getName(), ASSIGNED_TO_ME.getTopAggregationDef(), new String[] {uuid})
- .ifPresent(t::subAggregation);
- });
- esRequest.aggregation(aggregation);
- }
- }
-
- private static void addEffortTopAggregation(TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) {
- AggregationBuilder topAggregation = aggregationHelper.buildTopAggregation(
- FACET_MODE_EFFORT,
- EFFORT_TOP_AGGREGATION,
- NO_EXTRA_FILTER,
- t -> t.subAggregation(EFFORT_AGGREGATION));
- esRequest.aggregation(topAggregation);
- }
-
- public List<String> searchTags(IssueQuery query, @Nullable String textQuery, int size) {
- Terms terms = listTermsMatching(FIELD_ISSUE_TAGS, query, textQuery, BucketOrder.key(true), size);
- return EsUtils.termsKeys(terms);
- }
-
- public Map<String, Long> countTags(IssueQuery query, int maxNumberOfTags) {
- Terms terms = listTermsMatching(FIELD_ISSUE_TAGS, query, null, BucketOrder.count(false), maxNumberOfTags);
- return EsUtils.termsToMap(terms);
- }
-
- public List<String> searchAuthors(IssueQuery query, @Nullable String textQuery, int maxNumberOfAuthors) {
- Terms terms = listTermsMatching(FIELD_ISSUE_AUTHOR_LOGIN, query, textQuery, BucketOrder.key(true), maxNumberOfAuthors);
- return EsUtils.termsKeys(terms);
- }
-
- private Terms listTermsMatching(String fieldName, IssueQuery query, @Nullable String textQuery, BucketOrder termsOrder, int size) {
- SearchRequest requestBuilder = EsClient.prepareSearch(TYPE_ISSUE.getMainType());
-
- SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
- // Avoids returning search hits
- .size(0);
- requestBuilder.source(sourceBuilder);
-
- sourceBuilder.query(boolQuery().must(QueryBuilders.matchAllQuery()).filter(createBoolFilter(query)));
-
- TermsAggregationBuilder aggreg = AggregationBuilders.terms("_ref")
- .field(fieldName)
- .size(size)
- .order(termsOrder)
- .minDocCount(1L);
- if (textQuery != null) {
- aggreg.includeExclude(new IncludeExclude(format(SUBSTRING_MATCH_REGEXP, escapeSpecialRegexChars(textQuery)), null));
- }
-
- sourceBuilder.aggregation(aggreg);
-
- SearchResponse searchResponse = client.search(requestBuilder);
- return searchResponse.getAggregations().get("_ref");
- }
-
- private BoolQueryBuilder createBoolFilter(IssueQuery query) {
- BoolQueryBuilder boolQuery = boolQuery();
- createAllFilters(query).stream()
- .filter(Objects::nonNull)
- .forEach(boolQuery::must);
- return boolQuery;
- }
-
- public List<ProjectStatistics> searchProjectStatistics(List<String> projectUuids, List<Long> froms, @Nullable String assigneeUuid) {
- checkState(projectUuids.size() == froms.size(),
- "Expected same size for projectUuids (had size %s) and froms (had size %s)", projectUuids.size(), froms.size());
- if (projectUuids.isEmpty()) {
- return Collections.emptyList();
- }
- SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
- .query(
- boolQuery()
- .mustNot(existsQuery(FIELD_ISSUE_RESOLUTION))
- .filter(termQuery(FIELD_ISSUE_ASSIGNEE_UUID, assigneeUuid))
- .mustNot(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name())))
- .size(0);
-
- IntStream.range(0, projectUuids.size()).forEach(i -> {
- String projectUuid = projectUuids.get(i);
- long from = froms.get(i);
- sourceBuilder
- .aggregation(AggregationBuilders
- .filter(projectUuid, boolQuery()
- .filter(termQuery(FIELD_ISSUE_PROJECT_UUID, projectUuid))
- .filter(rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT).gte(epochMillisToEpochSeconds(from))))
- .subAggregation(
- AggregationBuilders.terms("branchUuid").field(FIELD_ISSUE_BRANCH_UUID)
- .subAggregation(
- AggregationBuilders.count(AGG_COUNT).field(FIELD_ISSUE_KEY))
- .subAggregation(
- AggregationBuilders.max("maxFuncCreatedAt").field(FIELD_ISSUE_FUNC_CREATED_AT))));
- });
-
- SearchRequest requestBuilder = EsClient.prepareSearch(TYPE_ISSUE.getMainType());
-
- requestBuilder.source(sourceBuilder);
- SearchResponse response = client.search(requestBuilder);
- return response.getAggregations().asList().stream()
- .map(x -> (ParsedFilter) x)
- .flatMap(projectBucket -> ((ParsedStringTerms) projectBucket.getAggregations().get("branchUuid")).getBuckets().stream()
- .flatMap(branchBucket -> {
- long count = ((ParsedValueCount) branchBucket.getAggregations().get(AGG_COUNT)).getValue();
- if (count < 1L) {
- return Stream.empty();
- }
- long lastIssueDate = (long) ((ParsedMax) branchBucket.getAggregations().get("maxFuncCreatedAt")).getValue();
- return Stream.of(new ProjectStatistics(branchBucket.getKeyAsString(), count, lastIssueDate));
- }))
- .collect(MoreCollectors.toList(projectUuids.size()));
- }
-
- public List<PrStatistics> searchBranchStatistics(String projectUuid, List<String> branchUuids) {
- if (branchUuids.isEmpty()) {
- return Collections.emptyList();
- }
-
- SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
- .query(
- boolQuery()
- .must(termsQuery(FIELD_ISSUE_BRANCH_UUID, branchUuids))
- .mustNot(existsQuery(FIELD_ISSUE_RESOLUTION))
- .must(termQuery(FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(false))))
- .size(0)
- .aggregation(AggregationBuilders.terms("branchUuids")
- .field(FIELD_ISSUE_BRANCH_UUID)
- .size(branchUuids.size())
- .subAggregation(AggregationBuilders.terms("types")
- .field(FIELD_ISSUE_TYPE)));
-
- SearchRequest requestBuilder = EsClient.prepareSearch(TYPE_ISSUE.getMainType())
- .routing(AuthorizationDoc.idOf(projectUuid));
-
- requestBuilder.source(sourceBuilder);
- SearchResponse response = client.search(requestBuilder);
- return ((ParsedStringTerms) response.getAggregations().get("branchUuids")).getBuckets().stream()
- .map(bucket -> new PrStatistics(bucket.getKeyAsString(),
- ((ParsedStringTerms) bucket.getAggregations().get("types")).getBuckets()
- .stream()
- .collect(uniqueIndex(MultiBucketsAggregation.Bucket::getKeyAsString, MultiBucketsAggregation.Bucket::getDocCount))))
- .collect(MoreCollectors.toList(branchUuids.size()));
- }
-
- /**
- * @deprecated SansTop25 report is outdated and will be removed in future versions
- */
- @Deprecated
- public List<SecurityStandardCategoryStatistics> getSansTop25Report(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
- SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
- Stream.of(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)
- .forEach(sansCategory -> request.aggregation(newSecurityReportSubAggregations(
- AggregationBuilders.filter(sansCategory, boolQuery().filter(termQuery(FIELD_ISSUE_SANS_TOP_25, sansCategory))),
- includeCwe,
- SecurityStandards.CWES_BY_SANS_TOP_25.get(sansCategory))));
- return processSecurityReportSearchResults(request, includeCwe);
- }
-
- public List<SecurityStandardCategoryStatistics> getCweTop25Reports(String projectUuid, boolean isViewOrApp) {
- SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
- CWES_BY_CWE_TOP_25.keySet()
- .forEach(cweYear -> request.aggregation(
- newSecurityReportSubAggregations(
- AggregationBuilders.filter(cweYear, boolQuery().filter(existsQuery(FIELD_ISSUE_CWE))),
- true,
- CWES_BY_CWE_TOP_25.get(cweYear))));
- List<SecurityStandardCategoryStatistics> result = processSecurityReportSearchResults(request, true);
- for (SecurityStandardCategoryStatistics cweReport : result) {
- Set<String> foundRules = cweReport.getChildren().stream()
- .map(SecurityStandardCategoryStatistics::getCategory)
- .collect(Collectors.toSet());
- CWES_BY_CWE_TOP_25.get(cweReport.getCategory()).stream()
- .filter(rule -> !foundRules.contains(rule))
- .forEach(rule -> cweReport.getChildren().add(emptyCweStatistics(rule)));
- }
- return result;
- }
-
- private static SecurityStandardCategoryStatistics emptyCweStatistics(String rule) {
- return new SecurityStandardCategoryStatistics(rule, 0, OptionalInt.of(1), 0, 0, 1, null);
- }
-
- public List<SecurityStandardCategoryStatistics> getSonarSourceReport(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
- SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
- Arrays.stream(SQCategory.values())
- .forEach(sonarsourceCategory -> request.aggregation(
- newSecurityReportSubAggregations(
- AggregationBuilders.filter(sonarsourceCategory.getKey(), boolQuery().filter(termQuery(FIELD_ISSUE_SQ_SECURITY_CATEGORY, sonarsourceCategory.getKey()))),
- includeCwe,
- SecurityStandards.CWES_BY_SQ_CATEGORY.get(sonarsourceCategory))));
- return processSecurityReportSearchResults(request, includeCwe);
- }
-
- public List<SecurityStandardCategoryStatistics> getOwaspTop10Report(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
- SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
- IntStream.rangeClosed(1, 10).mapToObj(i -> "a" + i)
- .forEach(owaspCategory -> request.aggregation(
- newSecurityReportSubAggregations(
- AggregationBuilders.filter(owaspCategory, boolQuery().filter(termQuery(FIELD_ISSUE_OWASP_TOP_10, owaspCategory))),
- includeCwe,
- null)));
- return processSecurityReportSearchResults(request, includeCwe);
- }
-
- private List<SecurityStandardCategoryStatistics> processSecurityReportSearchResults(SearchSourceBuilder sourceBuilder, boolean includeCwe) {
- SearchRequest request = EsClient.prepareSearch(TYPE_ISSUE.getMainType())
- .source(sourceBuilder);
- SearchResponse response = client.search(request);
- return response.getAggregations().asList().stream()
- .map(c -> processSecurityReportIssueSearchResults((ParsedFilter) c, includeCwe))
- .collect(MoreCollectors.toList());
- }
-
- private static SecurityStandardCategoryStatistics processSecurityReportIssueSearchResults(ParsedFilter categoryBucket, boolean includeCwe) {
- List<SecurityStandardCategoryStatistics> children = new ArrayList<>();
- if (includeCwe) {
- Stream<? extends Terms.Bucket> stream = ((ParsedStringTerms) categoryBucket.getAggregations().get(AGG_CWES)).getBuckets().stream();
- children = stream.map(cweBucket -> processSecurityReportCategorySearchResults(cweBucket, cweBucket.getKeyAsString(), null)).collect(toList());
- }
-
- return processSecurityReportCategorySearchResults(categoryBucket, categoryBucket.getName(), children);
- }
-
- private static SecurityStandardCategoryStatistics processSecurityReportCategorySearchResults(HasAggregations categoryBucket, String categoryName,
- @Nullable List<SecurityStandardCategoryStatistics> children) {
- List<? extends Terms.Bucket> severityBuckets = ((ParsedStringTerms) ((ParsedFilter) categoryBucket.getAggregations().get(AGG_VULNERABILITIES)).getAggregations()
- .get(AGG_SEVERITIES)).getBuckets();
- long vulnerabilities = severityBuckets.stream().mapToLong(b -> ((ParsedValueCount) b.getAggregations().get(AGG_COUNT)).getValue()).sum();
- // Worst severity having at least one issue
- OptionalInt severityRating = severityBuckets.stream()
- .filter(b -> ((ParsedValueCount) b.getAggregations().get(AGG_COUNT)).getValue() != 0)
- .mapToInt(b -> Severity.ALL.indexOf(b.getKeyAsString()) + 1)
- .max();
-
- long toReviewSecurityHotspots = ((ParsedValueCount) ((ParsedFilter) categoryBucket.getAggregations().get(AGG_TO_REVIEW_SECURITY_HOTSPOTS)).getAggregations().get(AGG_COUNT))
- .getValue();
- long reviewedSecurityHotspots = ((ParsedValueCount) ((ParsedFilter) categoryBucket.getAggregations().get(AGG_REVIEWED_SECURITY_HOTSPOTS)).getAggregations().get(AGG_COUNT))
- .getValue();
-
- Optional<Double> percent = computePercent(toReviewSecurityHotspots, reviewedSecurityHotspots);
- Integer securityReviewRating = computeRating(percent.orElse(null)).getIndex();
-
- return new SecurityStandardCategoryStatistics(categoryName, vulnerabilities, severityRating, toReviewSecurityHotspots,
- reviewedSecurityHotspots, securityReviewRating, children);
- }
-
- private static AggregationBuilder newSecurityReportSubAggregations(AggregationBuilder categoriesAggs, boolean includeCwe, @Nullable Collection<String> cwesInCategory) {
- AggregationBuilder aggregationBuilder = addSecurityReportIssueCountAggregations(categoriesAggs);
- if (includeCwe) {
- final TermsAggregationBuilder cwesAgg = AggregationBuilders.terms(AGG_CWES)
- .field(FIELD_ISSUE_CWE)
- // 100 should be enough to display all CWEs. If not, the UI will be broken anyway
- .size(MAX_FACET_SIZE);
- if (cwesInCategory != null) {
- cwesAgg.includeExclude(new IncludeExclude(cwesInCategory.toArray(new String[0]), new String[0]));
- }
- categoriesAggs.subAggregation(addSecurityReportIssueCountAggregations(cwesAgg));
- }
- return aggregationBuilder;
- }
-
- private static AggregationBuilder addSecurityReportIssueCountAggregations(AggregationBuilder categoryAggs) {
- return categoryAggs
- .subAggregation(
- AggregationBuilders.filter(AGG_VULNERABILITIES, NON_RESOLVED_VULNERABILITIES_FILTER)
- .subAggregation(
- AggregationBuilders.terms(AGG_SEVERITIES).field(FIELD_ISSUE_SEVERITY)
- .subAggregation(
- AggregationBuilders.count(AGG_COUNT).field(FIELD_ISSUE_KEY))))
- .subAggregation(AggregationBuilders.filter(AGG_TO_REVIEW_SECURITY_HOTSPOTS, TO_REVIEW_HOTSPOTS_FILTER)
- .subAggregation(
- AggregationBuilders.count(AGG_COUNT).field(FIELD_ISSUE_KEY)))
- .subAggregation(AggregationBuilders.filter(AGG_IN_REVIEW_SECURITY_HOTSPOTS, IN_REVIEW_HOTSPOTS_FILTER)
- .subAggregation(
- AggregationBuilders.count(AGG_COUNT).field(FIELD_ISSUE_KEY)))
- .subAggregation(AggregationBuilders.filter(AGG_REVIEWED_SECURITY_HOTSPOTS, REVIEWED_HOTSPOTS_FILTER)
- .subAggregation(
- AggregationBuilders.count(AGG_COUNT).field(FIELD_ISSUE_KEY)));
- }
-
- private static SearchSourceBuilder prepareNonClosedVulnerabilitiesAndHotspotSearch(String projectUuid, boolean isViewOrApp) {
- BoolQueryBuilder componentFilter = boolQuery();
- if (isViewOrApp) {
- IndexType.IndexMainType mainType = TYPE_VIEW;
- componentFilter.filter(QueryBuilders.termsLookupQuery(FIELD_ISSUE_BRANCH_UUID,
- new TermsLookup(
- mainType.getIndex().getName(),
- mainType.getType(),
- projectUuid,
- ViewIndexDefinition.FIELD_PROJECTS)));
- } else {
- componentFilter.filter(termQuery(FIELD_ISSUE_BRANCH_UUID, projectUuid));
- }
-
- SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
- return sourceBuilder
- .query(
- componentFilter
- .should(NON_RESOLVED_VULNERABILITIES_FILTER)
- .should(TO_REVIEW_HOTSPOTS_FILTER)
- .should(IN_REVIEW_HOTSPOTS_FILTER)
- .should(REVIEWED_HOTSPOTS_FILTER)
- .minimumShouldMatch(1))
- .size(0);
- }
-
- }
|