private final Collection<String> languages; | private final Collection<String> languages; | ||||
private final Collection<String> tags; | private final Collection<String> tags; | ||||
private final Collection<String> types; | 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 Map<String, PeriodStart> createdAfterByProjectUuids; | ||||
private final Boolean onComponentOnly; | private final Boolean onComponentOnly; | ||||
private final Boolean assigned; | private final Boolean assigned; | ||||
this.languages = defaultCollection(builder.languages); | this.languages = defaultCollection(builder.languages); | ||||
this.tags = defaultCollection(builder.tags); | this.tags = defaultCollection(builder.tags); | ||||
this.types = defaultCollection(builder.types); | 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.createdAfterByProjectUuids = defaultMap(builder.createdAfterByProjectUuids); | ||||
this.onComponentOnly = builder.onComponentOnly; | this.onComponentOnly = builder.onComponentOnly; | ||||
this.assigned = builder.assigned; | this.assigned = builder.assigned; | ||||
return types; | 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() { | public Map<String, PeriodStart> createdAfterByProjectUuids() { | ||||
return createdAfterByProjectUuids; | return createdAfterByProjectUuids; | ||||
} | } | ||||
private Collection<String> languages; | private Collection<String> languages; | ||||
private Collection<String> tags; | private Collection<String> tags; | ||||
private Collection<String> types; | private Collection<String> types; | ||||
private Collection<String> owaspTop10; | |||||
private Collection<String> sansTop25; | |||||
private Collection<String> cwe; | |||||
private Map<String, PeriodStart> createdAfterByProjectUuids; | private Map<String, PeriodStart> createdAfterByProjectUuids; | ||||
private Boolean onComponentOnly = false; | private Boolean onComponentOnly = false; | ||||
private Boolean assigned = null; | private Boolean assigned = null; | ||||
return this; | 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) { | public Builder createdAfterByProjectUuids(@Nullable Map<String, PeriodStart> createdAfterByProjectUuids) { | ||||
this.createdAfterByProjectUuids = createdAfterByProjectUuids; | this.createdAfterByProjectUuids = createdAfterByProjectUuids; | ||||
return this; | return this; |
private List<String> statuses; | private List<String> statuses; | ||||
private List<String> tags; | private List<String> tags; | ||||
private List<String> types; | private List<String> types; | ||||
private List<String> owaspTop10; | |||||
private List<String> sansTop25; | |||||
private List<String> cwe; | |||||
@CheckForNull | @CheckForNull | ||||
public List<String> getActionPlans() { | public List<String> getActionPlans() { | ||||
return this; | 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 | @CheckForNull | ||||
public List<String> getComponentRootUuids() { | public List<String> getComponentRootUuids() { | ||||
return componentRootUuids; | return componentRootUuids; |
return this; | 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; | |||||
} | |||||
} | } |
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES; | 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_AUTHORS; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AT; | 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_DIRECTORIES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FILE_UUIDS; | 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_LANGUAGES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_MODULE_UUIDS; | 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_PROJECT_UUIDS; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_REPORTERS; | 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_RESOLUTIONS; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES; | 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_SEVERITIES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES; | 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_TAGS; | ||||
PARAM_LANGUAGES, | PARAM_LANGUAGES, | ||||
PARAM_TAGS, | PARAM_TAGS, | ||||
PARAM_TYPES, | PARAM_TYPES, | ||||
PARAM_OWASP_TOP_10, | |||||
PARAM_SANS_TOP_25, | |||||
PARAM_CWE, | |||||
PARAM_CREATED_AT); | PARAM_CREATED_AT); | ||||
public static final String AGGREGATION_NAME_FOR_TAGS = "tags__issues"; | public static final String AGGREGATION_NAME_FOR_TAGS = "tags__issues"; | ||||
private static final String SUBSTRING_MATCH_REGEXP = ".*%s.*"; | private static final String SUBSTRING_MATCH_REGEXP = ".*%s.*"; | ||||
if (options.getFacets().contains(PARAM_TYPES)) { | if (options.getFacets().contains(PARAM_TYPES)) { | ||||
esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(IssueIndexDefinition.FIELD_ISSUE_TYPE, PARAM_TYPES, query.types().toArray())); | 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)) { | if (options.getFacets().contains(PARAM_RESOLUTIONS)) { | ||||
esSearch.addAggregation(createResolutionFacet(query, filters, esQuery)); | esSearch.addAggregation(createResolutionFacet(query, filters, esQuery)); | ||||
} | } |
public static final String FIELD_ISSUE_STATUS = "status"; | public static final String FIELD_ISSUE_STATUS = "status"; | ||||
public static final String FIELD_ISSUE_TAGS = "tags"; | public static final String FIELD_ISSUE_TAGS = "tags"; | ||||
public static final String FIELD_ISSUE_TYPE = "type"; | 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 Configuration config; | ||||
private final boolean enableSource; | private final boolean enableSource; | ||||
type.keywordFieldBuilder(FIELD_ISSUE_STATUS).disableNorms().addSubFields(SORTABLE_ANALYZER).build(); | type.keywordFieldBuilder(FIELD_ISSUE_STATUS).disableNorms().addSubFields(SORTABLE_ANALYZER).build(); | ||||
type.keywordFieldBuilder(FIELD_ISSUE_TAGS).disableNorms().build(); | type.keywordFieldBuilder(FIELD_ISSUE_TAGS).disableNorms().build(); | ||||
type.keywordFieldBuilder(FIELD_ISSUE_TYPE).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(); | |||||
} | } | ||||
} | } |
import com.google.common.base.CharMatcher; | import com.google.common.base.CharMatcher; | ||||
import com.google.common.base.Splitter; | 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.Iterators; | ||||
import com.google.common.collect.Maps; | import com.google.common.collect.Maps; | ||||
import java.sql.PreparedStatement; | import java.sql.PreparedStatement; | ||||
import java.sql.ResultSet; | import java.sql.ResultSet; | ||||
import java.sql.SQLException; | import java.sql.SQLException; | ||||
import java.util.Collection; | 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.Collectors; | ||||
import java.util.stream.IntStream; | import java.util.stream.IntStream; | ||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import org.sonar.db.ResultSetIterator; | import org.sonar.db.ResultSetIterator; | ||||
import static com.google.common.base.Preconditions.checkArgument; | 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.api.utils.DateUtils.longToDate; | ||||
import static org.sonar.db.DatabaseUtils.getLong; | 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 | * Scrolls over table ISSUES and reads documents to populate | ||||
"c.scope", | "c.scope", | ||||
"c.organization_uuid", | "c.organization_uuid", | ||||
"c.project_uuid", | "c.project_uuid", | ||||
"c.main_branch_project_uuid", | |||||
// column 21 | // column 21 | ||||
"c.main_branch_project_uuid", | |||||
"i.tags", | "i.tags", | ||||
"i.issue_type" | |||||
"i.issue_type", | |||||
"r.security_standards" | |||||
}; | }; | ||||
private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from issues i " + | private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from issues i " + | ||||
private static final String ISSUE_KEY_FILTER_SUFFIX = ")"; | private static final String ISSUE_KEY_FILTER_SUFFIX = ")"; | ||||
static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); | 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(); | 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; | private final DbSession session; | ||||
doc.setIsMainBranch(false); | doc.setIsMainBranch(false); | ||||
} | } | ||||
String tags = rs.getString(21); | 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))); | 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; | 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 | @CheckForNull | ||||
private static String extractDirPath(@Nullable String filePath, String scope) { | private static String extractDirPath(@Nullable String filePath, String scope) { | ||||
if (filePath != null) { | if (filePath != null) { |
.languages(newArrayList("xoo")) | .languages(newArrayList("xoo")) | ||||
.tags(newArrayList("tag1", "tag2")) | .tags(newArrayList("tag1", "tag2")) | ||||
.types(newArrayList("RELIABILITY", "SECURITY")) | .types(newArrayList("RELIABILITY", "SECURITY")) | ||||
.owaspTop10(newArrayList("a1", "a2")) | |||||
.sansTop25(newArrayList("insecure-interaction", "porous-defenses")) | |||||
.cwe(newArrayList("12", "125")) | |||||
.organizationUuid("orga") | .organizationUuid("orga") | ||||
.branchUuid("my_branch") | .branchUuid("my_branch") | ||||
.createdAfterByProjectUuids(ImmutableMap.of("PROJECT", filterDate)) | .createdAfterByProjectUuids(ImmutableMap.of("PROJECT", filterDate)) | ||||
assertThat(query.languages()).containsOnly("xoo"); | assertThat(query.languages()).containsOnly("xoo"); | ||||
assertThat(query.tags()).containsOnly("tag1", "tag2"); | assertThat(query.tags()).containsOnly("tag1", "tag2"); | ||||
assertThat(query.types()).containsOnly("RELIABILITY", "SECURITY"); | 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.organizationUuid()).isEqualTo("orga"); | ||||
assertThat(query.branchUuid()).isEqualTo("my_branch"); | assertThat(query.branchUuid()).isEqualTo("my_branch"); | ||||
assertThat(query.createdAfterByProjectUuids()).containsOnly(entry("PROJECT", filterDate)); | assertThat(query.createdAfterByProjectUuids()).containsOnly(entry("PROJECT", filterDate)); | ||||
.languages(null) | .languages(null) | ||||
.tags(null) | .tags(null) | ||||
.types(null) | .types(null) | ||||
.owaspTop10(null) | |||||
.sansTop25(null) | |||||
.cwe(null) | |||||
.createdAfterByProjectUuids(null) | .createdAfterByProjectUuids(null) | ||||
.build(); | .build(); | ||||
assertThat(query.issueKeys()).isEmpty(); | assertThat(query.issueKeys()).isEmpty(); | ||||
assertThat(query.languages()).isEmpty(); | assertThat(query.languages()).isEmpty(); | ||||
assertThat(query.tags()).isEmpty(); | assertThat(query.tags()).isEmpty(); | ||||
assertThat(query.types()).isEmpty(); | assertThat(query.types()).isEmpty(); | ||||
assertThat(query.owaspTop10()).isEmpty(); | |||||
assertThat(query.sansTop25()).isEmpty(); | |||||
assertThat(query.cwe()).isEmpty(); | |||||
assertThat(query.createdAfterByProjectUuids()).isEmpty(); | assertThat(query.createdAfterByProjectUuids()).isEmpty(); | ||||
} | } | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.HashSet; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.function.Predicate; | import java.util.function.Predicate; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import static org.sonar.db.component.ComponentTesting.newFileDto; | import static org.sonar.db.component.ComponentTesting.newFileDto; | ||||
import static org.sonar.server.issue.IssueDocTesting.newDoc; | 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.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; | import static org.sonar.server.permission.index.AuthorizationTypeSupport.TYPE_AUTHORIZATION; | ||||
public class IssueIndexerTest { | public class IssueIndexerTest { | ||||
assertThat(doc.line()).isEqualTo(issue.getLine()); | assertThat(doc.line()).isEqualTo(issue.getLine()); | ||||
// functional date | // functional date | ||||
assertThat(doc.updateDate()).isEqualToIgnoringMillis(new Date(issue.getIssueUpdateTime())); | 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 | @Test |
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AT; | 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_BEFORE; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_IN_LAST; | 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_DIRECTORIES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FILE_UUIDS; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FILE_UUIDS; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ISSUES; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ISSUES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_MODULE_UUIDS; | 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_ON_COMPONENT_ONLY; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ORGANIZATION; | 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_PLANNED; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECTS; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECTS; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_KEYS; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_KEYS; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLUTIONS; | 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_RESOLVED; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES; | 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_SEVERITIES; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_PERIOD; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_PERIOD; | ||||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES; | import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES; | ||||
public class SearchAction implements IssuesWsAction { | public class SearchAction implements IssuesWsAction { | ||||
public static final String LOGIN_MYSELF = "__me__"; | 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 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); | private static final Set<String> IGNORED_FACETS = newHashSet(PARAM_PLANNED, DEPRECATED_PARAM_ACTION_PLANS, PARAM_REPORTERS); | ||||
new Change("5.5", "response field 'debt' is renamed 'effort'"), | 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", "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.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")); | .setResponseExample(getClass().getResource("search-example.json")); | ||||
action.addPagingParams(100, MAX_LIMIT); | action.addPagingParams(100, MAX_LIMIT); | ||||
.setSince("5.5") | .setSince("5.5") | ||||
.setPossibleValues((Object[]) RuleType.values()) | .setPossibleValues((Object[]) RuleType.values()) | ||||
.setExampleValue(format("%s,%s", RuleType.CODE_SMELL, RuleType.BUG)); | .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) | action.createParam(PARAM_AUTHORS) | ||||
.setDescription("Comma-separated list of SCM accounts") | .setDescription("Comma-separated list of SCM accounts") | ||||
.setExampleValue("torvalds@linux-foundation.org"); | .setExampleValue("torvalds@linux-foundation.org"); | ||||
addMandatoryValuesToFacet(facets, PARAM_LANGUAGES, request.getLanguages()); | addMandatoryValuesToFacet(facets, PARAM_LANGUAGES, request.getLanguages()); | ||||
addMandatoryValuesToFacet(facets, PARAM_TAGS, request.getTags()); | addMandatoryValuesToFacet(facets, PARAM_TAGS, request.getTags()); | ||||
addMandatoryValuesToFacet(facets, PARAM_TYPES, RuleType.names()); | 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()); | addMandatoryValuesToFacet(facets, PARAM_COMPONENT_UUIDS, request.getComponentUuids()); | ||||
List<String> requestedFacets = request.getFacets(); | List<String> requestedFacets = request.getFacets(); | ||||
.setSeverities(request.paramAsStrings(PARAM_SEVERITIES)) | .setSeverities(request.paramAsStrings(PARAM_SEVERITIES)) | ||||
.setStatuses(request.paramAsStrings(PARAM_STATUSES)) | .setStatuses(request.paramAsStrings(PARAM_STATUSES)) | ||||
.setTags(request.paramAsStrings(PARAM_TAGS)) | .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)); | |||||
} | } | ||||
} | } |
"pullRequest", "organization", | "pullRequest", "organization", | ||||
"createdAfter", "createdAt", "createdBefore", "createdInLast", "directories", "facetMode", "facets", "fileUuids", "issues", "languages", "moduleUuids", "onComponentOnly", | "createdAfter", "createdAt", "createdBefore", "createdInLast", "directories", "facetMode", "facets", "fileUuids", "issues", "languages", "moduleUuids", "onComponentOnly", | ||||
"p", "projectUuids", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod", | "p", "projectUuids", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod", | ||||
"statuses", "tags", "types"); | |||||
"statuses", "tags", "types", "owaspTop10", "sansTop25", "cwe"); | |||||
assertThat(def.param("organization")) | assertThat(def.param("organization")) | ||||
.matches(WebService.Param::isInternal) | .matches(WebService.Param::isInternal) |
public static final String PARAM_LANGUAGES = "languages"; | public static final String PARAM_LANGUAGES = "languages"; | ||||
public static final String PARAM_TAGS = "tags"; | public static final String PARAM_TAGS = "tags"; | ||||
public static final String PARAM_TYPES = "types"; | 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"; | public static final String PARAM_ASSIGNED = "assigned"; | ||||
/** | /** | ||||
public static final String FACET_ASSIGNED_TO_ME = "assigned_to_me"; | 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, | 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_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_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, | PARAM_PROJECTS, PARAM_PROJECT_UUIDS, PARAM_PROJECT_KEYS, PARAM_COMPONENT_KEYS, PARAM_MODULE_UUIDS, PARAM_DIRECTORIES, PARAM_FILE_UUIDS, PARAM_AUTHORS, |