@@ -74,6 +74,9 @@ public class IssueQuery { | |||
private final Collection<String> languages; | |||
private final Collection<String> tags; | |||
private final Collection<String> types; | |||
private final Collection<String> owaspTop10; | |||
private final Collection<String> sansTop25; | |||
private final Collection<String> cwe; | |||
private final Map<String, PeriodStart> createdAfterByProjectUuids; | |||
private final Boolean onComponentOnly; | |||
private final Boolean assigned; | |||
@@ -107,6 +110,9 @@ public class IssueQuery { | |||
this.languages = defaultCollection(builder.languages); | |||
this.tags = defaultCollection(builder.tags); | |||
this.types = defaultCollection(builder.types); | |||
this.owaspTop10 = defaultCollection(builder.owaspTop10); | |||
this.sansTop25 = defaultCollection(builder.sansTop25); | |||
this.cwe = defaultCollection(builder.cwe); | |||
this.createdAfterByProjectUuids = defaultMap(builder.createdAfterByProjectUuids); | |||
this.onComponentOnly = builder.onComponentOnly; | |||
this.assigned = builder.assigned; | |||
@@ -191,6 +197,18 @@ public class IssueQuery { | |||
return types; | |||
} | |||
public Collection<String> owaspTop10() { | |||
return owaspTop10; | |||
} | |||
public Collection<String> sansTop25() { | |||
return sansTop25; | |||
} | |||
public Collection<String> cwe() { | |||
return cwe; | |||
} | |||
public Map<String, PeriodStart> createdAfterByProjectUuids() { | |||
return createdAfterByProjectUuids; | |||
} | |||
@@ -284,6 +302,9 @@ public class IssueQuery { | |||
private Collection<String> languages; | |||
private Collection<String> tags; | |||
private Collection<String> types; | |||
private Collection<String> owaspTop10; | |||
private Collection<String> sansTop25; | |||
private Collection<String> cwe; | |||
private Map<String, PeriodStart> createdAfterByProjectUuids; | |||
private Boolean onComponentOnly = false; | |||
private Boolean assigned = null; | |||
@@ -388,6 +409,21 @@ public class IssueQuery { | |||
return this; | |||
} | |||
public Builder owaspTop10(@Nullable Collection<String> o) { | |||
this.owaspTop10 = o; | |||
return this; | |||
} | |||
public Builder sansTop25(@Nullable Collection<String> s) { | |||
this.sansTop25 = s; | |||
return this; | |||
} | |||
public Builder cwe(@Nullable Collection<String> cwe) { | |||
this.cwe = cwe; | |||
return this; | |||
} | |||
public Builder createdAfterByProjectUuids(@Nullable Map<String, PeriodStart> createdAfterByProjectUuids) { | |||
this.createdAfterByProjectUuids = createdAfterByProjectUuids; | |||
return this; |
@@ -64,6 +64,9 @@ public class SearchRequest { | |||
private List<String> statuses; | |||
private List<String> tags; | |||
private List<String> types; | |||
private List<String> owaspTop10; | |||
private List<String> sansTop25; | |||
private List<String> cwe; | |||
@CheckForNull | |||
public List<String> getActionPlans() { | |||
@@ -405,6 +408,36 @@ public class SearchRequest { | |||
return this; | |||
} | |||
@CheckForNull | |||
public List<String> getOwaspTop10() { | |||
return owaspTop10; | |||
} | |||
public SearchRequest setOwaspTop10(@Nullable List<String> owaspTop10) { | |||
this.owaspTop10 = owaspTop10; | |||
return this; | |||
} | |||
@CheckForNull | |||
public List<String> getSansTop25() { | |||
return sansTop25; | |||
} | |||
public SearchRequest setSansTop25(@Nullable List<String> sansTop25) { | |||
this.sansTop25 = sansTop25; | |||
return this; | |||
} | |||
@CheckForNull | |||
public List<String> getCwe() { | |||
return cwe; | |||
} | |||
public SearchRequest setCwe(@Nullable List<String> cwe) { | |||
this.cwe = cwe; | |||
return this; | |||
} | |||
@CheckForNull | |||
public List<String> getComponentRootUuids() { | |||
return componentRootUuids; |
@@ -290,4 +290,34 @@ public class IssueDoc extends BaseDoc { | |||
return this; | |||
} | |||
@CheckForNull | |||
public Collection<String> getOwaspTop10() { | |||
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10); | |||
} | |||
public IssueDoc setOwaspTop10(@Nullable Collection<String> o) { | |||
setField(IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10, o); | |||
return this; | |||
} | |||
@CheckForNull | |||
public Collection<String> getSansTop25() { | |||
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_SANS_TOP_25); | |||
} | |||
public IssueDoc setSansTop25(@Nullable Collection<String> s) { | |||
setField(IssueIndexDefinition.FIELD_ISSUE_SANS_TOP_25, s); | |||
return this; | |||
} | |||
@CheckForNull | |||
public Collection<String> getCwe() { | |||
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_CWE); | |||
} | |||
public IssueDoc setCwe(@Nullable Collection<String> c) { | |||
setField(IssueIndexDefinition.FIELD_ISSUE_CWE, c); | |||
return this; | |||
} | |||
} |
@@ -104,14 +104,17 @@ 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_AUTHORS; | |||
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_FILE_UUIDS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_LANGUAGES; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_MODULE_UUIDS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_UUIDS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_REPORTERS; | |||
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_SEVERITIES; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TAGS; | |||
@@ -140,6 +143,9 @@ public class IssueIndex { | |||
PARAM_LANGUAGES, | |||
PARAM_TAGS, | |||
PARAM_TYPES, | |||
PARAM_OWASP_TOP_10, | |||
PARAM_SANS_TOP_25, | |||
PARAM_CWE, | |||
PARAM_CREATED_AT); | |||
public static final String AGGREGATION_NAME_FOR_TAGS = "tags__issues"; | |||
private static final String SUBSTRING_MATCH_REGEXP = ".*%s.*"; | |||
@@ -522,6 +528,15 @@ public class IssueIndex { | |||
if (options.getFacets().contains(PARAM_TYPES)) { | |||
esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(IssueIndexDefinition.FIELD_ISSUE_TYPE, PARAM_TYPES, query.types().toArray())); | |||
} | |||
if (options.getFacets().contains(PARAM_OWASP_TOP_10)) { | |||
esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10, PARAM_OWASP_TOP_10, query.owaspTop10().toArray())); | |||
} | |||
if (options.getFacets().contains(PARAM_SANS_TOP_25)) { | |||
esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(IssueIndexDefinition.FIELD_ISSUE_SANS_TOP_25, PARAM_SANS_TOP_25, query.sansTop25().toArray())); | |||
} | |||
if (options.getFacets().contains(PARAM_CWE)) { | |||
esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(IssueIndexDefinition.FIELD_ISSUE_CWE, PARAM_CWE, query.cwe().toArray())); | |||
} | |||
if (options.getFacets().contains(PARAM_RESOLUTIONS)) { | |||
esSearch.addAggregation(createResolutionFacet(query, filters, esQuery)); | |||
} |
@@ -92,6 +92,9 @@ public class IssueIndexDefinition implements IndexDefinition { | |||
public static final String FIELD_ISSUE_STATUS = "status"; | |||
public static final String FIELD_ISSUE_TAGS = "tags"; | |||
public static final String FIELD_ISSUE_TYPE = "type"; | |||
public static final String FIELD_ISSUE_OWASP_TOP_10 = "owaspTop10"; | |||
public static final String FIELD_ISSUE_SANS_TOP_25 = "sansTop25"; | |||
public static final String FIELD_ISSUE_CWE = "cwe"; | |||
private final Configuration config; | |||
private final boolean enableSource; | |||
@@ -151,5 +154,8 @@ public class IssueIndexDefinition implements IndexDefinition { | |||
type.keywordFieldBuilder(FIELD_ISSUE_STATUS).disableNorms().addSubFields(SORTABLE_ANALYZER).build(); | |||
type.keywordFieldBuilder(FIELD_ISSUE_TAGS).disableNorms().build(); | |||
type.keywordFieldBuilder(FIELD_ISSUE_TYPE).disableNorms().build(); | |||
type.keywordFieldBuilder(FIELD_ISSUE_OWASP_TOP_10).disableNorms().build(); | |||
type.keywordFieldBuilder(FIELD_ISSUE_SANS_TOP_25).disableNorms().build(); | |||
type.keywordFieldBuilder(FIELD_ISSUE_CWE).disableNorms().build(); | |||
} | |||
} |
@@ -21,13 +21,18 @@ package org.sonar.server.issue.index; | |||
import com.google.common.base.CharMatcher; | |||
import com.google.common.base.Splitter; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.ImmutableMap; | |||
import com.google.common.collect.Iterators; | |||
import com.google.common.collect.Maps; | |||
import java.sql.PreparedStatement; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.IntStream; | |||
import javax.annotation.CheckForNull; | |||
@@ -41,8 +46,14 @@ import org.sonar.db.DbSession; | |||
import org.sonar.db.ResultSetIterator; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static java.util.Arrays.asList; | |||
import static java.util.stream.Collectors.toList; | |||
import static org.sonar.api.utils.DateUtils.longToDate; | |||
import static org.sonar.db.DatabaseUtils.getLong; | |||
import static org.sonar.server.issue.ws.SearchAction.SANS_TOP_25_INSECURE_INTERACTION; | |||
import static org.sonar.server.issue.ws.SearchAction.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.ws.SearchAction.SANS_TOP_25_RISKY_RESOURCE; | |||
import static org.sonar.server.issue.ws.SearchAction.UNKNOWN_STANDARD; | |||
/** | |||
* Scrolls over table ISSUES and reads documents to populate | |||
@@ -73,11 +84,12 @@ class IssueIteratorForSingleChunk implements IssueIterator { | |||
"c.scope", | |||
"c.organization_uuid", | |||
"c.project_uuid", | |||
"c.main_branch_project_uuid", | |||
// column 21 | |||
"c.main_branch_project_uuid", | |||
"i.tags", | |||
"i.issue_type" | |||
"i.issue_type", | |||
"r.security_standards" | |||
}; | |||
private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from issues i " + | |||
@@ -89,7 +101,19 @@ class IssueIteratorForSingleChunk implements IssueIterator { | |||
private static final String ISSUE_KEY_FILTER_SUFFIX = ")"; | |||
static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); | |||
static final Splitter SECURITY_STANDARDS_SPLITTER = TAGS_SPLITTER; | |||
static final Splitter MODULE_PATH_SPLITTER = Splitter.on('.').trimResults().omitEmptyStrings(); | |||
private static final String OWASP_TOP10_PREFIX = "owaspTop10:"; | |||
private static final String CWE_PREFIX = "cwe:"; | |||
// See https://www.sans.org/top25-software-errors | |||
private static final Set<String> INSECURE_CWE = new HashSet<>(asList("89", "78", "79", "434", "352", "601")); | |||
private static final Set<String> RISKY_CWE = new HashSet<>(asList("120", "22", "494", "829", "676", "131", "134", "190")); | |||
private static final Set<String> POROUS_CWE = new HashSet<>(asList("306", "862", "798", "311", "807", "250", "863", "732", "327", "307", "759")); | |||
private static final Map<String, Set<String>> SANS_TOP_25_CWE_MAPPING = ImmutableMap.of( | |||
SANS_TOP_25_INSECURE_INTERACTION, INSECURE_CWE, | |||
SANS_TOP_25_RISKY_RESOURCE, RISKY_CWE, | |||
SANS_TOP_25_POROUS_DEFENSES, POROUS_CWE); | |||
private final DbSession session; | |||
@@ -223,11 +247,27 @@ class IssueIteratorForSingleChunk implements IssueIterator { | |||
doc.setIsMainBranch(false); | |||
} | |||
String tags = rs.getString(21); | |||
doc.setTags(ImmutableList.copyOf(IssueIteratorForSingleChunk.TAGS_SPLITTER.split(tags == null ? "" : tags))); | |||
doc.setTags(IssueIteratorForSingleChunk.TAGS_SPLITTER.splitToList(tags == null ? "" : tags)); | |||
doc.setType(RuleType.valueOf(rs.getInt(22))); | |||
String securityStandards = rs.getString(23); | |||
List<String> standards = IssueIteratorForSingleChunk.SECURITY_STANDARDS_SPLITTER.splitToList(securityStandards == null ? "" : securityStandards); | |||
List<String> owaspTop10 = standards.stream().filter(s -> s.startsWith(OWASP_TOP10_PREFIX)).map(s -> s.substring(OWASP_TOP10_PREFIX.length())).collect(toList()); | |||
doc.setOwaspTop10(owaspTop10.isEmpty() ? Collections.singletonList(UNKNOWN_STANDARD) : owaspTop10); | |||
List<String> cwe = standards.stream().filter(s -> s.startsWith(CWE_PREFIX)).map(s -> s.substring(CWE_PREFIX.length())).collect(toList()); | |||
doc.setCwe(cwe.isEmpty() ? Collections.singletonList(UNKNOWN_STANDARD) : cwe); | |||
doc.setSansTop25(getSansTop25(cwe)); | |||
return doc; | |||
} | |||
private static List<String> getSansTop25(List<String> cwe) { | |||
return SANS_TOP_25_CWE_MAPPING | |||
.keySet() | |||
.stream() | |||
.filter(k -> cwe.stream().anyMatch(SANS_TOP_25_CWE_MAPPING.get(k)::contains)) | |||
.collect(toList()); | |||
} | |||
@CheckForNull | |||
private static String extractDirPath(@Nullable String filePath, String scope) { | |||
if (filePath != null) { |
@@ -52,6 +52,9 @@ public class IssueQueryTest { | |||
.languages(newArrayList("xoo")) | |||
.tags(newArrayList("tag1", "tag2")) | |||
.types(newArrayList("RELIABILITY", "SECURITY")) | |||
.owaspTop10(newArrayList("a1", "a2")) | |||
.sansTop25(newArrayList("insecure-interaction", "porous-defenses")) | |||
.cwe(newArrayList("12", "125")) | |||
.organizationUuid("orga") | |||
.branchUuid("my_branch") | |||
.createdAfterByProjectUuids(ImmutableMap.of("PROJECT", filterDate)) | |||
@@ -74,6 +77,9 @@ public class IssueQueryTest { | |||
assertThat(query.languages()).containsOnly("xoo"); | |||
assertThat(query.tags()).containsOnly("tag1", "tag2"); | |||
assertThat(query.types()).containsOnly("RELIABILITY", "SECURITY"); | |||
assertThat(query.owaspTop10()).containsOnly("a1", "a2"); | |||
assertThat(query.sansTop25()).containsOnly("insecure-interaction", "porous-defenses"); | |||
assertThat(query.cwe()).containsOnly("12", "125"); | |||
assertThat(query.organizationUuid()).isEqualTo("orga"); | |||
assertThat(query.branchUuid()).isEqualTo("my_branch"); | |||
assertThat(query.createdAfterByProjectUuids()).containsOnly(entry("PROJECT", filterDate)); | |||
@@ -128,6 +134,9 @@ public class IssueQueryTest { | |||
.languages(null) | |||
.tags(null) | |||
.types(null) | |||
.owaspTop10(null) | |||
.sansTop25(null) | |||
.cwe(null) | |||
.createdAfterByProjectUuids(null) | |||
.build(); | |||
assertThat(query.issueKeys()).isEmpty(); | |||
@@ -142,6 +151,9 @@ public class IssueQueryTest { | |||
assertThat(query.languages()).isEmpty(); | |||
assertThat(query.tags()).isEmpty(); | |||
assertThat(query.types()).isEmpty(); | |||
assertThat(query.owaspTop10()).isEmpty(); | |||
assertThat(query.sansTop25()).isEmpty(); | |||
assertThat(query.cwe()).isEmpty(); | |||
assertThat(query.createdAfterByProjectUuids()).isEmpty(); | |||
} | |||
@@ -22,6 +22,7 @@ package org.sonar.server.issue.index; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Date; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.function.Predicate; | |||
import java.util.stream.Collectors; | |||
@@ -56,6 +57,8 @@ import static org.junit.rules.ExpectedException.none; | |||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
import static org.sonar.server.issue.IssueDocTesting.newDoc; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.INDEX_TYPE_ISSUE; | |||
import static org.sonar.server.issue.ws.SearchAction.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.ws.SearchAction.UNKNOWN_STANDARD; | |||
import static org.sonar.server.permission.index.AuthorizationTypeSupport.TYPE_AUTHORIZATION; | |||
public class IssueIndexerTest { | |||
@@ -134,6 +137,25 @@ public class IssueIndexerTest { | |||
assertThat(doc.line()).isEqualTo(issue.getLine()); | |||
// functional date | |||
assertThat(doc.updateDate()).isEqualToIgnoringMillis(new Date(issue.getIssueUpdateTime())); | |||
assertThat(doc.getCwe()).containsExactlyInAnyOrder(UNKNOWN_STANDARD); | |||
assertThat(doc.getOwaspTop10()).containsExactlyInAnyOrder(UNKNOWN_STANDARD); | |||
assertThat(doc.getSansTop25()).isEmpty(); | |||
} | |||
@Test | |||
public void verify_security_standards_indexation() { | |||
RuleDefinitionDto rule = db.rules().insert(r -> r.setSecurityStandards(new HashSet<>(Arrays.asList("cwe:123,owaspTop10:a3,cwe:863")))); | |||
ComponentDto project = db.components().insertPrivateProject(organization); | |||
ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java/foo")); | |||
ComponentDto file = db.components().insertComponent(newFileDto(project, dir, "F1")); | |||
IssueDto issue = db.issues().insertIssue(IssueTesting.newIssue(rule, project, file)); | |||
underTest.indexOnStartup(emptySet()); | |||
IssueDoc doc = es.getDocuments(INDEX_TYPE_ISSUE, IssueDoc.class).get(0); | |||
assertThat(doc.getCwe()).containsExactlyInAnyOrder("123", "863"); | |||
assertThat(doc.getOwaspTop10()).containsExactlyInAnyOrder("a3"); | |||
assertThat(doc.getSansTop25()).containsExactlyInAnyOrder(SANS_TOP_25_POROUS_DEFENSES); | |||
} | |||
@Test |
@@ -103,6 +103,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AFT | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AT; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_BEFORE; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_IN_LAST; | |||
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_FILE_UUIDS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ISSUES; | |||
@@ -110,6 +111,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_LANGUAGES; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_MODULE_UUIDS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ON_COMPONENT_ONLY; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ORGANIZATION; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PLANNED; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECTS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_KEYS; | |||
@@ -119,6 +121,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_REPORTERS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLUTIONS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLVED; | |||
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_SEVERITIES; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_PERIOD; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES; | |||
@@ -128,6 +131,10 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TYPES; | |||
public class SearchAction implements IssuesWsAction { | |||
public static final String LOGIN_MYSELF = "__me__"; | |||
public static final String UNKNOWN_STANDARD = "unknown"; | |||
public static final String SANS_TOP_25_INSECURE_INTERACTION = "insecure-interaction"; | |||
public static final String SANS_TOP_25_RISKY_RESOURCE = "risky-resource"; | |||
public static final String SANS_TOP_25_POROUS_DEFENSES = "porous-defenses"; | |||
private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. "; | |||
private static final Set<String> IGNORED_FACETS = newHashSet(PARAM_PLANNED, DEPRECATED_PARAM_ACTION_PLANS, PARAM_REPORTERS); | |||
@@ -174,7 +181,8 @@ public class SearchAction implements IssuesWsAction { | |||
new Change("5.5", "response field 'debt' is renamed 'effort'"), | |||
new Change("7.2", "response field 'externalRuleEngine' added to issues that have been imported from an external rule engine"), | |||
new Change("7.2", format("value '%s' in parameter '%s' is deprecated, it won't have any effect", SORT_BY_ASSIGNEE, Param.SORT)), | |||
new Change("7.3", "response field 'fromHotspot' added to issues that are security hotspots")) | |||
new Change("7.3", "response field 'fromHotspot' added to issues that are security hotspots"), | |||
new Change("7.3", "added facets 'sansTop25', 'owaspTop10' and 'cwe'")) | |||
.setResponseExample(getClass().getResource("search-example.json")); | |||
action.addPagingParams(100, MAX_LIMIT); | |||
@@ -223,6 +231,17 @@ public class SearchAction implements IssuesWsAction { | |||
.setSince("5.5") | |||
.setPossibleValues((Object[]) RuleType.values()) | |||
.setExampleValue(format("%s,%s", RuleType.CODE_SMELL, RuleType.BUG)); | |||
action.createParam(PARAM_OWASP_TOP_10) | |||
.setDescription("Comma-separated list of OWASP Top 10 lowercase categories. Use '" + UNKNOWN_STANDARD + "' to select issues not associated to any OWASP Top 10 category.") | |||
.setSince("7.3") | |||
.setPossibleValues("a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", UNKNOWN_STANDARD); | |||
action.createParam(PARAM_SANS_TOP_25) | |||
.setDescription("Comma-separated list of SANS Top 25 categories.") | |||
.setSince("7.3") | |||
.setPossibleValues(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES); | |||
action.createParam(PARAM_CWE) | |||
.setDescription("Comma-separated list of CWE identifiers. Use '" + UNKNOWN_STANDARD + "' to select issues not associated to any CWE.") | |||
.setExampleValue("12,125," + UNKNOWN_STANDARD); | |||
action.createParam(PARAM_AUTHORS) | |||
.setDescription("Comma-separated list of SCM accounts") | |||
.setExampleValue("torvalds@linux-foundation.org"); | |||
@@ -556,6 +575,9 @@ public class SearchAction implements IssuesWsAction { | |||
addMandatoryValuesToFacet(facets, PARAM_LANGUAGES, request.getLanguages()); | |||
addMandatoryValuesToFacet(facets, PARAM_TAGS, request.getTags()); | |||
addMandatoryValuesToFacet(facets, PARAM_TYPES, RuleType.names()); | |||
addMandatoryValuesToFacet(facets, PARAM_OWASP_TOP_10, request.getOwaspTop10()); | |||
addMandatoryValuesToFacet(facets, PARAM_SANS_TOP_25, request.getSansTop25()); | |||
addMandatoryValuesToFacet(facets, PARAM_CWE, request.getCwe()); | |||
addMandatoryValuesToFacet(facets, PARAM_COMPONENT_UUIDS, request.getComponentUuids()); | |||
List<String> requestedFacets = request.getFacets(); | |||
@@ -655,6 +677,9 @@ public class SearchAction implements IssuesWsAction { | |||
.setSeverities(request.paramAsStrings(PARAM_SEVERITIES)) | |||
.setStatuses(request.paramAsStrings(PARAM_STATUSES)) | |||
.setTags(request.paramAsStrings(PARAM_TAGS)) | |||
.setTypes(request.paramAsStrings(PARAM_TYPES)); | |||
.setTypes(request.paramAsStrings(PARAM_TYPES)) | |||
.setOwaspTop10(request.paramAsStrings(PARAM_OWASP_TOP_10)) | |||
.setSansTop25(request.paramAsStrings(PARAM_SANS_TOP_25)) | |||
.setCwe(request.paramAsStrings(PARAM_CWE)); | |||
} | |||
} |
@@ -148,7 +148,7 @@ public class SearchActionTest { | |||
"pullRequest", "organization", | |||
"createdAfter", "createdAt", "createdBefore", "createdInLast", "directories", "facetMode", "facets", "fileUuids", "issues", "languages", "moduleUuids", "onComponentOnly", | |||
"p", "projectUuids", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod", | |||
"statuses", "tags", "types"); | |||
"statuses", "tags", "types", "owaspTop10", "sansTop25", "cwe"); | |||
assertThat(def.param("organization")) | |||
.matches(WebService.Param::isInternal) |
@@ -99,6 +99,9 @@ public class IssuesWsParameters { | |||
public static final String PARAM_LANGUAGES = "languages"; | |||
public static final String PARAM_TAGS = "tags"; | |||
public static final String PARAM_TYPES = "types"; | |||
public static final String PARAM_OWASP_TOP_10 = "owaspTop10"; | |||
public static final String PARAM_SANS_TOP_25 = "sansTop25"; | |||
public static final String PARAM_CWE = "cwe"; | |||
public static final String PARAM_ASSIGNED = "assigned"; | |||
/** | |||
@@ -132,7 +135,7 @@ public class IssuesWsParameters { | |||
public static final String FACET_ASSIGNED_TO_ME = "assigned_to_me"; | |||
public static final List<String> ALL = ImmutableList.of(PARAM_ISSUES, PARAM_SEVERITIES, PARAM_STATUSES, PARAM_RESOLUTIONS, PARAM_RESOLVED, | |||
PARAM_COMPONENTS, PARAM_COMPONENT_ROOTS, PARAM_RULES, DEPRECATED_PARAM_ACTION_PLANS, PARAM_REPORTERS, PARAM_TAGS, PARAM_TYPES, | |||
PARAM_COMPONENTS, PARAM_COMPONENT_ROOTS, PARAM_RULES, DEPRECATED_PARAM_ACTION_PLANS, PARAM_REPORTERS, PARAM_TAGS, PARAM_TYPES, PARAM_OWASP_TOP_10, PARAM_SANS_TOP_25, PARAM_CWE, | |||
PARAM_ASSIGNEES, PARAM_LANGUAGES, PARAM_ASSIGNED, PARAM_PLANNED, PARAM_HIDE_RULES, PARAM_CREATED_AT, PARAM_CREATED_AFTER, PARAM_CREATED_BEFORE, PARAM_CREATED_IN_LAST, | |||
PARAM_COMPONENT_UUIDS, PARAM_COMPONENT_ROOT_UUIDS, FACET_MODE, | |||
PARAM_PROJECTS, PARAM_PROJECT_UUIDS, PARAM_PROJECT_KEYS, PARAM_COMPONENT_KEYS, PARAM_MODULE_UUIDS, PARAM_DIRECTORIES, PARAM_FILE_UUIDS, PARAM_AUTHORS, |