diff --git a/application/defs/osib_defs.py b/application/defs/osib_defs.py
index 89c204704..71e6c219e 100644
--- a/application/defs/osib_defs.py
+++ b/application/defs/osib_defs.py
@@ -347,7 +347,7 @@ def paths_to_osib(
attributes=Node_attributes(
sources_i18n={
Lang("en"): _Source(
- name="Open Worldwide Application Security Project",
+ name="Open Web Application Security Project",
source="https://owasp.org",
)
}
diff --git a/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.scss b/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.scss
index e69de29bb..8e29bb2ce 100644
--- a/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.scss
+++ b/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.scss
@@ -0,0 +1,11 @@
+.myopencre-section {
+ margin-top: 2rem;
+}
+
+.myopencre-upload {
+ margin-top: 1.5rem;
+}
+
+.myopencre-disabled {
+ opacity: 0.7;
+}
diff --git a/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.tsx b/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.tsx
index 68c1fb056..d433b617d 100644
--- a/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.tsx
+++ b/application/frontend/src/pages/MyOpenCRE/MyOpenCRE.tsx
@@ -1,23 +1,28 @@
-import React from 'react';
-import { Button, Container, Header } from 'semantic-ui-react';
+import './MyOpenCRE.scss';
+
+import React, { useState } from 'react';
+import { Button, Container, Form, Header, Message } from 'semantic-ui-react';
import { useEnvironment } from '../../hooks';
export const MyOpenCRE = () => {
const { apiUrl } = useEnvironment();
- // console.log('API URL:', apiUrl);
- const downloadCreCsv = async () => {
- try {
- const baseUrl = apiUrl || window.location.origin;
+ // Upload enabled only for local/dev
+ const isUploadEnabled = apiUrl !== '/rest/v1';
- const backendUrl = baseUrl.includes('localhost') ? 'http://127.0.0.1:5000' : baseUrl;
+ const [selectedFile, setSelectedFile] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [success, setSuccess] = useState(null);
- const response = await fetch(`${backendUrl}/cre_csv`, {
+ /* ------------------ CSV DOWNLOAD ------------------ */
+
+ const downloadCreCsv = async () => {
+ try {
+ const response = await fetch(`${apiUrl}/cre_csv`, {
method: 'GET',
- headers: {
- Accept: 'text/csv',
- },
+ headers: { Accept: 'text/csv' },
});
if (!response.ok) {
@@ -41,6 +46,67 @@ export const MyOpenCRE = () => {
}
};
+ /* ------------------ FILE SELECTION ------------------ */
+
+ const onFileChange = (e: React.ChangeEvent) => {
+ setError(null);
+ setSuccess(null);
+
+ if (!e.target.files || e.target.files.length === 0) return;
+
+ const file = e.target.files[0];
+
+ if (!file.name.toLowerCase().endsWith('.csv')) {
+ setError('Please upload a valid CSV file.');
+ e.target.value = '';
+ setSelectedFile(null);
+ return;
+ }
+
+ setSelectedFile(file);
+ };
+
+ /* ------------------ CSV UPLOAD ------------------ */
+
+ const uploadCsv = async () => {
+ if (!selectedFile) return;
+
+ setLoading(true);
+ setError(null);
+ setSuccess(null);
+
+ const formData = new FormData();
+ formData.append('cre_csv', selectedFile);
+
+ try {
+ const response = await fetch(`${apiUrl}/cre_csv_import`, {
+ method: 'POST',
+ body: formData,
+ });
+
+ if (response.status === 403) {
+ throw new Error(
+ 'CSV import is disabled on hosted environments. Run OpenCRE locally with CRE_ALLOW_IMPORT=true.'
+ );
+ }
+
+ if (!response.ok) {
+ const text = await response.text();
+ throw new Error(text || 'CSV import failed');
+ }
+
+ const result = await response.json();
+ setSuccess(result);
+ setSelectedFile(null);
+ } catch (err: any) {
+ setError(err.message || 'Unexpected error during import');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ /* ------------------ UI ------------------ */
+
return (
@@ -51,15 +117,56 @@ export const MyOpenCRE = () => {
- Start by downloading the mapping template below, fill it with your standard’s controls, and map them
- to CRE IDs.
+ Start by downloading the CRE catalogue below, then map your standard’s controls or sections to CRE IDs
+ in the spreadsheet.
+
+
+ Download CRE Catalogue (CSV)
+
+
+
+
+
+
+
Upload your completed mapping spreadsheet to import your standard into OpenCRE.
+
+ {!isUploadEnabled && (
+
+ CSV upload is disabled on hosted environments due to resource constraints.
+
+ Please run OpenCRE locally to enable standard imports.
+
+ )}
+
+ {error &&
{error} }
+
+ {success && (
+
+ Import successful
+
+ New CREs added: {success.new_cres?.length ?? 0}
+ Standards imported: {success.new_standards}
+
+
+ )}
-
- Download CRE Catalogue (CSV)
+
+
+
-
+
+ Upload CSV
+
+
+
);
};
diff --git a/application/frontend/src/scaffolding/Header/Header.tsx b/application/frontend/src/scaffolding/Header/Header.tsx
index 1b36dd721..f08b6d7dc 100644
--- a/application/frontend/src/scaffolding/Header/Header.tsx
+++ b/application/frontend/src/scaffolding/Header/Header.tsx
@@ -68,11 +68,11 @@ export const Header = () => {
Explorer
-
-
-
+
+
MyOpenCRE
+
@@ -190,16 +190,10 @@ export const Header = () => {
onClick={closeMobileMenu}
>
Explorer
-
-
-
+
+
MyOpenCRE
-
+
diff --git a/application/frontend/src/scaffolding/Header/header.scss b/application/frontend/src/scaffolding/Header/header.scss
index 9d43a50fe..50cf72531 100644
--- a/application/frontend/src/scaffolding/Header/header.scss
+++ b/application/frontend/src/scaffolding/Header/header.scss
@@ -322,14 +322,3 @@
display: block;
}
}
-.sr-only {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- white-space: nowrap;
- border: 0;
-}
diff --git a/application/tests/data/osib_example.yml b/application/tests/data/osib_example.yml
index 89a6d18eb..d91a739e3 100644
--- a/application/tests/data/osib_example.yml
+++ b/application/tests/data/osib_example.yml
@@ -4,11 +4,11 @@ schema: v0.90
date: 2021/11/04
children:
"OWASP":
- aliases: ["Open Worldwide Application Security Project", "owasp"]
+ aliases: ["Open Web Application Security Project", "owasp"]
attributes:
sources_i18n:
en:
- name: "Open Worldwide Application Security Project® (OWASP)"
+ name: "Open Web Application Security Project® (OWASP)"
children:
"ZAP":
attributes:
diff --git a/application/tests/data/osib_example.yml.bak b/application/tests/data/osib_example.yml.bak
index c6cf165fe..1dfb985c0 100644
--- a/application/tests/data/osib_example.yml.bak
+++ b/application/tests/data/osib_example.yml.bak
@@ -9,7 +9,7 @@ children:
attributes:
sources_i18n:
en:
- name: "Open Worldwide Application Security Project® (OWASP)"
+ name: "Open Web Application Security Project® (OWASP)"
children:
1:
alias: "asvs"
diff --git a/application/tests/osib_defs_test.py b/application/tests/osib_defs_test.py
index d74fd0c00..cae8f50a3 100644
--- a/application/tests/osib_defs_test.py
+++ b/application/tests/osib_defs_test.py
@@ -263,7 +263,7 @@
# attributes=Node_attributes(
# sources_i18n={
# Lang("en"): _Source(
-# name="Open Worldwide Application Security Project",
+# name="Open Web Application Security Project",
# source="https://owasp.org",
# )
# }
@@ -326,7 +326,7 @@
# attributes=defs.Node_attributes(
# sources_i18n={
# defs.Lang("en"): defs._Source(
-# name="Open Worldwide Application Security Project",
+# name="Open Web Application Security Project",
# source="https://owasp.org",
# )
# }