aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js
diff options
context:
space:
mode:
authorWouter Admiraal <wouter.admiraal@sonarsource.com>2019-04-17 15:33:26 +0200
committersonartech <sonartech@sonarsource.com>2019-05-07 09:54:27 +0200
commit378fa0ac1a561bcd9d0c5f2e063a9c4ea17a0511 (patch)
tree608eac38ca66321dd85f5101d2964008306964a0 /server/sonar-web/src/main/js
parent699a47b373815a9cf368e287b343d151f5fc0a59 (diff)
downloadsonarqube-378fa0ac1a561bcd9d0c5f2e063a9c4ea17a0511.tar.gz
sonarqube-378fa0ac1a561bcd9d0c5f2e063a9c4ea17a0511.zip
SONAR-11983 Create a new 'SonarSource' security report page
Diffstat (limited to 'server/sonar-web/src/main/js')
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx10
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap104
-rwxr-xr-xserver/sonar-web/src/main/js/apps/securityReports/components/App.tsx31
-rwxr-xr-xserver/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap144
-rwxr-xr-xserver/sonar-web/src/main/js/apps/securityReports/utils.ts19
-rw-r--r--server/sonar-web/src/main/js/helpers/standards.json68
8 files changed, 381 insertions, 26 deletions
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
index 1c59dad2338..4f779bf27ac 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
@@ -160,6 +160,16 @@ export class ComponentNavMenu extends React.PureComponent<Props> {
<li>
<Link
activeClassName="active"
+ to={{
+ pathname: '/project/security_reports/sonarsource_security',
+ query: this.getQuery()
+ }}>
+ {translate('security_reports.sonarsourceSecurity.page')}
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
to={{ pathname: '/project/security_reports/owasp_top_10', query: this.getQuery() }}>
{translate('security_reports.owaspTop10.page')}
</Link>
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap
index 221e7dd6f26..34ca59e182e 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap
@@ -50,6 +50,23 @@ exports[`should work for all qualifiers 1`] = `
style={Object {}}
to={
Object {
+ "pathname": "/project/security_reports/sonarsource_security",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sonarsourceSecurity.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
"pathname": "/project/security_reports/owasp_top_10",
"query": Object {
"id": "foo",
@@ -267,6 +284,23 @@ exports[`should work for all qualifiers 2`] = `
style={Object {}}
to={
Object {
+ "pathname": "/project/security_reports/sonarsource_security",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sonarsourceSecurity.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
"pathname": "/project/security_reports/owasp_top_10",
"query": Object {
"id": "foo",
@@ -433,6 +467,23 @@ exports[`should work for all qualifiers 3`] = `
style={Object {}}
to={
Object {
+ "pathname": "/project/security_reports/sonarsource_security",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sonarsourceSecurity.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
"pathname": "/project/security_reports/owasp_top_10",
"query": Object {
"id": "foo",
@@ -570,6 +621,23 @@ exports[`should work for all qualifiers 4`] = `
style={Object {}}
to={
Object {
+ "pathname": "/project/security_reports/sonarsource_security",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sonarsourceSecurity.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
"pathname": "/project/security_reports/owasp_top_10",
"query": Object {
"id": "foo",
@@ -738,6 +806,24 @@ exports[`should work for long-living branches 1`] = `
style={Object {}}
to={
Object {
+ "pathname": "/project/security_reports/sonarsource_security",
+ "query": Object {
+ "branch": "release",
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sonarsourceSecurity.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
"pathname": "/project/security_reports/owasp_top_10",
"query": Object {
"branch": "release",
@@ -882,6 +968,24 @@ exports[`should work for long-living branches 2`] = `
style={Object {}}
to={
Object {
+ "pathname": "/project/security_reports/sonarsource_security",
+ "query": Object {
+ "branch": "release",
+ "id": "foo",
+ },
+ }
+ }
+ >
+ security_reports.sonarsourceSecurity.page
+ </Link>
+ </li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
"pathname": "/project/security_reports/owasp_top_10",
"query": Object {
"branch": "release",
diff --git a/server/sonar-web/src/main/js/apps/securityReports/components/App.tsx b/server/sonar-web/src/main/js/apps/securityReports/components/App.tsx
index 6dc80000947..37c049a0c86 100755
--- a/server/sonar-web/src/main/js/apps/securityReports/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/securityReports/components/App.tsx
@@ -21,17 +21,18 @@ import * as React from 'react';
import Helmet from 'react-helmet';
import { Link } from 'react-router';
import VulnerabilityList from './VulnerabilityList';
-import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
-import { translate } from '../../../helpers/l10n';
import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
-import DeferredSpinner from '../../../components/common/DeferredSpinner';
+import { Alert } from '../../../components/ui/Alert';
import Checkbox from '../../../components/controls/Checkbox';
-import NotFound from '../../../app/components/NotFound';
-import { getSecurityHotspots } from '../../../api/security-reports';
-import { isLongLivingBranch } from '../../../helpers/branches';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
import DocTooltip from '../../../components/docs/DocTooltip';
-import { Alert } from '../../../components/ui/Alert';
+import NotFound from '../../../app/components/NotFound';
+import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import { withRouter, Location, Router } from '../../../components/hoc/withRouter';
+import { translate } from '../../../helpers/l10n';
+import { isLongLivingBranch } from '../../../helpers/branches';
+import { getSecurityHotspots } from '../../../api/security-reports';
+import { getType } from '../utils';
import '../style.css';
interface Props {
@@ -59,7 +60,7 @@ export class App extends React.PureComponent<Props, State> {
loading: false,
findings: [],
hasVulnerabilities: false,
- type: this.getType(props.params.type),
+ type: getType(props.params.type),
showCWE: props.location.query.showCWE === 'true'
};
}
@@ -72,7 +73,7 @@ export class App extends React.PureComponent<Props, State> {
componentWillReceiveProps(newProps: Props) {
if (newProps.location.pathname !== this.props.location.pathname) {
const showCWE = newProps.location.query.showCWE === 'true';
- const type = this.getType(newProps.params.type);
+ const type = getType(newProps.params.type);
this.setState({ type, showCWE }, this.fetchSecurityHotspots);
}
}
@@ -81,16 +82,6 @@ export class App extends React.PureComponent<Props, State> {
this.mounted = false;
}
- getType = (type: string): T.StandardType => {
- if (type === 'owasp_top_10') {
- return 'owaspTop10';
- } else if (type === 'sans_top_25') {
- return 'sansTop25';
- } else {
- return 'sonarsource';
- }
- };
-
fetchSecurityHotspots = () => {
const { branchLike, component } = this.props;
this.setState({ loading: true });
@@ -150,7 +141,7 @@ export class App extends React.PureComponent<Props, State> {
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') {
+ if (!['owasp_top_10', 'sans_top_25', 'sonarsource_security'].includes(params.type)) {
return <NotFound withContainer={false} />;
}
return (
diff --git a/server/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx b/server/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx
index a508d30c120..4d1d4dc6e86 100755
--- a/server/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx
+++ b/server/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx
@@ -27,7 +27,12 @@ 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 } from '../utils';
+import {
+ renderOwaspTop10Category,
+ renderSansTop25Category,
+ renderCWECategory,
+ renderSonarSourceSecurityCategory
+} from '../utils';
import DetachIcon from '../../../components/icons-components/DetachIcon';
import Tooltip from '../../../components/controls/Tooltip';
import { getRatingTooltip } from '../../../helpers/measures';
@@ -86,9 +91,7 @@ export default class VulnerabilityList extends React.PureComponent<Props, State>
owaspTop10: renderOwaspTop10Category,
sansTop25: renderSansTop25Category,
cwe: renderCWECategory,
- sonarsourceSecurity: () => {
- /* TODO */
- }
+ sonarsourceSecurity: renderSonarSourceSecurityCategory
};
return (
<>
diff --git a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx
index 4f43520f752..12bf3c5b428 100644
--- a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx
@@ -89,6 +89,7 @@ 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 sonarParams = { type: 'sonarsource_security' };
const wrongParams = { type: 'foo' };
beforeEach(() => {
@@ -190,3 +191,22 @@ it('renders sansTop25', () => {
});
expect(wrapper).toMatchSnapshot();
});
+
+it('renders sonarsourceSecurity', async () => {
+ const wrapper = shallow(
+ <App
+ component={component}
+ location={location}
+ params={sonarParams}
+ router={{ push: jest.fn() }}
+ />
+ );
+ await waitAndUpdate(wrapper);
+ expect(getSecurityHotspots).toBeCalledWith({
+ project: 'foo',
+ standard: 'sonarsourceSecurity',
+ includeDistribution: false,
+ branch: undefined
+ });
+ expect(wrapper).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap
index 2884f4f0c57..b89aa9c57c4 100644
--- a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -466,6 +466,150 @@ exports[`renders sansTop25 1`] = `
</div>
`;
+exports[`renders sonarsourceSecurity 1`] = `
+<div
+ className="page page-limited"
+ id="security-reports"
+>
+ <Suggestions
+ suggestions="security_reports"
+ />
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="security_reports.sonarsourceSecurity.page"
+ />
+ <header
+ className="page-header"
+ >
+ <A11ySkipTarget
+ anchor="security_main"
+ />
+ <h1
+ className="page-title"
+ >
+ security_reports.sonarsourceSecurity.page
+ </h1>
+ <div
+ className="page-description"
+ >
+ security_reports.sonarsourceSecurity.description
+ <Link
+ className="spacer-left"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ target="_blank"
+ to={
+ Object {
+ "pathname": "/documentation/user-guide/security-reports/",
+ }
+ }
+ >
+ learn_more
+ </Link>
+ <Alert
+ className="spacer-top"
+ display="inline"
+ variant="info"
+ >
+ security_reports.more_rules
+ </Alert>
+ </div>
+ </header>
+ <div
+ className="display-inline-flex-center"
+ >
+ <Checkbox
+ checked={false}
+ 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={Promise {}}
+ />
+ </label>
+ </Checkbox>
+ </div>
+ <DeferredSpinner
+ loading={false}
+ timeout={100}
+ >
+ <VulnerabilityList
+ component={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ findings={
+ Array [
+ Object {
+ "activeRules": 1,
+ "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,
+ "totalRules": 1,
+ "vulnerabilities": 2,
+ "vulnerabiliyRating": 5,
+ "wontFixSecurityHotspots": 0,
+ },
+ Object {
+ "activeRules": 1,
+ "category": "a2",
+ "openSecurityHotspots": 100,
+ "toReviewSecurityHotspots": 8,
+ "totalRules": 1,
+ "vulnerabilities": 3,
+ "vulnerabiliyRating": 3,
+ "wontFixSecurityHotspots": 10,
+ },
+ Object {
+ "activeRules": 0,
+ "category": "a3",
+ "openSecurityHotspots": 100,
+ "toReviewSecurityHotspots": 8,
+ "totalRules": 1,
+ "vulnerabilities": 3,
+ "vulnerabiliyRating": 3,
+ "wontFixSecurityHotspots": 10,
+ },
+ ]
+ }
+ showCWE={false}
+ type="sonarsourceSecurity"
+ />
+ </DeferredSpinner>
+</div>
+`;
+
exports[`renders with cwe 1`] = `
<div
className="page page-limited"
diff --git a/server/sonar-web/src/main/js/apps/securityReports/utils.ts b/server/sonar-web/src/main/js/apps/securityReports/utils.ts
index e53518ab2f4..604133f285e 100755
--- a/server/sonar-web/src/main/js/apps/securityReports/utils.ts
+++ b/server/sonar-web/src/main/js/apps/securityReports/utils.ts
@@ -52,6 +52,25 @@ export function renderSansTop25Category(
return addPrefix(record ? record.title : category, 'SANS', withPrefix);
}
+export function renderSonarSourceCategory(
+ standards: T.Standards,
+ category: string,
+ withPrefix = false
+): string {
+ const record = standards.sonarsourceSecurity[category];
+ return addPrefix(record ? record.title : category, 'SONAR', withPrefix);
+}
+
function addPrefix(title: string, prefix: string, withPrefix: boolean) {
return withPrefix ? `${prefix} ${title}` : title;
}
+
+const TYPES_MAP: T.Dict<T.StandardType> = {
+ owasp_top_10: 'owaspTop10',
+ sans_top_25: 'sansTop25',
+ sonarsource_security: 'sonarsourceSecurity'
+};
+
+export function getType(type: string): T.StandardType {
+ return TYPES_MAP[type] || 'sonarsourceSecurity';
+}
diff --git a/server/sonar-web/src/main/js/helpers/standards.json b/server/sonar-web/src/main/js/helpers/standards.json
index f2d32e1715b..051fc01038d 100644
--- a/server/sonar-web/src/main/js/helpers/standards.json
+++ b/server/sonar-web/src/main/js/helpers/standards.json
@@ -3619,8 +3619,72 @@
},
"99": {
"title": "Improper Control of Resource Identifiers ('Resource Injection')",
- "description":
- "The software receives input from an upstream component, but it does not restrict or incorrectly restricts the input before it is used as an identifier for a resource that may be outside the intended sphere of control."
+ "description": "The software receives input from an upstream component, but it does not restrict or incorrectly restricts the input before it is used as an identifier for a resource that may be outside the intended sphere of control."
+ }
+ },
+ "sonarsourceSecurity": {
+ "sql-injection": {
+ "title": "SQL Injection"
+ },
+ "rce": {
+ "title": "Code Injection (RCE)"
+ },
+ "object-injection": {
+ "title": "Object Injection"
+ },
+ "command-injection": {
+ "title": "Command Injection"
+ },
+ "path-traversal-injection": {
+ "title": "Path Traversal Injection"
+ },
+ "ldap-injection": {
+ "title": "LDAP Injection"
+ },
+ "xpath-injection": {
+ "title": "XPath Injection"
+ },
+ "expression-lang-injection": {
+ "title": "Expression Language Injection"
+ },
+ "log-injection": {
+ "title": "Log Injection"
+ },
+ "xxe": {
+ "title": "XML External Entity (XXE)"
+ },
+ "xss": {
+ "title": "Cross-Site Scripting (XSS)"
+ },
+ "dos": {
+ "title": "Denial of Service (DoS)"
+ },
+ "ssrf": {
+ "title": "Server-Side Request Forgery (SSRF)"
+ },
+ "csrf": {
+ "title": "Cross-Site Request Forgery (CSRF)"
+ },
+ "http-response-splitting": {
+ "title": "HTTP Response Splitting"
+ },
+ "open-redirect": {
+ "title": "Open Redirect"
+ },
+ "weak-cryptography": {
+ "title": "Weak Cryptography"
+ },
+ "auth": {
+ "title": "Authentication"
+ },
+ "insecure-conf": {
+ "title": "Insecure Configuration"
+ },
+ "file-manipulation": {
+ "title": "File Manipulation"
+ },
+ "others": {
+ "title": "Others"
}
}
}