diff --git a/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.scss b/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.tsx b/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.tsx
new file mode 100644
index 00000000..4a3de6a2
--- /dev/null
+++ b/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.tsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import { Button, Container, Header } from 'semantic-ui-react';
+
+import { useEnvironment } from '../../hooks';
+
+export const MyOpenCRE = () => {
+ const { apiUrl } = useEnvironment();
+
+ const downloadTemplate = () => {
+ const headers = ['standard_name', 'standard_section', 'cre_id', 'notes'];
+
+ const csvContent = headers.join(',') + '\n';
+
+ const blob = new Blob([csvContent], {
+ type: 'text/csv;charset=utf-8;',
+ });
+
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+
+ link.href = url;
+ link.setAttribute('download', 'myopencre_mapping_template.csv');
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ };
+
+ return (
+
+
+
+
+ MyOpenCRE allows you to map your own security standard (e.g. SOC2) to OpenCRE Common Requirements
+ using a CSV spreadsheet.
+
+
+
+ Start by downloading the mapping template below, fill it with your standard’s controls, and map them
+ to CRE IDs.
+
+
+
+
+ );
+};
diff --git a/application/frontend/src/routes.tsx b/application/frontend/src/routes.tsx
index da9c507e..5d18ba09 100644
--- a/application/frontend/src/routes.tsx
+++ b/application/frontend/src/routes.tsx
@@ -21,6 +21,7 @@ import { ExplorerCircles } from './pages/Explorer/visuals/circles/circles';
import { ExplorerForceGraph } from './pages/Explorer/visuals/force-graph/forceGraph';
import { GapAnalysis } from './pages/GapAnalysis/GapAnalysis';
import { MembershipRequired } from './pages/MembershipRequired/MembershipRequired';
+import { MyOpenCRE } from './pages/MyOpenCRE/MyOpenCRE';
import { SearchName } from './pages/Search/SearchName';
import { StandardSection } from './pages/Standard/StandardSection';
@@ -31,6 +32,12 @@ export interface IRoute {
}
export const ROUTES: IRoute[] = [
+ {
+ path: '/myopencre',
+ component: MyOpenCRE,
+ showFilter: false,
+ },
+
{
path: INDEX,
component: SearchPage,
diff --git a/application/frontend/src/scaffolding/Header/Header.tsx b/application/frontend/src/scaffolding/Header/Header.tsx
index 9e653b42..1b36dd72 100644
--- a/application/frontend/src/scaffolding/Header/Header.tsx
+++ b/application/frontend/src/scaffolding/Header/Header.tsx
@@ -8,6 +8,7 @@ import { Button } from 'semantic-ui-react';
import { ClearFilterButton } from '../../components/FilterButton/FilterButton';
import { useLocationFromOutsideRoute } from '../../hooks/useLocationFromOutsideRoute';
+import { MyOpenCRE } from '../../pages/MyOpenCRE/MyOpenCRE';
import { SearchBar } from '../../pages/Search/components/SearchBar';
export const Header = () => {
@@ -68,6 +69,10 @@ export const Header = () => {
Explorer
+
+
+ MyOpenCRE
+
@@ -186,6 +191,15 @@ export const Header = () => {
>
Explorer
+
+
+ MyOpenCRE
+