From e0be95f5315010292b1f16a13002c14c560a8bdf Mon Sep 17 00:00:00 2001 From: Hystepik Date: Wed, 29 Jan 2025 10:17:18 +0100 Subject: [PATCH 01/23] New westite security constants --- htdocs/core/website.inc.php | 16 +-- htdocs/website/index.php | 198 ++++++++++++++++++++++++++++- htdocs/website/lib/website.lib.php | 172 +++++++++++++++++++++++++ 3 files changed, 372 insertions(+), 14 deletions(-) create mode 100644 htdocs/website/lib/website.lib.php diff --git a/htdocs/core/website.inc.php b/htdocs/core/website.inc.php index e28082e208fb3..14194ca78fd0c 100644 --- a/htdocs/core/website.inc.php +++ b/htdocs/core/website.inc.php @@ -167,7 +167,7 @@ } // Content-Security-Policy - if (!defined('WEBSITE_MAIN_SECURITY_FORCECSP')) { + if (!defined('WEBSITE_'.$object->id.'_SECURITY_FORCECSP')) { // A default security policy that keep usage of js external component like ckeditor, stripe, google, working // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube) // default-src 'self'; style-src: https://cdnjs.cloudflare.com https://fonts.googleapis.com; script-src: https://cdn.transifex.com https://www.googletagmanager.com; object-src https://youtube.com; frame-src https://youtube.com; img-src: *; @@ -178,7 +178,7 @@ // // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src 'self' 'unsafe-inline' 'unsafe-eval' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com;"; // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src *; script-src 'self' 'unsafe-inline' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com; style-src 'self' 'unsafe-inline'; connect-src 'self';"; - $contentsecuritypolicy = getDolGlobalString('WEBSITE_MAIN_SECURITY_FORCECSP'); + $contentsecuritypolicy = getDolGlobalString('WEBSITE_'.$object->id.'_SECURITY_FORCECSP'); if (!is_object($hookmanager)) { include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; @@ -200,32 +200,32 @@ } // Referrer-Policy - if (!defined('WEBSITE_MAIN_SECURITY_FORCERP')) { + if (!defined('WEBSITE_'.$object->id.'_SECURITY_FORCERP')) { // The constant WEBSITE_MAIN_SECURITY_FORCERP should never be defined by page, but the variable used just after may be // For public web sites, we use the same default value than "strict-origin-when-cross-origin" - $referrerpolicy = getDolGlobalString('WEBSITE_MAIN_SECURITY_FORCERP', "strict-origin-when-cross-origin"); + $referrerpolicy = getDolGlobalString('WEBSITE_'.$object->id.'_SECURITY_FORCERP', "strict-origin-when-cross-origin"); header("Referrer-Policy: ".$referrerpolicy); } // Strict-Transport-Security - if (!defined('WEBSITE_MAIN_SECURITY_FORCESTS')) { + if (!defined('WEBSITE_'.$object->id.'_SECURITY_FORCESTS')) { // The constant WEBSITE_MAIN_SECURITY_FORCESTS should never be defined by page, but the variable used just after may be // Example: "max-age=31536000; includeSubDomains" - $sts = getDolGlobalString('WEBSITE_MAIN_SECURITY_FORCESTS'); + $sts = getDolGlobalString('WEBSITE_'.$object->id.'_SECURITY_FORCESTS'); if (!empty($sts)) { header("Strict-Transport-Security: ".$sts); } } // Permissions-Policy (old name was Feature-Policy) - if (!defined('WEBSITE_MAIN_SECURITY_FORCEPP')) { + if (!defined('WEBSITE_'.$object->id.'_SECURITY_FORCEPP')) { // The constant WEBSITE_MAIN_SECURITY_FORCEPP should never be defined by page, but the variable used just after may be // Example: "camera: 'none'; microphone: 'none';" - $pp = getDolGlobalString('WEBSITE_MAIN_SECURITY_FORCEPP'); + $pp = getDolGlobalString('WEBSITE_'.$object->id.'_SECURITY_FORCEPP'); if (!empty($pp)) { header("Permissions-Policy: ".$pp); } diff --git a/htdocs/website/index.php b/htdocs/website/index.php index bb683e00854e3..a6bd9f1260ded 100644 --- a/htdocs/website/index.php +++ b/htdocs/website/index.php @@ -40,6 +40,7 @@ // Load Dolibarr environment require '../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/website/lib/website.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/website.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/website2.lib.php'; @@ -1939,6 +1940,47 @@ } } +if ($action == "updatesecurity" && $usercanedit) { + $db->begin(); + $res1 = $res2 = $res3 = $res4 = 0; + $securityrp = GETPOST('WEBSITE_'.$object->id.'_SECURITY_FORCERP', 'alpha'); + $securitysts = GETPOST('WEBSITE_'.$object->id.'_SECURITY_FORCESTS', 'alpha'); + $securitypp = GETPOST('WEBSITE_'.$object->id.'_SECURITY_FORCEPP', 'alpha'); + $securitysp = GETPOST('WEBSITE_'.$object->id.'_SECURITY_FORCECSP', 'alpha'); + + if (!empty($securityrp)) { + $res1 = dolibarr_set_const($db, 'WEBSITE_'.$object->id.'_SECURITY_FORCERP', $securityrp, 'chaine', 0, '', $conf->entity); + } + if (!empty($securitysts)) { + $res2 = dolibarr_set_const($db, 'WEBSITE_'.$object->id.'_SECURITY_FORCESTS', $securitysts, 'chaine', 0, '', $conf->entity); + } + if (!empty($securitypp)) { + $res3 = dolibarr_set_const($db, 'WEBSITE_'.$object->id.'_SECURITY_FORCEPP', $securitypp, 'chaine', 0, '', $conf->entity); + } + if (!empty($securitysp)) { + $res4 = dolibarr_set_const($db, 'WEBSITE_'.$object->id.'_SECURITY_FORCECSP', $securitysp, 'chaine', 0, '', $conf->entity); + } + + if ($res1 >= 0 && $res2 >= 0 && $res3 >= 0 && $res4 >= 0 ) { + $db->commit(); + setEventMessages($langs->trans("Saved"), null, 'mesgs'); + } else { + $db->rollback(); + setEventMessages($langs->trans("ErrorSavingChanges"), null, 'errors'); + } + + if (!GETPOSTISSET('updateandstay')) { // If we click on "Save And Stay", we don not make the redirect + $action = 'preview'; + if ($backtopage) { + $backtopage = preg_replace('/searchstring=[^&]*/', '', $backtopage); // Clean backtopage url + header("Location: ".$backtopage); + exit; + } + } else { + $action = 'editsecurity'; + } +} + // Update page if ($action == 'setashome' && $usercanedit) { $db->begin(); @@ -2946,6 +2988,9 @@ if ($mode) { print ''; } +if ($action == 'editsecurity') { + print ''; +} print '
'."\n"; @@ -3258,8 +3303,8 @@ print ''; } - if (in_array($action, array('editcss', 'editmenu', 'file_manager', 'replacesiteconfirm')) || in_array($mode, array('replacesite'))) { - if ($action == 'editcss') { + if (in_array($action, array('editcss', 'editmenu', 'file_manager', 'replacesiteconfirm', 'editsecurity')) || in_array($mode, array('replacesite'))) { + if ($action == 'editcss' || $action == 'editsecurity') { // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox // accesskey is for Mac: CTRL + key for all browsers $stringforfirstkey = $langs->trans("KeyboardShortcut"); @@ -3290,8 +3335,8 @@ // Toolbar for pages // - if ($websitekey && $websitekey != '-1' && (!in_array($action, array('editcss', 'editmenu', 'importsite', 'file_manager', 'replacesite', 'replacesiteconfirm'))) && (!in_array($mode, array('replacesite'))) && !$file_manager) { - print '
'."\n"; // Close current websitebar to open a new one + if ($websitekey && $websitekey != '-1' && (!in_array($action, array('editcss', 'editmenu', 'importsite', 'file_manager', 'replacesite', 'replacesiteconfirm', 'editsecurity'))) && (!in_array($mode, array('replacesite'))) && !$file_manager) { + print ''; // Close current websitebar to open a new one print ''; print '
'."\n"; @@ -3754,7 +3799,7 @@ function switchEditorOnline(forceenable) // TODO Add js to save alias like we save virtual host name and use dynamic virtual host for url of id=previewpageext } - if (!in_array($mode, array('replacesite')) && !in_array($action, array('editcss', 'editmenu', 'file_manager', 'replacesiteconfirm', 'createsite', 'createcontainer', 'createfromclone', 'createpagefromclone', 'deletesite'))) { + if (!in_array($mode, array('replacesite')) && !in_array($action, array('editcss', 'editmenu', 'file_manager', 'replacesiteconfirm', 'createsite', 'createcontainer', 'createfromclone', 'createpagefromclone', 'deletesite', 'editsecurity'))) { if ($action == 'editsource' || $action == 'editmeta') { // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox // accesskey is for Mac: CTRL + key for all browsers @@ -3964,7 +4009,8 @@ function switchEditorOnline(forceenable) //$readmecontent.=""; //} - print dol_get_fiche_head(); + $head = websiteconfigPrepareHead($object); + print dol_get_fiche_head($head, 'general', $langs->trans("General"), 0, 'website'); print ''."\n"; print ''; @@ -4161,6 +4207,146 @@ function switchEditorOnline(forceenable) print '
'; } +if ($action == 'editsecurity') { + $selectarraySP = websiteGetContentPolicyDirectives(); + $selectarraySPlevel2 = websiteGetContentPolicySources(); + print '
'; + print '
'; + + $head = websiteconfigPrepareHead($object); + print dol_get_fiche_head($head, 'security', $langs->trans("General"), 0, 'website'); + + print '
'; + print '
'; + print ''; + print ''; + print ''."\n"; + print ''; + + // Force RP + print ''; + print ''; + print ''; + print ''; + // Force STS + print ''; + print ''; + print ''; + print ''; + // Force PP + print ''; + print ''; + print ''; + print ''; + print '
'.$langs->trans("Parameter").'
'.$langs->trans('WebsiteSecurityForceRP').'id."_SECURITY_FORCERP").'">
'.$langs->trans('WebsiteSecurityForceSTS').'id."_SECURITY_FORCESTS").'">
'.$langs->trans('WebsiteSecurityForcePP').'id."_SECURITY_FORCEPP").'">
'; + print '
'; + + // Security Policy + print '
'; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print '
'.$langs->trans("SecurityPolicy").'
'.$langs->trans("Value").':id."_SECURITY_FORCECSP").'">
'.$form->selectarray("select_identifier_WEBSITE_SECURITY_FORCECSP", $selectarraySP, "select_identifier_WEBSITE_SECURITY_FORCECSP", 1, 0, 0, '', 0, 0, 0, '', 'minwidth300').''; + foreach ($selectarraySPlevel2 as $key => $values) { + print ''; + } + print '
'; + print '
'; + + + // TODO: liste sous page identifier pour supprimer + print ''; + + print dol_get_fiche_end(); + print ''; +} + if ($action == 'createsite') { print '
'; diff --git a/htdocs/website/lib/website.lib.php b/htdocs/website/lib/website.lib.php new file mode 100644 index 0000000000000..045334dc77199 --- /dev/null +++ b/htdocs/website/lib/website.lib.php @@ -0,0 +1,172 @@ + + * Copyright (C) 2024 Frédéric France + * Copyright (C) 2024 MDW + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/website/lib/website.lib.php + * \ingroup website + * \brief Library files with common functions for WebsiteAccount + */ + + + /** + * Prepare array of tabs for Website + * + * @param Website $object Website + * @return array Array of tabs + */ +function websiteconfigPrepareHead($object) +{ + global $db, $langs, $conf; + + $h = 0; + $head = array(); + + $head[$h][0] = DOL_URL_ROOT.'/website/index.php?websiteid='.$object->id.'&action=editcss'; + $head[$h][1] = $langs->trans("General"); + $head[$h][2] = 'general'; + $h++; + + $head[$h][0] = DOL_URL_ROOT.'/website/index.php?websiteid='.$object->id.'&action=editsecurity'; + $head[$h][1] = $langs->trans("Security"); + $head[$h][2] = 'security'; + $h++; + + /*if (isset($object->fields['note_public']) || isset($object->fields['note_private'])) { + $nbNote = 0; + if(!empty($object->fields['note_private'])) $nbNote++; + if(!empty($object->fields['note_public'])) $nbNote++; + $head[$h][0] = dol_buildpath('/monmodule/websiteaccount_note.php', 1).'?id='.$object->id; + $head[$h][1] = $langs->trans('Notes'); + if ($nbNote > 0) $head[$h][1].= (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) ? ''.$nbNote.'' : ''); + $head[$h][2] = 'note'; + $h++; + }*/ + + /* + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php'; + $upload_dir = $conf->monmodule->dir_output . "/websiteaccount/" . dol_sanitizeFileName($object->ref); + $nbFiles = count(dol_dir_list($upload_dir,'files',0,'','(\.meta|_preview.*\.png)$')); + $nbLinks=Link::count($db, $object->element, $object->id); + $head[$h][0] = dol_buildpath("/monmodule/websiteaccount_document.php", 1).'?id='.$object->id; + $head[$h][1] = $langs->trans('Documents'); + if (($nbFiles+$nbLinks) > 0) $head[$h][1].= (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) ? ''.($nbFiles+$nbLinks).'' : ''); + $head[$h][2] = 'document'; + $h++; + + $head[$h][0] = dol_buildpath("/monmodule/websiteaccount_agenda.php", 1).'?id='.$object->id; + $head[$h][1] = $langs->trans("Events"); + $head[$h][2] = 'agenda'; + $h++; + */ + + // Show more tabs from modules + // Entries must be declared in modules descriptor with line + //$this->tabs = array( + // 'entity:+tabname:Title:@monmodule:/monmodule/mypage.php?id=__ID__' + //); // to add new tab + //$this->tabs = array( + // 'entity:-tabname:Title:@monmodule:/monmodule/mypage.php?id=__ID__' + //); // to remove a tab + complete_head_from_modules($conf, $langs, $object, $head, $h, 'websiteaccount@website'); + + complete_head_from_modules($conf, $langs, $object, $head, $h, 'websiteaccount@website', 'remove'); + + return $head; +} + +/** + * Prepare array of directives for Website + * + * @return array Array of tabs + */ + +function websiteGetContentPolicyDirectives() +{ + return array( + // Fetch directives + "child-src" => array("label" => "child-src","data-directivetype" => "fetch"), + "connect-src" => array("label" => "connect-src","data-directivetype" => "fetch"), + "default-src" => array("label" => "default-src","data-directivetype" => "fetch"), + "fenced-frame-src" => array("label" => "fenced-frame-src","data-directivetype" => "fetch"), + "font-src" => array("label" => "font-src","data-directivetype" => "fetch"), + "frame-src" => array("label" => "frame-src","data-directivetype" => "fetch"), + "img-src" => array("label" => "img-src","data-directivetype" => "fetch"), + "manifest-src" => array("label" => "manifest-src","data-directivetype" => "fetch"), + "media-src" => array("label" => "media-src","data-directivetype" => "fetch"), + "object-src" => array("label" => "object-src","data-directivetype" => "fetch"), + "prefetch-src" => array("label" => "prefetch-src","data-directivetype" => "fetch"), + "script-src" => array("label" => "script-src","data-directivetype" => "fetch"), + "script-src-elem" => array("label" => "script-src-elem","data-directivetype" => "fetch"), + "script-src-attr" => array("label" => "script-src-attr","data-directivetype" => "fetch"), + "style-src" => array("label" => "style-src","data-directivetype" => "fetch"), + "style-src-elem" => array("label" => "style-src-elem","data-directivetype" => "fetch"), + "style-src-attr" => array("label" => "style-src-attr","data-directivetype" => "fetch"), + "worker-src" => array("label" => "worker-src","data-directivetype" => "fetch"), + // Document directives + "base-uri" => array("label" => "base-uri","data-directivetype" => "document"), + "sandbox" => array("label" => "sandbox","data-directivetype" => "document"), + // Navigation directives + "form-action" => array("label" => "form-action","data-directivetype" => "navigation"), + "frame-ancestors" => array("label" => "frame-ancestors","data-directivetype" => "navigation"), + // Reporting directives + "report-to" => array("label" => "report-to","data-directivetype" => "reporting"), + // Other directives + "require-trusted-types-for" => array("label" => "require-trusted-types-for","data-directivetype" => "other"), + "othertrusted-types", + "upgrade-insecure-requests" => array("label" => "upgrade-insecure-requests","data-directivetype" => "other"), + ); +} + +/** + * Prepare array of sources for Website + * + * @return array Array of tabs + */ + +function websiteGetContentPolicySources() +{ + return array( + // Fetch directives + "fetch" => array( + "*", + "data", + "self", + ), + // Document directives + "document" => array( + "base-uri", + "sandbox", + ), + // Navigation directives + "navigation" => array( + "self", + ), + // Reporting directives + "reporting" => array( + "report-to", + ), + // Other directives + "other" => array( + "require-trusted-types-for", + "trusted-types", + "upgrade-insecure-requests", + ), + ); +} From 180954eb16422ff8293f1a1840303e265b362990 Mon Sep 17 00:00:00 2001 From: Hystepik Date: Wed, 29 Jan 2025 10:17:38 +0100 Subject: [PATCH 02/23] fix php typo --- htdocs/website/lib/website.lib.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/htdocs/website/lib/website.lib.php b/htdocs/website/lib/website.lib.php index 045334dc77199..88f4cb834a248 100644 --- a/htdocs/website/lib/website.lib.php +++ b/htdocs/website/lib/website.lib.php @@ -96,7 +96,6 @@ function websiteconfigPrepareHead($object) * * @return array Array of tabs */ - function websiteGetContentPolicyDirectives() { return array( @@ -139,7 +138,6 @@ function websiteGetContentPolicyDirectives() * * @return array Array of tabs */ - function websiteGetContentPolicySources() { return array( From 5fb161260f4950ba5119c720d7dfd9ce492c689b Mon Sep 17 00:00:00 2001 From: Hystepik Date: Fri, 31 Jan 2025 14:42:34 +0100 Subject: [PATCH 03/23] add remove div --- htdocs/website/index.php | 72 +++++++++++++++++++++++++-- htdocs/website/lib/website.lib.php | 79 ++++++++++++++++++++---------- 2 files changed, 120 insertions(+), 31 deletions(-) diff --git a/htdocs/website/index.php b/htdocs/website/index.php index a6bd9f1260ded..8cfc688f3a97e 100644 --- a/htdocs/website/index.php +++ b/htdocs/website/index.php @@ -266,6 +266,8 @@ $filelicense = $pathofwebsite.'/LICENSE'; $filemaster = $pathofwebsite.'/master.inc.php'; +$forceCSP = getDolGlobalString("WEBSITE_".$object->id."_SECURITY_FORCECSP"); + // Define $urlwithroot $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file @@ -1978,6 +1980,7 @@ } } else { $action = 'editsecurity'; + $forceCSP = getDolGlobalString("WEBSITE_".$object->id."_SECURITY_FORCECSP"); } } @@ -2903,6 +2906,47 @@ $action = 'preview'; } +if ($action == 'removecspsource' && $usercanedit) { + $db->begin(); + $sourcecsp = explode("_", GETPOST("sourcecsp")); + $directive = $sourcecsp[0]; + $sourcekey = $sourcecsp[1]; + $forceCSPArr = websiteGetContentPolicyToArray($forceCSP); + if (!isset($directive) || !isset($sourcekey)) { + $error++; + } + + $securityspstring = ""; + if (!$error && !empty($forceCSPArr)) { + if (!empty($forceCSPArr[$directive][$sourcekey])) { + unset($forceCSPArr[$directive][$sourcekey]); + } + if (count($forceCSPArr[$directive]) == 0) { + unset($forceCSPArr[$directive]); + } + foreach ($forceCSPArr as $directive => $sourcekeys) { + if ($securityspstring != ""){ + $securityspstring .= "; "; + } + $securityspstring .= $directive . " " . implode(" ", $sourcekeys); + } + $res = dolibarr_set_const($db, 'WEBSITE_'.$object->id.'_SECURITY_FORCECSP', $securityspstring, 'chaine', 0, '', $conf->entity); + if ($res <= 0) { + $error++; + } + } + + if (!$error) { + $db->commit(); + setEventMessages($langs->trans("SecurityPolicySucesfullyRemoved"), null, 'mesgs'); + } else { + $db->rollback(); + setEventMessages($langs->trans("ErrorRemovingSecurityPolicy"), null, 'errors'); + } + header("Location: ".$_SERVER["PHP_SELF"].'?websiteid='.$websiteid."&action=editsecurity"); + exit(); +} + /* * View @@ -4245,14 +4289,14 @@ function switchEditorOnline(forceenable) print '
'; print ''; print ''; - print ''; + print ''; print ''; print ''; print ''; print ''; @@ -4263,7 +4307,21 @@ function switchEditorOnline(forceenable) print ''; - // TODO: liste sous page identifier pour supprimer + $forceCSPArr = websiteGetContentPolicyToArray($forceCSP); + print '
'; + print '
    '; + foreach ($forceCSPArr as $directive => $sources) { + print '
  • '.$directive.''; + print '
      '; + foreach ($sources as $key => $source) { + print '
    • '.$source.' '.img_delete().'
    • '; + } + print '
    '; + print '
  • '; + } + print '
'; + print '
'; + print ''; diff --git a/htdocs/website/lib/website.lib.php b/htdocs/website/lib/website.lib.php index a55150c6194e6..ba6039683cafe 100644 --- a/htdocs/website/lib/website.lib.php +++ b/htdocs/website/lib/website.lib.php @@ -94,7 +94,7 @@ function websiteconfigPrepareHead($object) /** * Prepare array of directives for Website * - * @return array Array of tabs + * @return array Array of directives */ function websiteGetContentPolicyDirectives() { @@ -128,7 +128,7 @@ function websiteGetContentPolicyDirectives() "report-to" => array("label" => "report-to", "data-directivetype" => "reporting"), // Other directives "require-trusted-types-for" => array("label" => "require-trusted-types-for", "data-directivetype" => "other"), - "othertrusted-types", + "othertrusted-types" => array("label" => "othertrusted-types", "data-directivetype" => "other"), "upgrade-insecure-requests" => array("label" => "upgrade-insecure-requests", "data-directivetype" => "other"), ); } @@ -136,7 +136,7 @@ function websiteGetContentPolicyDirectives() /** * Prepare array of sources for Website * - * @return array Array of tabs + * @return array Array of sources */ function websiteGetContentPolicySources() { @@ -145,32 +145,43 @@ function websiteGetContentPolicySources() "fetch" => array( "*" => array("label" => "*", "data-sourcetype" => "select"), "data" => array("label" => "data", "data-sourcetype" => "data"), - "self" => array("label" => "self", "data-sourcetype" => "select"), + "self" => array("label" => "self", "data-sourcetype" => "quoted"), ), // Document directives "document" => array( - "base-uri", - "sandbox", + "base-uri" => array("label" => "base-uri", "data-sourcetype" => "select"), + "sandbox" => array("label" => "sandbox", "data-sourcetype" => "select"), ), // Navigation directives "navigation" => array( - "self", + "self" => array("label" => "self", "data-sourcetype" => "quoted"), ), // Reporting directives "reporting" => array( - "report-to", + "report-to" => array("label" => "report-to", "data-sourcetype" => "select"), ), // Other directives "other" => array( - "require-trusted-types-for", - "trusted-types", - "upgrade-insecure-requests", + "require-trusted-types-for" => array("label" => "require-trusted-types-for", "data-sourcetype" => "select"), + "trusted-types" => array("label" => "trusted-types", "data-sourcetype" => "select"), + "upgrade-insecure-requests" => array("label" => "upgrade-insecure-requests", "data-sourcetype" => "select"), ), ); } +/** + * Transform a Content Security Policy to an array + * @param string content security policy + * + * @return array Array of sources + */ function websiteGetContentPolicyToArray($forceCSP){ $forceCSPArr = array(); + $sourceCSPArr = websiteGetContentPolicySources(); + $sourceCSPArrflatten = array(); + foreach ($sourceCSPArr as $key => $arr) { + $sourceCSPArrflatten = array_merge($sourceCSPArrflatten, array_keys($arr)); + } $securitypolicies = explode(";", $forceCSP); foreach ($securitypolicies as $key => $securitypolicy) { if ($securitypolicy == "") continue; @@ -183,11 +194,29 @@ function websiteGetContentPolicyToArray($forceCSP){ continue; } $sources = $securitypolicyarr; + $issourcedata = 0; foreach ($sources as $key => $source) { - if (empty($forceCSPArr[$directive])) { - $forceCSPArr[$directive] = array($source); + $source = str_replace(":", "", $source); + $source = str_replace("'", "", $source); + + if ($source == "data") { + $issourcedata = 1; + if (empty($forceCSPArr[$directive])) { + $forceCSPArr[$directive] = array($source => array()); + } else { + $forceCSPArr[$directive][$source] = array(); + } + continue; + } + if ($issourcedata && !in_array($source, $sourceCSPArrflatten)) { + $forceCSPArr[$directive]["data"][] = $source; } else { - $forceCSPArr[$directive][] = $source; + $issourcedata = 0; + if (empty($forceCSPArr[$directive])) { + $forceCSPArr[$directive] = array($source); + } else { + $forceCSPArr[$directive][] = $source; + } } } } From b3f40b2c83a9727738c51b719736afc127f2b6a9 Mon Sep 17 00:00:00 2001 From: Hystepik Date: Fri, 7 Feb 2025 12:21:44 +0100 Subject: [PATCH 05/23] fix some naming and add comment --- htdocs/website/index.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/htdocs/website/index.php b/htdocs/website/index.php index 18b9cc8790a12..ae94a5b920ebe 100644 --- a/htdocs/website/index.php +++ b/htdocs/website/index.php @@ -4343,8 +4343,9 @@ function switchEditorOnline(forceenable) } if ($action == 'editsecurity') { - $selectarraySP = websiteGetContentPolicyDirectives(); - $selectarraySPlevel2 = websiteGetContentPolicySources(); + $selectarrayCSPDirectives = websiteGetContentPolicyDirectives(); + $selectarrayCSPSources = websiteGetContentPolicySources(); + $forceCSPArr = websiteGetContentPolicyToArray($forceCSP); print '
'; print '
'; @@ -4376,17 +4377,19 @@ function switchEditorOnline(forceenable) print '
'.$langs->trans("SecurityPolicy").'
'.$langs->trans("Value").':id."_SECURITY_FORCECSP").'">
'.$langs->trans("Value").':
'.$form->selectarray("select_identifier_WEBSITE_SECURITY_FORCECSP", $selectarraySP, "select_identifier_WEBSITE_SECURITY_FORCECSP", 1, 0, 0, '', 0, 0, 0, '', 'minwidth300').''; foreach ($selectarraySPlevel2 as $key => $values) { print ''; } print '
'; print '
'; - // Security Policy + // Content Security Policy print '
'; print ''; print ''; print ''; + print ''; + print ''; - print ''; + print ''; print '
'.$langs->trans("SecurityPolicy").'
'.$langs->trans("Value").':
'.$form->selectarray("select_identifier_WEBSITE_SECURITY_FORCECSP", $selectarraySP, "select_identifier_WEBSITE_SECURITY_FORCECSP", 1, 0, 0, '', 0, 0, 0, '', 'minwidth300').''.$form->selectarray("select_identifier_WEBSITE_SECURITY_FORCECSP", $selectarrayCSPDirectives, "select_identifier_WEBSITE_SECURITY_FORCECSP", 1, 0, 0, '', 0, 0, 0, '', 'minwidth300').''; print ''; - foreach ($selectarraySPlevel2 as $key => $values) { + foreach ($selectarrayCSPSources as $key => $values) { print ''; @@ -4398,9 +4401,7 @@ function switchEditorOnline(forceenable) print '
'; print '
'; - //TODO: add comment to explain + better look - - $forceCSPArr = websiteGetContentPolicyToArray($forceCSP); + // Content Security Policy list of selected rules print '
'; print '
    '; foreach ($forceCSPArr as $directive => $sources) { @@ -4424,7 +4425,7 @@ function switchEditorOnline(forceenable) } print '
'; print '
'; - //TODO: add console.log in js + print '