Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
382e242
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
9fd89b5
chore: resolve merge conflicts with upstream main
PRAteek-singHWY Dec 26, 2025
b3739c6
fix(header): restore sr-only utility for accessibility
PRAteek-singHWY Dec 26, 2025
bed311d
chore(osib): update OWASP name to Open Worldwide Application Security…
PRAteek-singHWY Jan 3, 2026
408761a
feat(myopencre): enable CSV download of all CREs
PRAteek-singHWY Dec 17, 2025
eb8c60b
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
43e535d
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
47d3535
chore: resolve merge conflicts with upstream main
PRAteek-singHWY Dec 26, 2025
2039adf
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 15, 2025
3c0ab5b
feat(myopencre): enable CSV download of all CREs
PRAteek-singHWY Dec 17, 2025
524fa5a
feat(myopencre): add CSV upload UI and wire to existing import endpoint
PRAteek-singHWY Dec 18, 2025
49b0ea2
fix: correct SCSS block after merge conflict
PRAteek-singHWY Dec 26, 2025
0bbe9b6
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
5b64e51
chore: resolve merge conflicts with upstream main
PRAteek-singHWY Dec 26, 2025
5d9342c
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
b10b322
feat(myopencre): add export-compatible CSV import validation
PRAteek-singHWY Dec 28, 2025
5ceafef
refactor(csv-import): move CSV validation into spreadsheet parser
PRAteek-singHWY Jan 4, 2026
3c9a6e4
refactor(csv): move import validation to spreadsheet parser entry point
PRAteek-singHWY Jan 4, 2026
67bff0d
style(csv): format spreadsheet parser with black
PRAteek-singHWY Jan 4, 2026
e013cad
chore: resolve merge conflicts with upstream main
PRAteek-singHWY Dec 26, 2025
2c299a9
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
f2805e1
refactor(csv-import): move CSV validation into spreadsheet parser
PRAteek-singHWY Jan 4, 2026
de4e690
feat(myopencre): surface CSV import errors in UI
PRAteek-singHWY Dec 28, 2025
b1d86ae
refactor(csv-import): move validation into spreadsheet parser and fix…
PRAteek-singHWY Jan 4, 2026
d3de493
style(csv): format files with black
PRAteek-singHWY Jan 4, 2026
4b2dc14
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
9536ca8
chore: resolve merge conflicts with upstream main
PRAteek-singHWY Dec 26, 2025
4ec04da
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
48715ca
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
a5a0ad0
feat(myopencre): surface CSV import errors in UI
PRAteek-singHWY Dec 28, 2025
a3f5538
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
608c1c3
refactor(csv-import): move CSV validation into spreadsheet parser
PRAteek-singHWY Jan 4, 2026
b6f6c73
style(myopencre): move container spacing to SCSS
PRAteek-singHWY Dec 31, 2025
3ae4f5c
refactor(myopencre): move CSV validation to parser and add noop impor…
PRAteek-singHWY Jan 4, 2026
cde7db2
style(csv): format files with black
PRAteek-singHWY Jan 4, 2026
c521d84
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
4a94aa7
chore: resolve merge conflicts with upstream main
PRAteek-singHWY Dec 26, 2025
bd39097
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
7b9433b
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
ab06088
style(myopencre): move container spacing to SCSS
PRAteek-singHWY Dec 31, 2025
9f0d78a
refactor(myopencre): move CSV validation to parser and add noop impor…
PRAteek-singHWY Jan 4, 2026
0183ca7
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
c9776fe
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
599f1dd
feat(myopencre): surface CSV import errors in UI
PRAteek-singHWY Dec 28, 2025
ac5b89a
refactor(myopencre): move CSV validation to parser and add noop impor…
PRAteek-singHWY Jan 4, 2026
6184d70
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
a7b9aa6
refactor(csv-import): move CSV validation into spreadsheet parser
PRAteek-singHWY Jan 4, 2026
34370e5
feat(myopencre): improve UI handling for no-op CSV imports
PRAteek-singHWY Dec 29, 2025
ec8826d
feat(myopencre): add CSV import preview and confirmation flow
PRAteek-singHWY Dec 31, 2025
8022557
ui(myopencre): adjust header spacing and clean up MyOpenCRE styles
PRAteek-singHWY Jan 4, 2026
f5bdac2
chore(myopencre): drop changes that belong to other stacked branches
PRAteek-singHWY Jan 4, 2026
27d3ec1
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
31c1c3a
chore: resolve merge conflicts with upstream main
PRAteek-singHWY Dec 26, 2025
12236c3
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
79f80fe
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
9f9c547
style(myopencre): move container spacing to SCSS
PRAteek-singHWY Dec 31, 2025
686b74d
refactor(myopencre): move CSV validation to parser and add noop impor…
PRAteek-singHWY Jan 4, 2026
220e4f8
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
f884332
feat(myopencre): improve UI handling for no-op CSV imports
PRAteek-singHWY Dec 29, 2025
f1ce405
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
09906ec
feat(myopencre): surface CSV import errors in UI
PRAteek-singHWY Dec 28, 2025
071d074
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
d9bcb8d
feat(myopencre): surface CSV import errors in UI
PRAteek-singHWY Dec 28, 2025
f2d078c
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
274db9c
feat(myopencre): surface CSV import errors in UI
PRAteek-singHWY Dec 28, 2025
5394be0
feat(myopencre): improve UI handling for no-op CSV imports
PRAteek-singHWY Dec 29, 2025
b367e9c
feat(myopencre): add initial MyOpenCRE page and CSV template download
PRAteek-singHWY Dec 17, 2025
a2b5786
feat(myopencre): improve UI handling for no-op CSV imports
PRAteek-singHWY Dec 29, 2025
8249447
feat(myopencre): add CSV import preview and confirmation flow
PRAteek-singHWY Dec 31, 2025
09abd8c
ui(myopencre): add inline help and clarify CSV preparation
PRAteek-singHWY Jan 2, 2026
095fa02
refactor(myopencre): move CSV validation to parser and detect noop im…
PRAteek-singHWY Jan 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.11
3.11.9
2 changes: 1 addition & 1 deletion application/defs/osib_defs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)
}
Expand Down
26 changes: 26 additions & 0 deletions application/frontend/src/pages/MyOpenCRE/MyOpenCRE.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.myopencre-container {
margin-top: 3rem;
}

.myopencre-section {
margin-top: 2rem;
}

.myopencre-upload {
margin-top: 1.5rem;
}

.myopencre-disabled {
opacity: 0.7;
}
.myopencre-preview {
margin-bottom: 1rem;
}
.myopencre-intro {
font-size: 1.05rem;
font-weight: 400;
margin-bottom: 0.5rem;
}
.cursor-pointer summary {
cursor: pointer;
}
302 changes: 292 additions & 10 deletions application/frontend/src/pages/MyOpenCRE/MyOpenCRE.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,59 @@
import React from 'react';
import { Button, Container, Header } from 'semantic-ui-react';
import './MyOpenCRE.scss';

import React, { useRef, useState } from 'react';
import { Button, Container, Form, Header, Message } from 'semantic-ui-react';

import { useEnvironment } from '../../hooks';

export const MyOpenCRE = () => {
const { apiUrl } = useEnvironment();
const isUploadEnabled = apiUrl !== '/rest/v1';

const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<ImportErrorResponse | null>(null);
const [success, setSuccess] = useState<any | null>(null);

const [preview, setPreview] = useState<{
rows: number;
creMappings: number;
uniqueSections: number;
creColumns: string[];
} | null>(null);

const [info, setInfo] = useState<string | null>(null);
const [confirmedImport, setConfirmedImport] = useState(false);

const fileInputRef = useRef<HTMLInputElement | null>(null);

/* ------------------ FILE SELECTION ------------------ */

const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setError(null);
setSuccess(null);
setInfo(null);
setConfirmedImport(false);
setPreview(null);

if (!e.target.files || e.target.files.length === 0) return;

const file = e.target.files[0];

if (!file.name.toLowerCase().endsWith('.csv')) {
setError({
success: false,
type: 'FILE_ERROR',
message: 'Please upload a valid CSV file.',
});
e.target.value = '';
setSelectedFile(null);
return;
}

setSelectedFile(file);
generateCsvPreview(file);
};
/* ------------------ CSV DOWNLOAD ------------------ */

const downloadTemplate = () => {
const headers = ['standard_name', 'standard_section', 'cre_id', 'notes'];
Expand All @@ -24,24 +73,257 @@ export const MyOpenCRE = () => {
link.click();
document.body.removeChild(link);
};
/* ------------------ CSV UPLOAD ------------------ */

const uploadCsv = async () => {
if (!selectedFile || !confirmedImport) return;

setLoading(true);
setError(null);
setSuccess(null);
setInfo(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.'
);
}

const payload = await response.json();

if (!response.ok) {
setError(payload);
setPreview(null);
setConfirmedImport(false);
return;
}

if (payload.import_type === 'noop') {
setInfo(
'Import completed successfully, but no new CREs or standards were added because all mappings already exist.'
);
} else if (payload.import_type === 'empty') {
setInfo('The uploaded CSV did not contain any importable rows. No changes were made.');
} else {
setSuccess(payload);
}

setConfirmedImport(false);
setPreview(null);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
} catch (err: any) {
setError({
success: false,
type: 'CLIENT_ERROR',
message: err.message || 'Unexpected error during import',
});
setPreview(null);
setConfirmedImport(false);
} finally {
setLoading(false);
}
};

/* ------------------ ERROR RENDERING ------------------ */

const renderErrorMessage = () => {
if (!error) return null;

if (error.errors && error.errors.length > 0) {
return (
<Message negative>
<strong>Import failed due to validation errors</strong>
<ul>
{error.errors.map((e, idx) => (
<li key={idx}>
<strong>Row {e.row}:</strong> {e.message}
</li>
))}
</ul>
</Message>
);
}

return <Message negative>{error.message || 'Import failed'}</Message>;
};

/* ------------------ CSV PREVIEW ------------------ */

const generateCsvPreview = async (file: File) => {
const text = await file.text();
const lines = text.split('\n').filter(Boolean);

if (lines.length < 2) {
setPreview(null);
return;
}

const headers = lines[0].split(',').map((h) => h.trim());
const rows = lines.slice(1);

const creColumns = headers.filter((h) => h.startsWith('CRE'));
let creMappings = 0;
const sectionSet = new Set<string>();

rows.forEach((line) => {
const values = line.split(',');
const rowObj: Record<string, string> = {};

headers.forEach((h, i) => {
rowObj[h] = (values[i] || '').trim();
});

const name = (rowObj['standard|name'] || '').trim();
const id = (rowObj['standard|id'] || '').trim();

if (name || id) {
sectionSet.add(`${name}|${id}`);
}

creColumns.forEach((col) => {
if (rowObj[col]) creMappings += 1;
});
});

setPreview({
rows: rows.length,
creMappings,
uniqueSections: sectionSet.size,
creColumns,
});
};

/* ------------------ UI ------------------ */

return (
<Container style={{ marginTop: '3rem' }}>
<Container className="myopencre-container">
<Header as="h1">MyOpenCRE</Header>

<p>
<p className="myopencre-intro">
MyOpenCRE allows you to map your own security standard (e.g. SOC2) to OpenCRE Common Requirements
using a CSV spreadsheet.
</p>

<p>
Start by downloading the mapping template below, fill it with your standard’s controls, and map them
to CRE IDs.
<p className="myopencre-intro">
Start by downloading the CRE catalogue below, then map your standard’s controls or sections to CRE IDs
in the spreadsheet.
</p>
<div className="myopencre-section">
<Button primary onClick={downloadCreCsv}>
Download CRE Catalogue (CSV)
</Button>
</div>

<div className="myopencre-section myopencre-upload">
<Header as="h3">Upload Mapping CSV</Header>
<Message info className="cursor-pointer">
<details>
<summary>
<strong>How to prepare your CSV</strong>
</summary>

<ul>
<li>Start from the downloaded CRE Catalogue CSV.</li>
<li>
Fill <code>standard|name</code> and <code>standard|id</code> for your standard.
</li>
<li>
Map your controls using CRE columns (<code>CRE 0</code>, <code>CRE 1</code>, …).
</li>

<li>
CRE values must be in the format <code>&lt;CRE-ID&gt;|&lt;Name&gt;</code>
<br />
<em>Example:</em> <code>616-305|Development processes for security</code>
</li>
</ul>
</details>
</Message>
{renderErrorMessage()}
{info && <Message info>{info}</Message>}
{success && (
<Message positive>
<strong>Import successful</strong>
<ul>
<li>New CREs added: {success.new_cres?.length ?? 0}</li>
<li>Standards imported: {success.new_standards}</li>
</ul>
</Message>
)}

{confirmedImport && !loading && !success && !error && (
<Message positive>
CSV validated successfully. Click <strong>Upload CSV</strong> to start importing.
</Message>
)}

{preview && (
<Message info className="myopencre-preview">
<strong>Import Preview</strong>
<ul>
<li>Rows detected: {preview.rows}</li>
<li>CRE mappings found: {preview.creMappings}</li>
<li>Unique standard sections: {preview.uniqueSections}</li>
<li>CRE columns detected: {preview.creColumns.join(', ')}</li>
</ul>

<Button
primary
size="small"
onClick={() => {
setPreview(null);
setConfirmedImport(true);
}}
>
Confirm Import
</Button>

<Button
size="small"
onClick={() => {
setPreview(null);
setConfirmedImport(false);
setSelectedFile(null);
if (fileInputRef.current) fileInputRef.current.value = '';
}}
>
Cancel
</Button>
</Message>
)}

<Form>
<Form.Field>
<input
ref={fileInputRef}
type="file"
accept=".csv"
disabled={!isUploadEnabled || loading || !!preview}
onChange={onFileChange}
/>
</Form.Field>

<Button primary onClick={downloadTemplate}>
Download Mapping Template (CSV)
</Button>
<Button
primary
loading={loading}
disabled={!isUploadEnabled || !selectedFile || !confirmedImport || loading}
onClick={uploadCsv}
>
Upload CSV
</Button>
</Form>
</div>
</Container>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@
color: var(--muted-foreground);
height: 1rem;
width: 1rem;
pointer-events: none;
z-index: 1;
}

input {
padding: 0.5rem 1rem 0.5rem 2.5rem;
width: 16rem;
Expand Down
Loading