--- /dev/null
+CWE™ is a community-developed list of common software security weaknesses. It serves as a common language, a measuring stick for software security tools, and as a baseline for weakness identification, mitigation, and prevention efforts.
\ No newline at end of file
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { SecurityHotspot } from '../app/types';
+import { getJSON } from '../helpers/request';
+import throwGlobalError from '../app/utils/throwGlobalError';
+
+export function getSecurityHotspots(data: {
+ project: string;
+ standard: 'owaspTop10' | 'sansTop25' | 'cwe';
+ includeDistribution?: boolean;
+ branch?: string;
+}): Promise<{ categories: Array<SecurityHotspot> }> {
+ return getJSON('/api/security_reports/show', data).catch(throwGlobalError);
+}
);
}
+ renderSecurityReportsLink() {
+ return (
+ <ul className="menu">
+ <li>
+ <Link
+ activeClassName="active"
+ to={{ pathname: '/project/security_reports/owasp_top_10', query: this.getQuery() }}>
+ {translate('security_reports.owaspTop10.page')}
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ to={{ pathname: '/project/security_reports/sans_top_25', query: this.getQuery() }}>
+ {translate('security_reports.sansTop25.page')}
+ </Link>
+ </li>
+ </ul>
+ );
+ }
+
+ renderSecurityReports() {
+ const isActive = location.pathname.startsWith('/project/security_reports');
+ return (
+ <Dropdown overlay={this.renderSecurityReportsLink()} tagName="li">
+ {({ onToggleClick, open }) => (
+ <a
+ aria-expanded={String(open)}
+ aria-haspopup="true"
+ className={classNames('dropdown-toggle', { active: isActive || open })}
+ href="#"
+ onClick={onToggleClick}>
+ {translate('layout.security_reports')}
+ <DropdownIcon className="little-spacer-left" />
+ </a>
+ )}
+ </Dropdown>
+ );
+ }
+
renderAdministration() {
const { branchLike } = this.props;
<NavBarTabs>
{this.renderDashboardLink()}
{this.renderIssuesLink()}
+ {this.renderSecurityReports()}
{this.renderComponentMeasuresLink()}
{this.renderCodeLink()}
{this.renderActivityLink()}
issues.page
</Link>
</li>
+ <Dropdown
+ overlay={
+ <ul
+ className="menu"
+ >
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/owasp_top_10",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.owaspTop10.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/sans_top_25",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sansTop25.page
+ </Link>
+ </li>
+ </ul>
+ }
+ tagName="li"
+ />
<li>
<Link
activeClassName="active"
issues.page
</Link>
</li>
+ <Dropdown
+ overlay={
+ <ul
+ className="menu"
+ >
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/owasp_top_10",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.owaspTop10.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/sans_top_25",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sansTop25.page
+ </Link>
+ </li>
+ </ul>
+ }
+ tagName="li"
+ />
<li>
<Link
activeClassName="active"
issues.page
</Link>
</li>
+ <Dropdown
+ overlay={
+ <ul
+ className="menu"
+ >
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/owasp_top_10",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.owaspTop10.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/sans_top_25",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sansTop25.page
+ </Link>
+ </li>
+ </ul>
+ }
+ tagName="li"
+ />
<li>
<Link
activeClassName="active"
issues.page
</Link>
</li>
+ <Dropdown
+ overlay={
+ <ul
+ className="menu"
+ >
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/owasp_top_10",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.owaspTop10.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/sans_top_25",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sansTop25.page
+ </Link>
+ </li>
+ </ul>
+ }
+ tagName="li"
+ />
<li>
<Link
activeClassName="active"
issues.page
</Link>
</li>
+ <Dropdown
+ overlay={
+ <ul
+ className="menu"
+ >
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/owasp_top_10",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.owaspTop10.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/sans_top_25",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sansTop25.page
+ </Link>
+ </li>
+ </ul>
+ }
+ tagName="li"
+ />
<li>
<Link
activeClassName="active"
issues.page
</Link>
</li>
+ <Dropdown
+ overlay={
+ <ul
+ className="menu"
+ >
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/owasp_top_10",
+ "query": Object {
+ "branch": "release",
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.owaspTop10.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/sans_top_25",
+ "query": Object {
+ "branch": "release",
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sansTop25.page
+ </Link>
+ </li>
+ </ul>
+ }
+ tagName="li"
+ />
<li>
<Link
activeClassName="active"
issues.page
</Link>
</li>
+ <Dropdown
+ overlay={
+ <ul
+ className="menu"
+ >
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/owasp_top_10",
+ "query": Object {
+ "branch": "release",
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.owaspTop10.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/sans_top_25",
+ "query": Object {
+ "branch": "release",
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sansTop25.page
+ </Link>
+ </li>
+ </ul>
+ }
+ tagName="li"
+ />
<li>
<Link
activeClassName="active"
issues.page
</Link>
</li>
+ <Dropdown
+ overlay={
+ <ul
+ className="menu"
+ >
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/owasp_top_10",
+ "query": Object {
+ "branch": "feature",
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.owaspTop10.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/security_reports/sans_top_25",
+ "query": Object {
+ "branch": "feature",
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sansTop25.page
+ </Link>
+ </li>
+ </ul>
+ }
+ tagName="li"
+ />
<li>
<Link
activeClassName="active"
padding-bottom: 5px;
}
+table.data tr.subheader th {
+ font-size: var(--smallFontSize);
+ border-bottom: none;
+}
+
table.data.no-outer-padding > thead > tr > th:first-child {
padding-left: 0;
}
);
}
+export interface SecurityHotspot {
+ category?: string;
+ cwe?: string;
+ distribution?: Array<SecurityHotspot>;
+ openSecurityHotspots: number;
+ toReviewSecurityHotspots: number;
+ vulnerabilities: number;
+ vulnerabilityRating?: number;
+ wontFixSecurityHotspots: number;
+}
+
export interface Issue {
actions?: string[];
assignee?: string;
)}
/>
<Route path="project/issues" component={Issues} />
+ <Route
+ path="project/security_reports/:type"
+ component={lazyLoad(() => import('../../apps/securityReports/components/App'))}
+ />
<Route path="project/quality_gate" childRoutes={projectQualityGateRoutes} />
<Route
path="project/quality_profiles"
import FacetItemsList from '../../../components/facet/FacetItemsList';
import FacetItem from '../../../components/facet/FacetItem';
import Select from '../../../components/controls/Select';
+import {
+ renderOwaspTop10Category,
+ renderSansTop25Category,
+ renderCWECategory,
+ Standards
+} from '../../securityReports/utils';
export interface Props {
cwe: string[];
sansTop25Stats: { [x: string]: number } | undefined;
}
-interface Standards {
- owaspTop10: { [x: string]: { title: string } };
- sansTop25: { [x: string]: { title: string } };
- cwe: { [x: string]: { title: string } };
-}
-
interface State {
standards: Standards;
}
getValues = () => {
return [
- ...this.props.owaspTop10.map(this.renderOwaspTop10Category),
- ...this.props.sansTop25.map(this.renderSansTop25Category),
- ...this.props.cwe.map(this.renderCWECategory)
+ ...this.props.owaspTop10.map(item => renderOwaspTop10Category(this.state.standards, item)),
+ ...this.props.sansTop25.map(item => renderSansTop25Category(this.state.standards, item)),
+ ...this.props.cwe.map(item => renderCWECategory(this.state.standards, item))
];
};
this.handleItemClick('cwe', value, true);
};
- renderOwaspTop10Category = (category: string) => {
- const record = this.state.standards.owaspTop10[category];
- if (!record) {
- return category.toUpperCase();
- } else if (category === 'unknown') {
- return record.title;
- } else {
- return `${category.toUpperCase()} - ${record.title}`;
- }
- };
-
- renderCWECategory = (category: string) => {
- const record = this.state.standards.cwe[category];
- if (!record) {
- return `CWE-${category}`;
- } else if (category === 'unknown') {
- return record.title;
- } else {
- return `CWE-${category} - ${record.title}`;
- }
- };
-
- renderSansTop25Category = (category: string) => {
- const record = this.state.standards.sansTop25[category];
- return record ? record.title : category;
- };
-
renderList = (
statsProp: 'owaspTop10Stats' | 'cweStats' | 'sansTop25Stats',
valuesProp: 'owaspTop10' | 'cwe' | 'sansTop25',
- renderName: (category: string) => string,
+ renderName: (standards: Standards, category: string) => string,
onClick: (x: string, multiple?: boolean) => void
) => {
const stats = this.props[statsProp];
active={values.includes(category)}
key={category}
loading={this.props.loading}
- name={renderName(category)}
+ name={renderName(this.state.standards, category)}
onClick={onClick}
stat={formatFacetStat(getStat(category))}
tooltip={values.length === 1 && !values.includes(category)}
return this.renderList(
'owaspTop10Stats',
'owaspTop10',
- this.renderOwaspTop10Category,
+ renderOwaspTop10Category,
this.handleOwaspTop10ItemClick
);
}
renderCWEList() {
- return this.renderList('cweStats', 'cwe', this.renderCWECategory, this.handleCWEItemClick);
+ return this.renderList('cweStats', 'cwe', renderCWECategory, this.handleCWEItemClick);
}
renderCWESearch() {
const options = Object.keys(this.state.standards.cwe).map(cwe => ({
- label: this.renderCWECategory(cwe),
+ label: renderCWECategory(this.state.standards, cwe),
value: cwe
}));
return (
return this.renderList(
'sansTop25Stats',
'sansTop25',
- this.renderSansTop25Category,
+ renderSansTop25Category,
this.handleSansTop25ItemClick
);
}
name={translate('issues.facet.owaspTop10')}
onClick={this.handleOwaspTop10HeaderClick}
open={this.props.owaspTop10Open}
- values={this.props.owaspTop10.map(this.renderOwaspTop10Category)}
+ values={this.props.owaspTop10.map(item =>
+ renderOwaspTop10Category(this.state.standards, item)
+ )}
/>
{this.props.owaspTop10Open && this.renderOwaspTop10List()}
</FacetBox>
name={translate('issues.facet.sansTop25')}
onClick={this.handleSansTop25HeaderClick}
open={this.props.sansTop25Open}
- values={this.props.sansTop25.map(this.renderSansTop25Category)}
+ values={this.props.sansTop25.map(item =>
+ renderSansTop25Category(this.state.standards, item)
+ )}
/>
{this.props.sansTop25Open && this.renderSansTop25List()}
</FacetBox>
name={translate('issues.facet.cwe')}
onClick={this.handleCWEHeaderClick}
open={this.props.cweOpen}
- values={this.props.cwe.map(this.renderCWECategory)}
+ values={this.props.cwe.map(item => renderCWECategory(this.state.standards, item))}
/>
{this.props.cweOpen && this.renderCWEList()}
{this.props.cweOpen && this.renderCWESearch()}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 * as React from 'react';
+import * as PropTypes from 'prop-types';
+import Helmet from 'react-helmet';
+import VulnerabilityList from './VulnerabilityList';
+import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
+import { translate } from '../../../helpers/l10n';
+import { Component, BranchLike, SecurityHotspot } from '../../../app/types';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
+import Checkbox from '../../../components/controls/Checkbox';
+import { RawQuery } from '../../../helpers/query';
+import NotFound from '../../../app/components/NotFound';
+import '../style.css';
+import { getSecurityHotspots } from '../../../api/security-reports';
+import { isLongLivingBranch } from '../../../helpers/branches';
+import DocTooltip from '../../../components/docs/DocTooltip';
+
+interface Props {
+ branchLike?: BranchLike;
+ component: Component;
+ location: { pathname: string; query: RawQuery };
+ params: { type: string };
+}
+
+interface State {
+ loading: boolean;
+ findings: Array<SecurityHotspot>;
+ hasVulnerabilities: boolean;
+ type: 'owaspTop10' | 'sansTop25' | 'cwe';
+ showCWE: boolean;
+}
+
+export default class App extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
+
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ loading: false,
+ findings: [],
+ hasVulnerabilities: false,
+ type: props.params.type === 'owasp_top_10' ? 'owaspTop10' : 'sansTop25',
+ showCWE: props.location.query.showCWE === 'true'
+ };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ this.fetchSecurityHotspots();
+ }
+
+ componentWillReceiveProps(newProps: Props) {
+ if (newProps.location.pathname !== this.props.location.pathname) {
+ const showCWE = newProps.location.query.showCWE === 'true';
+ const type = newProps.params.type === 'owasp_top_10' ? 'owaspTop10' : 'sansTop25';
+ this.setState({ type, showCWE }, this.fetchSecurityHotspots);
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchSecurityHotspots = () => {
+ const { branchLike, component } = this.props;
+ this.setState({ loading: true });
+ getSecurityHotspots({
+ project: component.key,
+ standard: this.state.type,
+ includeDistribution: this.state.showCWE,
+ branch: isLongLivingBranch(branchLike) ? branchLike.name : undefined
+ })
+ .then(results => {
+ if (this.mounted) {
+ const hasVulnerabilities = results.categories.some(item => item.vulnerabilities > 0);
+ this.setState({ hasVulnerabilities, findings: results.categories, loading: false });
+ }
+ })
+ .catch(() => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ });
+ };
+
+ handleCheck = (checked: boolean) => {
+ const { router } = this.context;
+ router.push({
+ pathname: this.props.location.pathname,
+ query: { id: this.props.component.key, showCWE: checked }
+ });
+ this.setState({ showCWE: checked }, this.fetchSecurityHotspots);
+ };
+
+ render() {
+ const { branchLike, component, params } = this.props;
+ const { loading, findings, showCWE, type } = this.state;
+ if (params.type !== 'owasp_top_10' && params.type !== 'sans_top_25') {
+ return <NotFound withContainer={false} />;
+ }
+ return (
+ <div className="page page-limited" id="security-reports">
+ <Suggestions suggestions="security_reports" />
+ <Helmet title={translate('security_reports', type, 'page')} />
+ <header className="page-header">
+ <h1 className="page-title">{translate('security_reports', type, 'page')}</h1>
+ <div className="page-description">
+ {translate('security_reports', type, 'description')}
+ </div>
+ </header>
+ <div className="display-inline-flex-center">
+ <Checkbox
+ checked={showCWE}
+ className="spacer-left spacer-right vertical-middle"
+ disabled={!this.state.hasVulnerabilities}
+ id={'showCWE'}
+ onCheck={this.handleCheck}>
+ <label className="little-spacer-left" htmlFor={'showCWE'}>
+ {translate('security_reports.cwe.show')}
+ <DocTooltip className="spacer-left" doc="security-reports/cwe" />
+ </label>
+ </Checkbox>
+ </div>
+ <DeferredSpinner loading={loading}>
+ <VulnerabilityList
+ branchLike={branchLike}
+ component={component}
+ findings={findings}
+ showCWE={showCWE}
+ type={type}
+ />
+ </DeferredSpinner>
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 * as React from 'react';
+import { Link } from 'react-router';
+import { translate } from '../../../helpers/l10n';
+import { SecurityHotspot, Component, BranchLike } from '../../../app/types';
+import Rating from '../../../components/ui/Rating';
+import { getComponentIssuesUrl } from '../../../helpers/urls';
+import { getBranchLikeQuery } from '../../../helpers/branches';
+import HelpTooltip from '../../../components/controls/HelpTooltip';
+import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon';
+import SecurityHotspotIcon from '../../../components/icons-components/SecurityHotspotIcon';
+import {
+ renderOwaspTop10Category,
+ renderSansTop25Category,
+ renderCWECategory,
+ Standards
+} from '../utils';
+
+interface Props {
+ branchLike?: BranchLike;
+ component: Component;
+ findings: Array<SecurityHotspot>;
+ showCWE: boolean;
+ type: 'owaspTop10' | 'sansTop25' | 'cwe';
+}
+
+interface State {
+ standards: Standards;
+}
+
+export default class VulnerabilityList extends React.PureComponent<Props, State> {
+ mounted = false;
+ state: State = { standards: { owaspTop10: {}, sansTop25: {}, cwe: {} } };
+
+ componentDidMount() {
+ this.mounted = true;
+ this.loadStandards();
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ loadStandards = () => {
+ import('../../../helpers/standards.json')
+ .then(x => x.default)
+ .then(
+ ({ owaspTop10, sansTop25, cwe }: Standards) => {
+ if (this.mounted) {
+ this.setState({ standards: { owaspTop10, sansTop25, cwe } });
+ }
+ },
+ () => {}
+ );
+ };
+
+ getName(finding: SecurityHotspot, type: 'owaspTop10' | 'sansTop25' | 'cwe') {
+ const category = finding.category || finding.cwe || 'unknown';
+ const renderers = {
+ owaspTop10: renderOwaspTop10Category,
+ sansTop25: renderSansTop25Category,
+ cwe: renderCWECategory
+ };
+ return (
+ <>
+ {renderers[type](this.state.standards, category)}
+ {this.state.standards[type][category] &&
+ this.state.standards[type][category].description && (
+ <HelpTooltip
+ className="spacer-left"
+ overlay={this.state.standards[type][category].description}
+ />
+ )}
+ </>
+ );
+ }
+
+ renderFinding(finding: SecurityHotspot, isCWE?: boolean): React.ReactFragment {
+ const { branchLike, component, type } = this.props;
+ const params: { [name: string]: string | undefined } = {
+ ...getBranchLikeQuery(branchLike),
+ types: 'SECURITY_HOTSPOT'
+ };
+ params[type] = finding.category || finding.cwe;
+
+ const subFindings =
+ this.props.showCWE && finding.distribution
+ ? finding.distribution.map(f => this.renderFinding(f, true))
+ : null;
+
+ return (
+ <React.Fragment key={finding.category || finding.cwe}>
+ <tr>
+ {isCWE && <td />}
+ <td className="nowrap" colSpan={isCWE ? 1 : 2}>
+ <div className="display-inline-flex-center">
+ {this.getName(finding, isCWE ? 'cwe' : type)}
+ </div>
+ </td>
+ <td>
+ <div className="display-inline-flex-center">
+ <Link
+ to={getComponentIssuesUrl(component.key, { ...params, types: 'VULNERABILITY' })}>
+ {finding.vulnerabilities}
+ </Link>
+ <Link
+ className="link-no-underline spacer-left"
+ to={getComponentIssuesUrl(component.key, { ...params, types: 'VULNERABILITY' })}>
+ <Rating value={finding.vulnerabilityRating || 1} />
+ </Link>
+ </div>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ to={getComponentIssuesUrl(component.key, {
+ ...params,
+ types: 'SECURITY_HOTSPOT',
+ resolved: 'false',
+ statuses: 'OPEN'
+ })}>
+ {finding.openSecurityHotspots}
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ to={getComponentIssuesUrl(component.key, {
+ ...params,
+ types: 'SECURITY_HOTSPOT',
+ resolutions: 'FIXED'
+ })}>
+ {finding.toReviewSecurityHotspots}
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ to={getComponentIssuesUrl(component.key, {
+ ...params,
+ types: 'SECURITY_HOTSPOT',
+ resolutions: 'WONTFIX'
+ })}>
+ {finding.wontFixSecurityHotspots}
+ </Link>
+ </td>
+ </tr>
+ {subFindings}
+ </React.Fragment>
+ );
+ }
+
+ render() {
+ return (
+ <div className="boxed-group boxed-group-inner spacer-top">
+ <table className="data zebra">
+ <thead>
+ <tr>
+ <th className="security-category-column" colSpan={2}>
+ {translate('security_reports.list.categories')}
+ </th>
+ <th className="security-result-column">
+ <div className="display-inline-flex-center">
+ <VulnerabilityIcon className="spacer-right" />{' '}
+ {translate('security_reports.list.vulnerabilities')}
+ </div>
+ </th>
+ <th colSpan={3}>
+ <div className="display-inline-flex-center">
+ <SecurityHotspotIcon className="spacer-right" />{' '}
+ {translate('security_reports.list.hotspots')}
+ </div>
+ </th>
+ </tr>
+ <tr className="subheader">
+ <th colSpan={3} />
+ <th className="security-result-column">{translate('security_reports.line.open')}</th>
+ <th className="security-result-column">
+ {translate('security_reports.line.in_review')}
+ </th>
+ <th className="security-result-column">
+ {translate('security_reports.line.wont_fix')}
+ </th>
+ </tr>
+ </thead>
+ <tbody>{this.props.findings.map(finding => this.renderFinding(finding))}</tbody>
+ </table>
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+/* eslint-disable import/first, import/order */
+jest.mock('../../../../api/security-reports', () => ({
+ getSecurityHotspots: jest.fn(() => {
+ const distribution: any = [
+ {
+ cwe: '477',
+ vulnerabilities: 1,
+ vulnerabiliyRating: 1,
+ toReviewSecurityHotspots: 2,
+ openSecurityHotspots: 10,
+ wontFixSecurityHotspots: 0
+ },
+ {
+ cwe: '396',
+ vulnerabilities: 2,
+ vulnerabiliyRating: 2,
+ toReviewSecurityHotspots: 2,
+ openSecurityHotspots: 10,
+ wontFixSecurityHotspots: 0
+ }
+ ];
+ return Promise.resolve({
+ categories: [
+ {
+ category: 'a1',
+ vulnerabilities: 2,
+ vulnerabiliyRating: 5,
+ toReviewSecurityHotspots: 2,
+ openSecurityHotspots: 10,
+ wontFixSecurityHotspots: 0,
+ distribution
+ },
+ {
+ category: 'a2',
+ vulnerabilities: 3,
+ vulnerabiliyRating: 3,
+ toReviewSecurityHotspots: 8,
+ openSecurityHotspots: 100,
+ wontFixSecurityHotspots: 10
+ }
+ ]
+ });
+ })
+}));
+
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import { Component } from '../../../../app/types';
+import App from '../App';
+import { waitAndUpdate } from '../../../../helpers/testUtils';
+
+const getSecurityHotspots = require('../../../../api/security-reports')
+ .getSecurityHotspots as jest.Mock<any>;
+
+const component = { key: 'foo', name: 'Foo', qualifier: 'TRK' } as Component;
+const context = { router: { push: jest.fn() } };
+const location = { pathname: 'foo', query: {} };
+const locationWithCWE = { pathname: 'foo', query: { showCWE: 'true' } };
+const owaspParams = { type: 'owasp_top_10' };
+const sansParams = { type: 'sans_top_25' };
+const wrongParams = { type: 'foo' };
+
+beforeEach(() => {
+ getSecurityHotspots.mockClear();
+});
+
+it('renders error on wrong type parameters', () => {
+ const wrapper = shallow(<App component={component} location={location} params={wrongParams} />, {
+ context
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('renders owaspTop10', () => {
+ const wrapper = shallow(<App component={component} location={location} params={owaspParams} />, {
+ context
+ });
+ expect(getSecurityHotspots).toBeCalledWith({
+ project: 'foo',
+ standard: 'owaspTop10',
+ includeDistribution: false,
+ branch: undefined
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('renders with cwe', () => {
+ const wrapper = shallow(
+ <App component={component} location={locationWithCWE} params={owaspParams} />,
+ { context }
+ );
+ expect(getSecurityHotspots).toBeCalledWith({
+ project: 'foo',
+ standard: 'owaspTop10',
+ includeDistribution: true,
+ branch: undefined
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('handle checkbox for cwe display', async () => {
+ const wrapper = shallow(<App component={component} location={location} params={owaspParams} />, {
+ context
+ });
+ expect(getSecurityHotspots).toBeCalledWith({
+ project: 'foo',
+ standard: 'owaspTop10',
+ includeDistribution: false,
+ branch: undefined
+ });
+ expect(wrapper).toMatchSnapshot();
+
+ wrapper.find('Checkbox').prop<Function>('onCheck')(true);
+ await waitAndUpdate(wrapper);
+
+ expect(getSecurityHotspots).toBeCalledWith({
+ project: 'foo',
+ standard: 'owaspTop10',
+ includeDistribution: true,
+ branch: undefined
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('renders sansTop25', () => {
+ const wrapper = shallow(<App component={component} location={location} params={sansParams} />, {
+ context
+ });
+ expect(getSecurityHotspots).toBeCalledWith({
+ project: 'foo',
+ standard: 'sansTop25',
+ includeDistribution: false,
+ branch: undefined
+ });
+ expect(wrapper).toMatchSnapshot();
+});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import VulnerabilityList from '../VulnerabilityList';
+import { Component } from '../../../../app/types';
+
+jest.mock('../../../../helpers/standards.json', () => ({
+ default: {
+ owaspTop10: { a1: { title: 'a1 title' }, unknown: { title: 'Not OWAPS' } },
+ sansTop25: { 'risky-resource': { title: 'Risky Resource Management' } },
+ cwe: { 42: { title: 'cwe-42 title' }, unknown: { title: 'Unknown CWE' } }
+ }
+}));
+
+const component = { key: 'foo', name: 'Foo', qualifier: 'TRK' } as Component;
+const findings = [
+ {
+ category: 'a1',
+ vulnerabilities: 2,
+ vulnerabilityRating: 5,
+ toReviewSecurityHotspots: 2,
+ openSecurityHotspots: 10,
+ wontFixSecurityHotspots: 0,
+ distribution: [
+ {
+ cwe: '42',
+ vulnerabilities: 1,
+ vulnerabilityRating: 1,
+ toReviewSecurityHotspots: 2,
+ openSecurityHotspots: 10,
+ wontFixSecurityHotspots: 0
+ }
+ ]
+ },
+ {
+ category: 'unknown',
+ vulnerabilities: 3,
+ vulnerabilityRating: 3,
+ toReviewSecurityHotspots: 8,
+ openSecurityHotspots: 100,
+ wontFixSecurityHotspots: 10
+ }
+];
+
+it('renders', () => {
+ const wrapper = shallow(
+ <VulnerabilityList
+ component={component}
+ findings={findings}
+ showCWE={false}
+ type="owaspTop10"
+ />
+ );
+ expect(wrapper.find('tr').length).toBe(4);
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('renders with cwe', () => {
+ const wrapper = shallow(
+ <VulnerabilityList component={component} findings={findings} showCWE={true} type="owaspTop10" />
+ );
+ expect(wrapper.find('tr').length).toBe(5);
+ expect(wrapper).toMatchSnapshot();
+});
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`handle checkbox for cwe display 1`] = `
+<div
+ className="page page-limited"
+ id="security-reports"
+>
+ <Suggestions
+ suggestions="security_reports"
+ />
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="security_reports.owaspTop10.page"
+ />
+ <header
+ className="page-header"
+ >
+ <h1
+ className="page-title"
+ >
+ security_reports.owaspTop10.page
+ </h1>
+ <div
+ className="page-description"
+ >
+ security_reports.owaspTop10.description
+ </div>
+ </header>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Checkbox
+ checked={false}
+ className="spacer-left spacer-right vertical-middle"
+ disabled={true}
+ id="showCWE"
+ onCheck={[Function]}
+ thirdState={false}
+ >
+ <label
+ className="little-spacer-left"
+ htmlFor="showCWE"
+ >
+ security_reports.cwe.show
+ <DocTooltip
+ className="spacer-left"
+ doc="security-reports/cwe"
+ />
+ </label>
+ </Checkbox>
+ </div>
+ <DeferredSpinner
+ loading={true}
+ timeout={100}
+ >
+ <VulnerabilityList
+ component={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ findings={Array []}
+ showCWE={false}
+ type="owaspTop10"
+ />
+ </DeferredSpinner>
+</div>
+`;
+
+exports[`handle checkbox for cwe display 2`] = `
+<div
+ className="page page-limited"
+ id="security-reports"
+>
+ <Suggestions
+ suggestions="security_reports"
+ />
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="security_reports.owaspTop10.page"
+ />
+ <header
+ className="page-header"
+ >
+ <h1
+ className="page-title"
+ >
+ security_reports.owaspTop10.page
+ </h1>
+ <div
+ className="page-description"
+ >
+ security_reports.owaspTop10.description
+ </div>
+ </header>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Checkbox
+ checked={true}
+ className="spacer-left spacer-right vertical-middle"
+ disabled={false}
+ id="showCWE"
+ onCheck={[Function]}
+ thirdState={false}
+ >
+ <label
+ className="little-spacer-left"
+ htmlFor="showCWE"
+ >
+ security_reports.cwe.show
+ <DocTooltip
+ className="spacer-left"
+ doc="security-reports/cwe"
+ />
+ </label>
+ </Checkbox>
+ </div>
+ <DeferredSpinner
+ loading={false}
+ timeout={100}
+ >
+ <VulnerabilityList
+ component={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ findings={
+ Array [
+ Object {
+ "category": "a1",
+ "distribution": Array [
+ Object {
+ "cwe": "477",
+ "openSecurityHotspots": 10,
+ "toReviewSecurityHotspots": 2,
+ "vulnerabilities": 1,
+ "vulnerabiliyRating": 1,
+ "wontFixSecurityHotspots": 0,
+ },
+ Object {
+ "cwe": "396",
+ "openSecurityHotspots": 10,
+ "toReviewSecurityHotspots": 2,
+ "vulnerabilities": 2,
+ "vulnerabiliyRating": 2,
+ "wontFixSecurityHotspots": 0,
+ },
+ ],
+ "openSecurityHotspots": 10,
+ "toReviewSecurityHotspots": 2,
+ "vulnerabilities": 2,
+ "vulnerabiliyRating": 5,
+ "wontFixSecurityHotspots": 0,
+ },
+ Object {
+ "category": "a2",
+ "openSecurityHotspots": 100,
+ "toReviewSecurityHotspots": 8,
+ "vulnerabilities": 3,
+ "vulnerabiliyRating": 3,
+ "wontFixSecurityHotspots": 10,
+ },
+ ]
+ }
+ showCWE={true}
+ type="owaspTop10"
+ />
+ </DeferredSpinner>
+</div>
+`;
+
+exports[`renders error on wrong type parameters 1`] = `
+<NotFound
+ withContainer={false}
+/>
+`;
+
+exports[`renders owaspTop10 1`] = `
+<div
+ className="page page-limited"
+ id="security-reports"
+>
+ <Suggestions
+ suggestions="security_reports"
+ />
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="security_reports.owaspTop10.page"
+ />
+ <header
+ className="page-header"
+ >
+ <h1
+ className="page-title"
+ >
+ security_reports.owaspTop10.page
+ </h1>
+ <div
+ className="page-description"
+ >
+ security_reports.owaspTop10.description
+ </div>
+ </header>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Checkbox
+ checked={false}
+ className="spacer-left spacer-right vertical-middle"
+ disabled={true}
+ id="showCWE"
+ onCheck={[Function]}
+ thirdState={false}
+ >
+ <label
+ className="little-spacer-left"
+ htmlFor="showCWE"
+ >
+ security_reports.cwe.show
+ <DocTooltip
+ className="spacer-left"
+ doc="security-reports/cwe"
+ />
+ </label>
+ </Checkbox>
+ </div>
+ <DeferredSpinner
+ loading={true}
+ timeout={100}
+ >
+ <VulnerabilityList
+ component={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ findings={Array []}
+ showCWE={false}
+ type="owaspTop10"
+ />
+ </DeferredSpinner>
+</div>
+`;
+
+exports[`renders sansTop25 1`] = `
+<div
+ className="page page-limited"
+ id="security-reports"
+>
+ <Suggestions
+ suggestions="security_reports"
+ />
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="security_reports.sansTop25.page"
+ />
+ <header
+ className="page-header"
+ >
+ <h1
+ className="page-title"
+ >
+ security_reports.sansTop25.page
+ </h1>
+ <div
+ className="page-description"
+ >
+ security_reports.sansTop25.description
+ </div>
+ </header>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Checkbox
+ checked={false}
+ className="spacer-left spacer-right vertical-middle"
+ disabled={true}
+ id="showCWE"
+ onCheck={[Function]}
+ thirdState={false}
+ >
+ <label
+ className="little-spacer-left"
+ htmlFor="showCWE"
+ >
+ security_reports.cwe.show
+ <DocTooltip
+ className="spacer-left"
+ doc="security-reports/cwe"
+ />
+ </label>
+ </Checkbox>
+ </div>
+ <DeferredSpinner
+ loading={true}
+ timeout={100}
+ >
+ <VulnerabilityList
+ component={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ findings={Array []}
+ showCWE={false}
+ type="sansTop25"
+ />
+ </DeferredSpinner>
+</div>
+`;
+
+exports[`renders with cwe 1`] = `
+<div
+ className="page page-limited"
+ id="security-reports"
+>
+ <Suggestions
+ suggestions="security_reports"
+ />
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="security_reports.owaspTop10.page"
+ />
+ <header
+ className="page-header"
+ >
+ <h1
+ className="page-title"
+ >
+ security_reports.owaspTop10.page
+ </h1>
+ <div
+ className="page-description"
+ >
+ security_reports.owaspTop10.description
+ </div>
+ </header>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Checkbox
+ checked={true}
+ className="spacer-left spacer-right vertical-middle"
+ disabled={true}
+ id="showCWE"
+ onCheck={[Function]}
+ thirdState={false}
+ >
+ <label
+ className="little-spacer-left"
+ htmlFor="showCWE"
+ >
+ security_reports.cwe.show
+ <DocTooltip
+ className="spacer-left"
+ doc="security-reports/cwe"
+ />
+ </label>
+ </Checkbox>
+ </div>
+ <DeferredSpinner
+ loading={true}
+ timeout={100}
+ >
+ <VulnerabilityList
+ component={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ findings={Array []}
+ showCWE={true}
+ type="owaspTop10"
+ />
+ </DeferredSpinner>
+</div>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className="boxed-group boxed-group-inner spacer-top"
+>
+ <table
+ className="data zebra"
+ >
+ <thead>
+ <tr>
+ <th
+ className="security-category-column"
+ colSpan={2}
+ >
+ security_reports.list.categories
+ </th>
+ <th
+ className="security-result-column"
+ >
+ <div
+ className="display-inline-flex-center"
+ >
+ <VulnerabilityIcon
+ className="spacer-right"
+ />
+
+ security_reports.list.vulnerabilities
+ </div>
+ </th>
+ <th
+ colSpan={3}
+ >
+ <div
+ className="display-inline-flex-center"
+ >
+ <SecurityHotspotIcon
+ className="spacer-right"
+ />
+
+ security_reports.list.hotspots
+ </div>
+ </th>
+ </tr>
+ <tr
+ className="subheader"
+ >
+ <th
+ colSpan={3}
+ />
+ <th
+ className="security-result-column"
+ >
+ security_reports.line.open
+ </th>
+ <th
+ className="security-result-column"
+ >
+ security_reports.line.in_review
+ </th>
+ <th
+ className="security-result-column"
+ >
+ security_reports.line.wont_fix
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <React.Fragment
+ key="a1"
+ >
+ <tr>
+ <td
+ className="nowrap"
+ colSpan={2}
+ >
+ <div
+ className="display-inline-flex-center"
+ >
+ <React.Fragment>
+ A1
+ </React.Fragment>
+ </div>
+ </td>
+ <td>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ 2
+ </Link>
+ <Link
+ className="link-no-underline spacer-left"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ <Rating
+ value={5}
+ />
+ </Link>
+ </div>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "resolved": "false",
+ "statuses": "OPEN",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 10
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "resolutions": "FIXED",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 2
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "resolutions": "WONTFIX",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 0
+ </Link>
+ </td>
+ </tr>
+ </React.Fragment>
+ <React.Fragment
+ key="unknown"
+ >
+ <tr>
+ <td
+ className="nowrap"
+ colSpan={2}
+ >
+ <div
+ className="display-inline-flex-center"
+ >
+ <React.Fragment>
+ UNKNOWN
+ </React.Fragment>
+ </div>
+ </td>
+ <td>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ 3
+ </Link>
+ <Link
+ className="link-no-underline spacer-left"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ <Rating
+ value={3}
+ />
+ </Link>
+ </div>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "resolved": "false",
+ "statuses": "OPEN",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 100
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "resolutions": "FIXED",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 8
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "resolutions": "WONTFIX",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 10
+ </Link>
+ </td>
+ </tr>
+ </React.Fragment>
+ </tbody>
+ </table>
+</div>
+`;
+
+exports[`renders with cwe 1`] = `
+<div
+ className="boxed-group boxed-group-inner spacer-top"
+>
+ <table
+ className="data zebra"
+ >
+ <thead>
+ <tr>
+ <th
+ className="security-category-column"
+ colSpan={2}
+ >
+ security_reports.list.categories
+ </th>
+ <th
+ className="security-result-column"
+ >
+ <div
+ className="display-inline-flex-center"
+ >
+ <VulnerabilityIcon
+ className="spacer-right"
+ />
+
+ security_reports.list.vulnerabilities
+ </div>
+ </th>
+ <th
+ colSpan={3}
+ >
+ <div
+ className="display-inline-flex-center"
+ >
+ <SecurityHotspotIcon
+ className="spacer-right"
+ />
+
+ security_reports.list.hotspots
+ </div>
+ </th>
+ </tr>
+ <tr
+ className="subheader"
+ >
+ <th
+ colSpan={3}
+ />
+ <th
+ className="security-result-column"
+ >
+ security_reports.line.open
+ </th>
+ <th
+ className="security-result-column"
+ >
+ security_reports.line.in_review
+ </th>
+ <th
+ className="security-result-column"
+ >
+ security_reports.line.wont_fix
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <React.Fragment
+ key="a1"
+ >
+ <tr>
+ <td
+ className="nowrap"
+ colSpan={2}
+ >
+ <div
+ className="display-inline-flex-center"
+ >
+ <React.Fragment>
+ A1
+ </React.Fragment>
+ </div>
+ </td>
+ <td>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ 2
+ </Link>
+ <Link
+ className="link-no-underline spacer-left"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ <Rating
+ value={5}
+ />
+ </Link>
+ </div>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "resolved": "false",
+ "statuses": "OPEN",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 10
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "resolutions": "FIXED",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 2
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "a1",
+ "resolutions": "WONTFIX",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 0
+ </Link>
+ </td>
+ </tr>
+ <React.Fragment
+ key="42"
+ >
+ <tr>
+ <td />
+ <td
+ className="nowrap"
+ colSpan={1}
+ >
+ <div
+ className="display-inline-flex-center"
+ >
+ <React.Fragment>
+ CWE-42
+ </React.Fragment>
+ </div>
+ </td>
+ <td>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "42",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ 1
+ </Link>
+ <Link
+ className="link-no-underline spacer-left"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "42",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ <Rating
+ value={1}
+ />
+ </Link>
+ </div>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "42",
+ "resolved": "false",
+ "statuses": "OPEN",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 10
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "42",
+ "resolutions": "FIXED",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 2
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "42",
+ "resolutions": "WONTFIX",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 0
+ </Link>
+ </td>
+ </tr>
+ </React.Fragment>
+ </React.Fragment>
+ <React.Fragment
+ key="unknown"
+ >
+ <tr>
+ <td
+ className="nowrap"
+ colSpan={2}
+ >
+ <div
+ className="display-inline-flex-center"
+ >
+ <React.Fragment>
+ UNKNOWN
+ </React.Fragment>
+ </div>
+ </td>
+ <td>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ 3
+ </Link>
+ <Link
+ className="link-no-underline spacer-left"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "types": "VULNERABILITY",
+ },
+ }
+ }
+ >
+ <Rating
+ value={3}
+ />
+ </Link>
+ </div>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "resolved": "false",
+ "statuses": "OPEN",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 100
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "resolutions": "FIXED",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 8
+ </Link>
+ </td>
+ <td>
+ <Link
+ className="spacer-right"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "owaspTop10": "unknown",
+ "resolutions": "WONTFIX",
+ "types": "SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 10
+ </Link>
+ </td>
+ </tr>
+ </React.Fragment>
+ </tbody>
+ </table>
+</div>
+`;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+.security-category-column {
+ width: 52%;
+}
+
+.security-result-column {
+ width: 12%;
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+export interface Standards {
+ owaspTop10: { [x: string]: { title: string; description?: string } };
+ sansTop25: { [x: string]: { title: string; description?: string } };
+ cwe: { [x: string]: { title: string; description?: string } };
+}
+
+export function renderOwaspTop10Category(standards: Standards, category: string): string {
+ const record = standards.owaspTop10[category];
+ if (!record) {
+ return category.toUpperCase();
+ } else if (category === 'unknown') {
+ return record.title;
+ } else {
+ return `${category.toUpperCase()} - ${record.title}`;
+ }
+}
+
+export function renderCWECategory(standards: Standards, category: string): string {
+ const record = standards.cwe[category];
+ if (!record) {
+ return `CWE-${category}`;
+ } else if (category === 'unknown') {
+ return record.title;
+ } else {
+ return `CWE-${category} - ${record.title}`;
+ }
+}
+
+export function renderSansTop25Category(standards: Standards, category: string): string {
+ const record = standards.sansTop25[category];
+ return record ? record.title : category;
+}
return (
<a
className={classNames('link-checkbox', this.props.className, {
- 'text-muted': this.props.disabled,
+ note: this.props.disabled,
disabled: this.props.disabled
})}
href="#"
"a1": {
"title": "Injection",
"description":
- "Injection flaws, such as SQL, NoSQL, OS, and LDAP injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter into executing unintended commands or accessing data without proper authorization."
+ "Injection flaws, such as SQL, NoSQL, OS, and LDAP injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter into executing unintended commands or accessing data without proper authorization."
},
"a2": {
"title": "Broken Authentication",
"description":
- "Application functions related to authentication and session management are often implemented incorrectly, allowing attackers to compromise passwords, keys, or session tokens, or to exploit other implementation flaws to assume other users’ identities temporarily or permanently."
+ "Application functions related to authentication and session management are often implemented incorrectly, allowing attackers to compromise passwords, keys, or session tokens, or to exploit other implementation flaws to assume other users’ identities temporarily or permanently."
},
"a3": {
"title": "Sensitive Data Exposure",
"a5": {
"title": "Broken Access Control",
"description":
- "Restrictions on what authenticated users are allowed to do are often not properly enforced. Attackers can exploit these flaws to access unauthorized functionality and/or data, such as access other users' accounts, view sensitive files, modify other users’ data, change access rights, etc."
+ "Restrictions on what authenticated users are allowed to do are often not properly enforced. Attackers can exploit these flaws to access unauthorized functionality and/or data, such as access other users' accounts, view sensitive files, modify other users’ data, change access rights, etc."
},
"a6": {
"title": "Security Misconfiguration",
"a7": {
"title": "Cross-Site Scripting (XSS)",
"description":
- "XSS flaws occur whenever an application includes untrusted data in a new web page without proper validation or escaping, or updates an existing web page with user-supplied data using a browser API that can create HTML or JavaScript. XSS allows attackers to execute scripts in the victim’s browser which can hijack user sessions, deface web sites, or redirect the user to malicious sites."
+ "XSS flaws occur whenever an application includes untrusted data in a new web page without proper validation or escaping, or updates an existing web page with user-supplied data using a browser API that can create HTML or JavaScript. XSS allows attackers to execute scripts in the victim’s browser which can hijack user sessions, deface web sites, or redirect the user to malicious sites."
},
"a8": {
"title": "Insecure Deserialization",
layout.logout=Log out
layout.measures=Measures
layout.settings=Administration
+layout.security_reports=Security Reports
layout.sonar.slogan=Continuous Code Quality
sidebar.projects=Projects
organizations_permissions.provisioning=Create Projects
organizations_permissions.provisioning.desc=Ability to initialize a project so its settings can be configured before the first analysis.
-
+#------------------------------------------------------------------------------
+#
+# SECURITY REPORTS PAGE
+#
+#------------------------------------------------------------------------------
+security_reports.owaspTop10.page=OWASP Top 10
+security_reports.sansTop25.page=SANS Top 25
+security_reports.owaspTop10.description=Track Vulnerabilities and Security Hotspots conforming to OWASP Top 10 standard.
+security_reports.sansTop25.description=Track Vulnerabilities and Security Hotspots conforming to SANS Top 25 standard.
+security_reports.list.categories=Categories
+security_reports.list.vulnerabilities=Vulnerabilities
+security_reports.list.hotspots=Security Hotspots
+security_reports.line.open=Open
+security_reports.line.wont_fix=Won't Fix
+security_reports.line.in_review=In Review
+security_reports.cwe.show=Show CWE distribution
#------------------------------------------------------------------------------
#