]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11983 Create a new 'SonarSource' security report page
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Wed, 17 Apr 2019 13:33:26 +0000 (15:33 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 7 May 2019 07:54:27 +0000 (09:54 +0200)
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap
server/sonar-web/src/main/js/apps/securityReports/components/App.tsx
server/sonar-web/src/main/js/apps/securityReports/components/VulnerabilityList.tsx
server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx
server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap
server/sonar-web/src/main/js/apps/securityReports/utils.ts
server/sonar-web/src/main/js/helpers/standards.json
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 1c59dad23387427fef77cda16ec0a8e2caf7453b..4f779bf27ac2e3c096681e27de01e903f226fc34 100644 (file)
@@ -157,6 +157,16 @@ export class ComponentNavMenu extends React.PureComponent<Props> {
   renderSecurityReportsLink() {
     return (
       <ul className="menu">
+        <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"
index 221e7dd6f26ad9778496a4319fee1b7cccc859d6..34ca59e182e63ca92e185cd442e49b8b628e5577 100644 (file)
@@ -43,6 +43,23 @@ exports[`should work for all qualifiers 1`] = `
       <ul
         className="menu"
       >
+        <li>
+          <Link
+            activeClassName="active"
+            onlyActiveOnIndex={false}
+            style={Object {}}
+            to={
+              Object {
+                "pathname": "/project/security_reports/sonarsource_security",
+                "query": Object {
+                  "id": "foo",
+                },
+              }
+            }
+          >
+            security_reports.sonarsourceSecurity.page
+          </Link>
+        </li>
         <li>
           <Link
             activeClassName="active"
@@ -260,6 +277,23 @@ exports[`should work for all qualifiers 2`] = `
       <ul
         className="menu"
       >
+        <li>
+          <Link
+            activeClassName="active"
+            onlyActiveOnIndex={false}
+            style={Object {}}
+            to={
+              Object {
+                "pathname": "/project/security_reports/sonarsource_security",
+                "query": Object {
+                  "id": "foo",
+                },
+              }
+            }
+          >
+            security_reports.sonarsourceSecurity.page
+          </Link>
+        </li>
         <li>
           <Link
             activeClassName="active"
@@ -426,6 +460,23 @@ exports[`should work for all qualifiers 3`] = `
       <ul
         className="menu"
       >
+        <li>
+          <Link
+            activeClassName="active"
+            onlyActiveOnIndex={false}
+            style={Object {}}
+            to={
+              Object {
+                "pathname": "/project/security_reports/sonarsource_security",
+                "query": Object {
+                  "id": "foo",
+                },
+              }
+            }
+          >
+            security_reports.sonarsourceSecurity.page
+          </Link>
+        </li>
         <li>
           <Link
             activeClassName="active"
@@ -563,6 +614,23 @@ exports[`should work for all qualifiers 4`] = `
       <ul
         className="menu"
       >
+        <li>
+          <Link
+            activeClassName="active"
+            onlyActiveOnIndex={false}
+            style={Object {}}
+            to={
+              Object {
+                "pathname": "/project/security_reports/sonarsource_security",
+                "query": Object {
+                  "id": "foo",
+                },
+              }
+            }
+          >
+            security_reports.sonarsourceSecurity.page
+          </Link>
+        </li>
         <li>
           <Link
             activeClassName="active"
@@ -731,6 +799,24 @@ exports[`should work for long-living branches 1`] = `
       <ul
         className="menu"
       >
+        <li>
+          <Link
+            activeClassName="active"
+            onlyActiveOnIndex={false}
+            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"
@@ -875,6 +961,24 @@ exports[`should work for long-living branches 2`] = `
       <ul
         className="menu"
       >
+        <li>
+          <Link
+            activeClassName="active"
+            onlyActiveOnIndex={false}
+            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"
index 6dc80000947cdbd4fc6e12732a3d74665c396e0f..37c049a0c8663553a1f8373f7a75170aa831d0b6 100755 (executable)
@@ -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 (
index a508d30c1209d7c7a186998fec8ada0ba8e7d414..4d1d4dc6e866978b47fcb6e186b137a4aa96e7bb 100755 (executable)
@@ -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 (
       <>
index 4f43520f7525696c153f5856af1f86848616d66d..12bf3c5b42837e19c2e7f78507b459c8416a3e62 100644 (file)
@@ -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();
+});
index 2884f4f0c57852465fedf37295ecac0aa6d21365..b89aa9c57c4b8935b841f3a2bf6ca9ecec994cee 100644 (file)
@@ -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"
index e53518ab2f427771d2c72a589cb39a0f8f5b0103..604133f285ef773591e659acc2734eaa9d739f8c 100755 (executable)
@@ -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';
+}
index f2d32e1715b3872831f6f2da5ef15123326bb063..051fc01038daf31a33bba574867477dfb3409280 100644 (file)
     },
     "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"
     }
   }
 }
index 8285ddb5d0408c2fe13e23c22711f23286d37e5f..d379b914590397ed290af9718aaeb634fd3280b8 100644 (file)
@@ -2092,8 +2092,10 @@ organizations_permissions.provisioning.desc=Ability to initialize a project so i
 security_reports.more_rules=Additional security-related rules are available but not active in your profiles.
 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 (25 CWE items in three categories).
+security_reports.sonarsourceSecurity.page=SonarSource Security
+security_reports.owaspTop10.description=Track Vulnerabilities and Security Hotspots corresponding to OWASP Top 10 standard.
+security_reports.sansTop25.description=Track Vulnerabilities and Security Hotspots corresponding to SANS Top 25 standard (25 CWE items in three categories).
+security_reports.sonarsourceSecurity.description=Track Vulnerabilities and Security Hotspots corresponding to SonarSource Security categories.
 security_reports.list.categories=Categories
 security_reports.list.vulnerabilities=Vulnerabilities
 security_reports.list.security_hotspots=Security Hotspots