123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- /*
- * SonarQube
- * Copyright (C) 2009-2023 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.
- */
- import { intersection, isArray, uniq } from 'lodash';
- import { getUsers } from '../../api/users';
- import { DEFAULT_ISSUES_QUERY } from '../../components/shared/utils';
- import { formatMeasure } from '../../helpers/measures';
- import {
- cleanQuery,
- parseAsArray,
- parseAsBoolean,
- parseAsDate,
- parseAsString,
- queriesEqual,
- serializeDateShort,
- serializeString,
- serializeStringArray,
- } from '../../helpers/query';
- import { get, save } from '../../helpers/storage';
- import { isDefined } from '../../helpers/types';
- import {
- CleanCodeAttributeCategory,
- SoftwareImpactSeverity,
- SoftwareQuality,
- } from '../../types/clean-code-taxonomy';
- import {
- Facet,
- IssueDeprecatedStatus,
- IssueResolution,
- IssueStatus,
- RawFacet,
- } from '../../types/issues';
- import { MetricType } from '../../types/metrics';
- import { SecurityStandard } from '../../types/security';
- import { Dict, Issue, Paging, RawQuery } from '../../types/types';
- import { RestUser } from '../../types/users';
-
- const OWASP_ASVS_4_0 = 'owaspAsvs-4.0';
-
- export interface Query {
- assigned: boolean;
- assignees: string[];
- author: string[];
- cleanCodeAttributeCategories: CleanCodeAttributeCategory[];
- codeVariants: string[];
- createdAfter: Date | undefined;
- createdAt: string;
- createdBefore: Date | undefined;
- createdInLast: string;
- cwe: string[];
- directories: string[];
- files: string[];
- impactSeverities: SoftwareImpactSeverity[];
- impactSoftwareQualities: SoftwareQuality[];
- issues: string[];
- languages: string[];
- owaspTop10: string[];
- 'owaspTop10-2021': string[];
- 'pciDss-3.2': string[];
- 'pciDss-4.0': string[];
- [OWASP_ASVS_4_0]: string[];
- owaspAsvsLevel: string;
- projects: string[];
- rules: string[];
- scopes: string[];
- severities: string[];
- inNewCodePeriod: boolean;
- sonarsourceSecurity: string[];
- sort: string;
- issueStatuses: IssueStatus[];
- tags: string[];
- types: string[];
- }
-
- export const STANDARDS = 'standards';
-
- // allow sorting by CREATION_DATE only
- const parseAsSort = (sort: string) => (sort === 'CREATION_DATE' ? 'CREATION_DATE' : '');
- const ISSUES_DEFAULT = 'sonarqube.issues.default';
-
- export function parseQuery(query: RawQuery): Query {
- return {
- assigned: parseAsBoolean(query.assigned),
- assignees: parseAsArray(query.assignees, parseAsString),
- author: isArray(query.author) ? query.author : [query.author].filter(isDefined),
- cleanCodeAttributeCategories: parseAsArray<CleanCodeAttributeCategory>(
- query.cleanCodeAttributeCategories,
- parseAsString,
- ),
- createdAfter: parseAsDate(query.createdAfter),
- createdAt: parseAsString(query.createdAt),
- createdBefore: parseAsDate(query.createdBefore),
- createdInLast: parseAsString(query.createdInLast),
- cwe: parseAsArray(query.cwe, parseAsString),
- directories: parseAsArray(query.directories, parseAsString),
- files: parseAsArray(query.files, parseAsString),
- impactSeverities: parseAsArray<SoftwareImpactSeverity>(query.impactSeverities, parseAsString),
- impactSoftwareQualities: parseAsArray<SoftwareQuality>(
- query.impactSoftwareQualities,
- parseAsString,
- ),
- inNewCodePeriod: parseAsBoolean(query.inNewCodePeriod, false),
- issues: parseAsArray(query.issues, parseAsString),
- languages: parseAsArray(query.languages, parseAsString),
- owaspTop10: parseAsArray(query.owaspTop10, parseAsString),
- 'owaspTop10-2021': parseAsArray(query['owaspTop10-2021'], parseAsString),
- 'pciDss-3.2': parseAsArray(query['pciDss-3.2'], parseAsString),
- 'pciDss-4.0': parseAsArray(query['pciDss-4.0'], parseAsString),
- [OWASP_ASVS_4_0]: parseAsArray(query[OWASP_ASVS_4_0], parseAsString),
- owaspAsvsLevel: parseAsString(query['owaspAsvsLevel']),
- projects: parseAsArray(query.projects, parseAsString),
- rules: parseAsArray(query.rules, parseAsString),
- scopes: parseAsArray(query.scopes, parseAsString),
- severities: parseAsArray(query.severities, parseAsString),
- sonarsourceSecurity: parseAsArray(query.sonarsourceSecurity, parseAsString),
- sort: parseAsSort(query.s),
- issueStatuses: parseIssueStatuses(query),
- tags: parseAsArray(query.tags, parseAsString),
- types: parseAsArray(query.types, parseAsString),
- codeVariants: parseAsArray(query.codeVariants, parseAsString),
- };
- }
-
- function parseIssueStatuses(query: RawQuery) {
- let result: Array<IssueStatus> = [];
-
- if (query.issueStatuses) {
- return parseAsArray<IssueStatus>(query.issueStatuses, parseAsString);
- }
-
- const deprecatedStatusesMap = {
- [IssueDeprecatedStatus.Open]: [IssueStatus.Open],
- [IssueDeprecatedStatus.Confirmed]: [IssueStatus.Confirmed],
- [IssueDeprecatedStatus.Reopened]: [IssueStatus.Open],
- [IssueDeprecatedStatus.Resolved]: [
- IssueStatus.Fixed,
- IssueStatus.Accepted,
- IssueStatus.FalsePositive,
- ],
- [IssueDeprecatedStatus.Closed]: [IssueStatus.Fixed],
- };
- const deprecatedResolutionsMap = {
- [IssueResolution.FalsePositive]: [IssueStatus.FalsePositive],
- [IssueResolution.WontFix]: [IssueStatus.Accepted],
- [IssueResolution.Fixed]: [IssueStatus.Fixed],
- [IssueResolution.Removed]: [IssueStatus.Fixed],
- [IssueResolution.Unresolved]: [IssueStatus.Open, IssueStatus.Confirmed],
- };
-
- const issuesStatusesFromDeprecatedStatuses = parseAsArray<IssueDeprecatedStatus>(
- query.statuses,
- parseAsString,
- )
- .map((status) => deprecatedStatusesMap[status])
- .filter(Boolean)
- .flat();
- const issueStatusesFromResolutions = parseAsArray<IssueResolution>(
- query.resolutions,
- parseAsString,
- )
- .map((status) => deprecatedResolutionsMap[status])
- .filter(Boolean)
- .flat();
-
- const intesectedIssueStatuses = intersection(
- issuesStatusesFromDeprecatedStatuses,
- issueStatusesFromResolutions,
- );
- result = intesectedIssueStatuses.length
- ? intesectedIssueStatuses
- : issueStatusesFromResolutions.concat(issuesStatusesFromDeprecatedStatuses);
-
- if (
- query.resolved === 'false' &&
- [IssueStatus.Open, IssueStatus.Confirmed].every((status) => !result.includes(status))
- ) {
- result = result.concat(
- parseAsArray<IssueStatus>(DEFAULT_ISSUES_QUERY.issueStatuses, parseAsString),
- );
- }
-
- return uniq(result);
- }
-
- export function getOpen(query: RawQuery): string | undefined {
- return query.open;
- }
-
- export function getOpenIssue(props: { location: { query: RawQuery } }, issues: Issue[]) {
- const open = getOpen(props.location.query);
- return open ? issues.find((issue) => issue.key === open) : undefined;
- }
-
- export const areMyIssuesSelected = (query: RawQuery) => query.myIssues === 'true';
-
- export function serializeQuery(query: Query): RawQuery {
- const filter = {
- assigned: query.assigned ? undefined : 'false',
- assignees: serializeStringArray(query.assignees),
- author: query.author,
- cleanCodeAttributeCategories: serializeStringArray(query.cleanCodeAttributeCategories),
- createdAfter: serializeDateShort(query.createdAfter),
- createdAt: serializeString(query.createdAt),
- createdBefore: serializeDateShort(query.createdBefore),
- createdInLast: serializeString(query.createdInLast),
- cwe: serializeStringArray(query.cwe),
- directories: serializeStringArray(query.directories),
- files: serializeStringArray(query.files),
- issues: serializeStringArray(query.issues),
- languages: serializeStringArray(query.languages),
- owaspTop10: serializeStringArray(query.owaspTop10),
- 'owaspTop10-2021': serializeStringArray(query['owaspTop10-2021']),
- 'pciDss-3.2': serializeStringArray(query['pciDss-3.2']),
- 'pciDss-4.0': serializeStringArray(query['pciDss-4.0']),
- [OWASP_ASVS_4_0]: serializeStringArray(query[OWASP_ASVS_4_0]),
- owaspAsvsLevel: serializeString(query['owaspAsvsLevel']),
- projects: serializeStringArray(query.projects),
- rules: serializeStringArray(query.rules),
- s: serializeString(query.sort),
- scopes: serializeStringArray(query.scopes),
- severities: serializeStringArray(query.severities),
- impactSeverities: serializeStringArray(query.impactSeverities),
- impactSoftwareQualities: serializeStringArray(query.impactSoftwareQualities),
- inNewCodePeriod: query.inNewCodePeriod ? 'true' : undefined,
- sonarsourceSecurity: serializeStringArray(query.sonarsourceSecurity),
- issueStatuses: serializeStringArray(query.issueStatuses),
- tags: serializeStringArray(query.tags),
- types: serializeStringArray(query.types),
- codeVariants: serializeStringArray(query.codeVariants),
- };
-
- return cleanQuery(filter);
- }
-
- export const areQueriesEqual = (a: RawQuery, b: RawQuery) =>
- queriesEqual(parseQuery(a), parseQuery(b));
-
- export function parseFacets(facets?: RawFacet[]): Dict<Facet> {
- if (!facets) {
- return {};
- }
-
- const result: Dict<Facet> = {};
- facets.forEach((facet) => {
- const values: Facet = {};
- facet.values.forEach((value) => {
- values[value.val] = value.count;
- });
- result[facet.property] = values;
- });
- return result;
- }
-
- export function formatFacetStat(stat: number | undefined) {
- return stat && formatMeasure(stat, MetricType.ShortInteger);
- }
-
- export const searchAssignees = (
- query: string,
- page = 1,
- ): Promise<{ paging: Paging; results: RestUser[] }> => {
- return getUsers<RestUser>({ pageIndex: page, q: query }).then(({ page, users }) => ({
- paging: page,
- results: users,
- }));
- };
-
- const LOCALSTORAGE_MY = 'my';
- const LOCALSTORAGE_ALL = 'all';
-
- export const isMySet = () => {
- return get(ISSUES_DEFAULT) === LOCALSTORAGE_MY;
- };
-
- export const saveMyIssues = (myIssues: boolean) =>
- save(ISSUES_DEFAULT, myIssues ? LOCALSTORAGE_MY : LOCALSTORAGE_ALL);
-
- export function getLocations(
- {
- flows,
- secondaryLocations,
- flowsWithType,
- }: Pick<Issue, 'flows' | 'secondaryLocations' | 'flowsWithType'>,
- selectedFlowIndex: number | undefined,
- ) {
- if (secondaryLocations.length > 0) {
- return secondaryLocations;
- } else if (selectedFlowIndex !== undefined) {
- return flows[selectedFlowIndex] || flowsWithType[selectedFlowIndex]?.locations || [];
- }
- return [];
- }
-
- export function getSelectedLocation(
- issue: Pick<Issue, 'flows' | 'secondaryLocations' | 'flowsWithType'>,
- selectedFlowIndex: number | undefined,
- selectedLocationIndex: number | undefined,
- ) {
- const locations = getLocations(issue, selectedFlowIndex);
- if (
- selectedLocationIndex !== undefined &&
- selectedLocationIndex >= 0 &&
- locations.length >= selectedLocationIndex
- ) {
- return locations[selectedLocationIndex];
- }
- return undefined;
- }
-
- export function allLocationsEmpty(
- issue: Pick<Issue, 'flows' | 'secondaryLocations' | 'flowsWithType'>,
- selectedFlowIndex: number | undefined,
- ) {
- return getLocations(issue, selectedFlowIndex).every((location) => !location.msg);
- }
-
- export function shouldOpenStandardsFacet(
- openFacets: Dict<boolean>,
- query: Partial<Query>,
- ): boolean {
- return (
- openFacets[STANDARDS] ||
- isFilteredBySecurityIssueTypes(query) ||
- isOneStandardChildFacetOpen(openFacets, query)
- );
- }
-
- export function shouldOpenStandardsChildFacet(
- openFacets: Dict<boolean>,
- query: Partial<Query>,
- standardType:
- | SecurityStandard.CWE
- | SecurityStandard.OWASP_TOP10
- | SecurityStandard.OWASP_TOP10_2021
- | SecurityStandard.SONARSOURCE,
- ): boolean {
- const filter = query[standardType];
- return (
- openFacets[STANDARDS] !== false &&
- (openFacets[standardType] ||
- (standardType !== SecurityStandard.CWE && filter !== undefined && filter.length > 0))
- );
- }
-
- export function shouldOpenSonarSourceSecurityFacet(
- openFacets: Dict<boolean>,
- query: Partial<Query>,
- ): boolean {
- // Open it by default if the parent is open, and no other standard is open.
- return (
- shouldOpenStandardsChildFacet(openFacets, query, SecurityStandard.SONARSOURCE) ||
- (shouldOpenStandardsFacet(openFacets, query) && !isOneStandardChildFacetOpen(openFacets, query))
- );
- }
-
- function isFilteredBySecurityIssueTypes(query: Partial<Query>): boolean {
- return query.types !== undefined && query.types.includes('VULNERABILITY');
- }
-
- function isOneStandardChildFacetOpen(openFacets: Dict<boolean>, query: Partial<Query>): boolean {
- return [SecurityStandard.OWASP_TOP10, SecurityStandard.CWE, SecurityStandard.SONARSOURCE].some(
- (
- standardType:
- | SecurityStandard.CWE
- | SecurityStandard.OWASP_TOP10
- | SecurityStandard.OWASP_TOP10_2021
- | SecurityStandard.SONARSOURCE,
- ) => shouldOpenStandardsChildFacet(openFacets, query, standardType),
- );
- }
|