diff --git a/application/controllers/AuditController.php b/application/controllers/AuditController.php index 37a7048..73703b1 100644 --- a/application/controllers/AuditController.php +++ b/application/controllers/AuditController.php @@ -64,6 +64,14 @@ class AuditController extends BaseController $this->jsonError('File troppo grande (max 10MB)', 400, 'FILE_TOO_LARGE'); } + // Allowlist estensioni (anti stored-XSS same-origin: niente html/svg/js eseguibili). + // Allineata a StakeholderPortalController::ALLOWED_EXT. + $allowedExt = ['pdf','png','jpg','jpeg','gif','webp','txt','csv','xlsx','xls','docx','doc','pptx','ppt','odt','ods','zip']; + $extCheck = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); + if ($extCheck === '' || !in_array($extCheck, $allowedExt, true)) { + $this->jsonError('Tipo di file non consentito. Formati ammessi: PDF, immagini, documenti Office, txt, csv, zip.', 422, 'BAD_FILE_TYPE'); + } + $orgId = $this->getCurrentOrgId(); $uploadDir = UPLOAD_PATH . "/evidence/{$orgId}"; diff --git a/public/policies.html b/public/policies.html index 4d42bc0..46a7376 100644 --- a/public/policies.html +++ b/public/policies.html @@ -627,7 +627,29 @@ + +
Caricamento allegati...
+Formati ammessi: PDF, immagini, documenti Office, txt, csv, zip. Dimensione massima 10 MB.
+Nessun allegato. Carica un documento di supporto alla policy.
'; + return; + } + box.innerHTML = files.map(f => ` +Errore nel caricamento degli allegati.
'; + } + } + + async function caricaAllegato(policyId) { + const input = document.getElementById('policy-att-file-' + policyId); + if (!input || !input.files || !input.files.length) { + showNotification('Seleziona un file da allegare.', 'warning'); + return; + } + const file = input.files[0]; + const ext = (file.name.split('.').pop() || '').toLowerCase(); + if (!ATT_ALLOWED_EXT.includes(ext)) { + showNotification('Tipo di file non consentito. Formati ammessi: PDF, immagini, documenti Office, txt, csv, zip.', 'error'); + return; + } + if (file.size > 10 * 1024 * 1024) { + showNotification('File troppo grande (max 10 MB).', 'error'); + return; + } + const btn = input.parentElement.querySelector('.btn-primary'); + setButtonLoading(btn, true); + try { + const fd = new FormData(); + fd.append('file', file); + fd.append('entity_type', 'policy'); + fd.append('entity_id', policyId); + const headers = { 'Authorization': 'Bearer ' + api.token }; + if (api.orgId) headers['X-Organization-Id'] = api.orgId; + const resp = await fetch(api.baseUrl + '/audit/evidence/upload', { method: 'POST', headers, body: fd }); + const result = await resp.json(); + setButtonLoading(btn, false); + if (result && result.success) { + showNotification('Allegato caricato con successo.', 'success'); + input.value = ''; + loadPolicyAttachments(policyId); + } else { + showNotification((result && result.message) || 'Errore nel caricamento dell\'allegato.', 'error'); + } + } catch (e) { + setButtonLoading(btn, false); + showNotification('Errore di connessione durante il caricamento.', 'error'); + } + } + // ── Create / Edit Modal ───────────────────────────────── function openCreateModal() { showPolicyFormModal(null); diff --git a/public/version.json b/public/version.json index eacbdf0..916f692 100644 --- a/public/version.json +++ b/public/version.json @@ -1 +1 @@ -{"version": "1.23.4", "build": "2026-06-18-v1.23.4", "date": "2026-06-18", "changelog": "Ticket #384 punto 2.1 (ricodifica): nella pagina 'Misure e Requisiti' i codici delle policy/procedure di default ora si presentano con il prefisso 'Policy.' al posto di 'Proc.' (es. Proc.01 -> Policy.01), in coerenza con l'unificazione lessicale Policy/Procedura del punto 1. Cambia solo l'etichetta mostrata a video: il codice canonico nel catalogo resta invariato come chiave di collegamento requisito<->policy. Restano in lavorazione a fasi: unificazione archivi M:N requisito<->policy (punto 2.2), link attivi bidirezionali tra i due menu (punto 3), allegati nel modale policy (punto 5), matrice RACI assegnabile per policy con R e A obbligatorie in approvazione (punto 6)."} +{"version": "1.23.5", "build": "2026-06-18-v1.23.5", "date": "2026-06-18", "changelog": "Ticket #384 punto 5 (allegati Policy): nel dettaglio di ogni Policy ora c'e' la sezione 'Allegati' per caricare documenti di supporto (PDF, immagini, Office, txt, csv, zip; max 10 MB), elencarli con autore/data e scaricarli. Hardening sicurezza: aggiunta allowlist server-side delle estensioni sull'endpoint di upload evidenze (rifiuto html/svg/js, anti stored-XSS same-origin). Restano in lavorazione a fasi: unificazione archivi M:N requisito<->policy (punto 2.2), link attivi bidirezionali tra i due menu (punto 3), matrice RACI assegnabile per policy con R e A obbligatorie in approvazione (punto 6)."}