mirror of
https://github.com/Dolibarr/dolibarr.git
synced 2026-02-08 00:52:01 +01:00
NEW Add a page to edit http security headers of application (#34941)
* New main http security headers page * fix CI * Fix CI --------- Co-authored-by: Lucas Marcouiller <lmarcouiller@dolicloud.com> Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
This commit is contained in:
committed by
GitHub
parent
5ee1d43db0
commit
cbd600ec8c
422
htdocs/admin/security_headers_http.php
Normal file
422
htdocs/admin/security_headers_http.php
Normal file
@@ -0,0 +1,422 @@
|
||||
<?php
|
||||
/* Copyright (C) 2004-2013 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
|
||||
* Copyright (C) 2013 Juanjo Menent <jmenent@2byte.es>
|
||||
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file htdocs/admin/security_headers_http.php
|
||||
* \ingroup core
|
||||
* \brief Security options setup
|
||||
*/
|
||||
|
||||
// Load Dolibarr environment
|
||||
require '../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("users", "admin", "other"));
|
||||
|
||||
if (!$user->admin) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
|
||||
$forceCSP = getDolGlobalString("MAIN_SECURITY_FORCECSP");
|
||||
$selectarrayCSPDirectives = GetContentPolicyDirectives();
|
||||
$selectarrayCSPSources = GetContentPolicySources();
|
||||
$forceCSPArr = GetContentPolicyToArray($forceCSP);
|
||||
$error = 0;
|
||||
|
||||
/*
|
||||
* Actions
|
||||
*/
|
||||
|
||||
if (preg_match('/set_([a-z0-9_\-]+)/i', $action, $reg)) {
|
||||
$code = $reg[1];
|
||||
$value = (GETPOST($code, 'alpha') ? GETPOST($code, 'alpha') : 1);
|
||||
if (dolibarr_set_const($db, $code, $value, 'chaine', 0, '', $conf->entity) > 0) {
|
||||
header("Location: ".$_SERVER["PHP_SELF"]);
|
||||
exit;
|
||||
} else {
|
||||
dol_print_error($db);
|
||||
}
|
||||
} elseif (preg_match('/del_([a-z0-9_\-]+)/i', $action, $reg)) {
|
||||
$code = $reg[1];
|
||||
if (dolibarr_del_const($db, $code, $conf->entity) > 0) {
|
||||
header("Location: ".$_SERVER["PHP_SELF"]);
|
||||
exit;
|
||||
} else {
|
||||
dol_print_error($db);
|
||||
}
|
||||
} elseif ($action == 'removecspsource') {
|
||||
$db->begin();
|
||||
$sourcetype = "";
|
||||
$sourcecsp = explode("_", GETPOST("sourcecsp"));
|
||||
$directive = $sourcecsp[0];
|
||||
$sourcekey = isset($sourcecsp[1]) ? $sourcecsp[1] : null;
|
||||
$sourcedata = isset($sourcecsp[2]) ? $sourcecsp[2] : null;
|
||||
$forceCSPArr = GetContentPolicyToArray($forceCSP);
|
||||
$directivesarray = GetContentPolicyDirectives();
|
||||
$sourcesarray = GetContentPolicySources();
|
||||
if (empty($directive)) {
|
||||
$error++;
|
||||
}
|
||||
|
||||
if (!empty($directivesarray[$directive])) {
|
||||
$directivetype = (string) $directivesarray[$directive]["data-directivetype"];
|
||||
if (isset($sourcekey)) {
|
||||
$sourcetype = $sourcesarray[$directivetype][$sourcekey]["data-sourcetype"];
|
||||
}
|
||||
}
|
||||
|
||||
$securityspstring = "";
|
||||
if (!$error && !empty($forceCSPArr)) {
|
||||
if (isset($sourcekey) && !empty($forceCSPArr[$directive][$sourcekey])) {
|
||||
unset($forceCSPArr[$directive][$sourcekey]);
|
||||
}
|
||||
if (count($forceCSPArr[$directive]) == 0) {
|
||||
unset($forceCSPArr[$directive]);
|
||||
}
|
||||
foreach ($forceCSPArr as $directive => $sourcekeys) {
|
||||
if ($securityspstring != "") {
|
||||
$securityspstring .= "; ";
|
||||
}
|
||||
$sourcestring = "";
|
||||
foreach ($sourcekeys as $key => $source) {
|
||||
$directivetype = $directivesarray[$directive]["data-directivetype"];
|
||||
$sourcetype = $sourcesarray[$directivetype][$source]["data-sourcetype"];
|
||||
if ($sourcetype == "quoted") {
|
||||
$sourcestring .= " '".$source."'";
|
||||
} else {
|
||||
$sourcestring .= " ".$source;
|
||||
}
|
||||
}
|
||||
$securityspstring .= $directive . $sourcestring;
|
||||
}
|
||||
$res = dolibarr_set_const($db, 'MAIN_SECURITY_FORCECSP', $securityspstring, 'chaine', 0, '', $conf->entity);
|
||||
if ($res <= 0) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$db->commit();
|
||||
setEventMessages($langs->trans("MainSecurityPolicySucesfullyRemoved"), null, 'mesgs');
|
||||
} else {
|
||||
$db->rollback();
|
||||
setEventMessages($langs->trans("MainErrorRemovingSecurityPolicy"), null, 'errors');
|
||||
}
|
||||
|
||||
header("Location: ".$_SERVER["PHP_SELF"]);
|
||||
exit();
|
||||
} elseif ($action == "updateform" && GETPOST("btn_MAIN_SECURITY_FORCECSP")) {
|
||||
$directivecsp = GETPOST("select_identifier_MAIN_SECURITY_FORCECSP");
|
||||
$sourcecsp = GETPOST("select_source_MAIN_SECURITY_FORCECSP");
|
||||
$sourcedatacsp = GETPOST("input_data_MAIN_SECURITY_FORCECSP");
|
||||
$sourcetype = "";
|
||||
|
||||
$forceCSPArr = GetContentPolicyToArray($forceCSP);
|
||||
$directivesarray = GetContentPolicyDirectives();
|
||||
$sourcesarray = GetContentPolicySources();
|
||||
if (empty($directivecsp)) {
|
||||
$error++;
|
||||
}
|
||||
if ($error || (!isset($sourcecsp) && $directivesarray[$directivecsp]["data-directivetype"] != "none")) {
|
||||
$error++;
|
||||
}
|
||||
if (!$error) {
|
||||
$directivetype = $directivesarray[$directivecsp]["data-directivetype"];
|
||||
if (isset($sourcecsp)) {
|
||||
$sourcetype = $sourcesarray[$directivetype][$sourcecsp]["data-sourcetype"];
|
||||
}
|
||||
$securityspstring = "";
|
||||
if (isset($sourcetype) && $sourcetype == "data") {
|
||||
$forceCSPArr[$directivecsp][] = "data:".$sourcedatacsp;
|
||||
} elseif (isset($sourcetype) && $sourcetype == "input") {
|
||||
if (empty($forceCSPArr[$directivecsp])) {
|
||||
$forceCSPArr[$directivecsp] = array();
|
||||
}
|
||||
$forceCSPArr[$directivecsp] = array_merge(explode(" ", $sourcedatacsp), $forceCSPArr[$directivecsp]);
|
||||
} else {
|
||||
if (empty($forceCSPArr[$directivecsp])) {
|
||||
$forceCSPArr[$directivecsp] = array();
|
||||
}
|
||||
if (!isset($sourcecsp)) {
|
||||
$sourcecsp = "";
|
||||
}
|
||||
array_unshift($forceCSPArr[$directivecsp], $sourcecsp);
|
||||
}
|
||||
foreach ($forceCSPArr as $directive => $sourcekeys) {
|
||||
if ($securityspstring != "") {
|
||||
$securityspstring .= "; ";
|
||||
}
|
||||
$sourcestring = "";
|
||||
foreach ($sourcekeys as $key => $source) {
|
||||
$directivetype = $directivesarray[$directive]["data-directivetype"];
|
||||
$sourcetype = $sourcesarray[$directivetype][$source]["data-sourcetype"];
|
||||
if (isset($sourcetype) && $sourcetype == "quoted") {
|
||||
$sourcestring .= " '".$source."'";
|
||||
} elseif ($directivetype != "none") {
|
||||
$sourcestring .= " ".$source;
|
||||
}
|
||||
}
|
||||
$securityspstring .= $directive . $sourcestring;
|
||||
}
|
||||
$res = dolibarr_set_const($db, 'MAIN_SECURITY_FORCECSP', $securityspstring, 'chaine', 0, '', $conf->entity);
|
||||
if ($res <= 0) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$db->commit();
|
||||
setEventMessages($langs->trans("MainSecurityPolicySucesfullyAdded"), null, 'mesgs');
|
||||
} else {
|
||||
$db->rollback();
|
||||
setEventMessages($langs->trans("MainErrorAddingSecurityPolicy"), null, 'errors');
|
||||
}
|
||||
header("Location: ".$_SERVER["PHP_SELF"]);
|
||||
exit();
|
||||
} elseif ($action == "updateform") {
|
||||
$db->begin();
|
||||
$res1 = $res2 = $res3 = $res4 = 0;
|
||||
$securityrp = GETPOST('MAIN_SECURITY_FORCERP', 'alpha');
|
||||
$securitysts = GETPOST('MAIN_SECURITY_FORCESTS', 'alpha');
|
||||
$securitypp = GETPOST('MAIN_SECURITY_FORCEPP', 'alpha');
|
||||
$securitysp = GETPOST('MAIN_SECURITY_FORCECSP', 'alpha');
|
||||
$securitycspro = GETPOST('MAIN_SECURITY_FORCECSPRO', 'alpha');
|
||||
|
||||
$res1 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCERP', $securityrp, 'chaine', 0, '', $conf->entity);
|
||||
$res2 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCESTS', $securitysts, 'chaine', 0, '', $conf->entity);
|
||||
$res3 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCEPP', $securitypp, 'chaine', 0, '', $conf->entity);
|
||||
$res4 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCECSP', $securitysp, 'chaine', 0, '', $conf->entity);
|
||||
$res5 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCECSPRO', $securitycspro, 'chaine', 0, '', $conf->entity);
|
||||
|
||||
if ($res1 >= 0 && $res2 >= 0 && $res3 >= 0 && $res4 >= 0 && $res5 >= 0) {
|
||||
$db->commit();
|
||||
setEventMessages($langs->trans("Saved"), null, 'mesgs');
|
||||
} else {
|
||||
$db->rollback();
|
||||
setEventMessages($langs->trans("ErrorSavingChanges"), null, 'errors');
|
||||
}
|
||||
$action = '';
|
||||
$forceCSP = getDolGlobalString("MAIN_SECURITY_FORCECSP");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
$form = new Form($db);
|
||||
|
||||
$wikihelp = 'EN:Setup_Security|FR:Paramétrage_Sécurité|ES:Configuración_Seguridad';
|
||||
llxHeader('', $langs->trans("MainHttpSecurityHeaders"), $wikihelp, '', 0, 0, '', '', '', 'mod-admin page-security_other');
|
||||
|
||||
print load_fiche_titre($langs->trans("SecuritySetup"), '', 'title_setup');
|
||||
$head = security_prepare_head();
|
||||
|
||||
print dol_get_fiche_head($head, 'headers_http', '', -1);
|
||||
|
||||
print '<br>';
|
||||
|
||||
print '<span class="opacitymedium">'.$langs->trans("HTTPHeaderEditor").'. '.$langs->trans("ReservedToAdvancedUsers").'.</span><br><br>';
|
||||
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<td>'.$langs->trans("HTTPHeader").'</td>';
|
||||
print '<td></td>'."\n";
|
||||
print '</tr>';
|
||||
|
||||
print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="updateform">';
|
||||
|
||||
// Force RP
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$form->textwithpicto($langs->trans('MainSecurityForceRP'), 'HTTP Header Referer-Policy<br><br>'.$langs->trans("Recommended").':<br>"strict-origin-when-cross-origin" '.$langs->trans("or").' "same-origin"=more secured"', 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCERP').'</td>';
|
||||
print '<td><input class="minwidth500" name="MAIN_SECURITY_FORCERP" id="MAIN_SECURITY_FORCERP" value="'.getDolGlobalString("MAIN_SECURITY_FORCERP").'"></td>';
|
||||
print '</tr>';
|
||||
// Force STS
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$form->textwithpicto($langs->trans('MainSecurityForceSTS'), 'HTTP Header Strict-Transport-Security<br><br>'.$langs->trans("Example").':<br>"max-age=31536000; includeSubDomains"', 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCESTS').'</td>';
|
||||
print '<td><input class="minwidth500" name="MAIN_SECURITY_FORCESTS" id="MAIN_SECURITY_FORCESTS" value="'.getDolGlobalString("MAIN_SECURITY_FORCESTS").'"></td>';
|
||||
print '</tr>';
|
||||
// Force PP
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$form->textwithpicto($langs->trans('MainSecurityForcePP'), 'HTTP Header Permissions-Policy<br><br>'.$langs->trans("Example").':<br>"camera=(), microphone=(), geolocation=*"', 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCEPP').'</td>';
|
||||
print '<td><input class="minwidth500" name="MAIN_SECURITY_FORCEPP" id="MAIN_SECURITY_FORCEPP" value="'.getDolGlobalString("MAIN_SECURITY_FORCEPP").'"></td>';
|
||||
print '</tr>';
|
||||
|
||||
$examplecsprule = "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;";
|
||||
|
||||
// Force CSP - Content Security Policy
|
||||
print '<tr class="oddeven nohover">';
|
||||
print '<td class="tdtop">'.$form->textwithpicto($langs->trans('MainContentSecurityPolicy'), 'HTTP Header Content-Security-Policy<br><br>'.$langs->trans("Example").":<br>".$examplecsprule, 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCECSP').'</td>';
|
||||
print '<td>';
|
||||
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
|
||||
print '<input class="minwidth500 quatrevingtpercent" name="MAIN_SECURITY_FORCECSP" id="MAIN_SECURITY_FORCECSP" value="'.$forceCSP.'"> <a href="#" id="btnaddcontentsecuritypolicy">'.img_picto('', 'add').'</a><br>';
|
||||
|
||||
print '<br class="selectaddcontentsecuritypolicy hidden">';
|
||||
|
||||
print '<div id="selectaddcontentsecuritypolicy" class="hidden selectaddcontentsecuritypolicy">';
|
||||
print $form->selectarray("select_identifier_MAIN_SECURITY_FORCECSP", $selectarrayCSPDirectives, "select_identifier_MAIN_SECURITY_FORCECSP", $langs->trans("FillCSPDirective"), 0, 0, '', 0, 0, 0, '', 'minwidth200 maxwidth350 inline-block');
|
||||
print ' ';
|
||||
print '<input type="hidden" id="select_source_MAIN_SECURITY_FORCECSP" name="select_source_MAIN_SECURITY_FORCECSP">';
|
||||
foreach ($selectarrayCSPSources as $key => $values) {
|
||||
print '<div class="div_MAIN_SECURITY_FORCECSP hidden inline-block maxwidth350" id="div_'.$key.'_MAIN_SECURITY_FORCECSP">';
|
||||
print $form->selectarray("select_".$key."_MAIN_SECURITY_FORCECSP", $values, "select_".$key."_MAIN_SECURITY_FORCECSP", $langs->trans("FillCSPSource"), 0, 0, '', 0, 0, 0, '', 'minwidth200 maxwidth300 inline-block select_MAIN_SECURITY_FORCECSP');
|
||||
print '</div>';
|
||||
}
|
||||
print ' ';
|
||||
print '<div class="div_input_data_MAIN_SECURITY_FORCECSP hidden inline-block maxwidth200"><input id="input_data_MAIN_SECURITY_FORCECSP" name="input_data_MAIN_SECURITY_FORCECSP"></div>';
|
||||
print ' ';
|
||||
print '<div class="div_btn_class_MAIN_SECURITY_FORCECSP inline-block maxwidth200"><input type="submit" id="btn_MAIN_SECURITY_FORCECSP" name="btn_MAIN_SECURITY_FORCECSP" class="butAction small smallpaddingimp" value="'.$langs->trans("Add").'" disabled></div>';
|
||||
print '<br><br>';
|
||||
print '</div>';
|
||||
|
||||
if (!empty($forceCSP)) {
|
||||
// Content Security Policy list of selected rules
|
||||
print '<br>';
|
||||
print '<div class="div-table-responsive-no-min">';
|
||||
print img_picto('', 'graph', 'class="pictofixedwidth"').$langs->trans("HierarchicView").'<br>';
|
||||
print '<ul>';
|
||||
foreach ($forceCSPArr as $directive => $sources) {
|
||||
print '<li>';
|
||||
if (in_array($directive, array_keys($selectarrayCSPDirectives))) {
|
||||
print '<span>'.$directive.'</span>';
|
||||
} else {
|
||||
print $form->textwithpicto($directive, $langs->trans("UnknowContentSecurityPolicyDirective"), 1, 'warning');
|
||||
}
|
||||
if (!empty($sources)) {
|
||||
print '<ul>';
|
||||
foreach ($sources as $key => $source) {
|
||||
print '<li><span>'.$source.'</span> <a href="'.$_SERVER["PHP_SELF"].'?action=removecspsource&sourcecsp='.$directive.'_'.$key.'&token='.newToken().'">'.img_delete().'</a></li>';
|
||||
}
|
||||
print '</ul>';
|
||||
} else {
|
||||
print ' <a href="'.$_SERVER["PHP_SELF"].'?action=removecspsource&sourcecsp='.$directive.'&token='.newToken().'">'.img_delete().'</a>';
|
||||
}
|
||||
print '</li>';
|
||||
}
|
||||
print '</ul>';
|
||||
print '</div>';
|
||||
}
|
||||
print '</div>';
|
||||
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Force CSPRO
|
||||
if (getDolGlobalString("MAIN_SECURITY_FORCECSPRO")) {
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$form->textwithpicto($langs->trans('MainSecurityForceCSPRO'), 'HTTP Header Content-Security-Policy-Report-Only<br><br>'.$langs->trans("Example").":<br>".$examplecsprule, 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCECSPRO').'</td>';
|
||||
print '<td><input class="minwidth500" name="MAIN_SECURITY_FORCECSPRO" id="MAIN_SECURITY_FORCECSPRO" value="'.getDolGlobalString("MAIN_SECURITY_FORCECSPRO").'"></td>';
|
||||
print '</tr>';
|
||||
}
|
||||
|
||||
print '</table>';
|
||||
print '</div>';
|
||||
|
||||
|
||||
print '<div class="center">';
|
||||
|
||||
print '<input type="submit" class="button small" name="updateandstay" value="'.$langs->trans("Save").'">';
|
||||
print '<input class="button button-cancel small" type="submit" name="preview" value="'.$langs->trans("Cancel").'">';
|
||||
|
||||
print '</div>';
|
||||
|
||||
|
||||
print '<script>
|
||||
$(document).ready(function() {
|
||||
$("#btnaddcontentsecuritypolicy").on("click", function(){
|
||||
if($("#selectaddcontentsecuritypolicy").is(":visible")){
|
||||
console.log("We hide select to add Content Security Policy");
|
||||
$(".selectaddcontentsecuritypolicy").hide();
|
||||
} else {
|
||||
console.log("We show select to add Content Security Policy");
|
||||
$(".selectaddcontentsecuritypolicy").show();
|
||||
}
|
||||
});
|
||||
|
||||
$("#select_identifier_MAIN_SECURITY_FORCECSP").on("change", function() {
|
||||
key = $(this).find(":selected").data("directivetype");
|
||||
console.log("We hide all select div");
|
||||
$(".div_MAIN_SECURITY_FORCECSP").hide();
|
||||
$(".select_MAIN_SECURITY_FORCECSP").val(null).trigger("change");
|
||||
$(".div_input_data_MAIN_SECURITY_FORCECSP").hide();
|
||||
$("#btn_MAIN_SECURITY_FORCECSP").prop("disabled",true);
|
||||
if (key == "none"){
|
||||
$("#btn_MAIN_SECURITY_FORCECSP").prop("disabled",false);
|
||||
} else {
|
||||
console.log("We show div select with key "+key);
|
||||
$("#div_"+key+"_MAIN_SECURITY_FORCECSP").css("display", "inline-block");
|
||||
}
|
||||
});
|
||||
|
||||
$(".select_MAIN_SECURITY_FORCECSP").on("change", function() {
|
||||
keysource = $(this).find(":selected").data("sourcetype");
|
||||
$("#select_source_MAIN_SECURITY_FORCECSP").val($(this).val());
|
||||
console.log("We hide and show fields");
|
||||
if (keysource == "data" || keysource == "input") {
|
||||
$(".div_input_data_MAIN_SECURITY_FORCECSP").css("display", "inline-block");
|
||||
$("#btn_MAIN_SECURITY_FORCECSP").prop("disabled",true);
|
||||
} else {
|
||||
$("#input_data_MAIN_SECURITY_FORCECSP").val("");
|
||||
$(".div_input_data_MAIN_SECURITY_FORCECSP").hide();
|
||||
if (keysource != undefined) {
|
||||
$("#btn_MAIN_SECURITY_FORCECSP").prop("disabled",false);
|
||||
} else {
|
||||
$("#btn_MAIN_SECURITY_FORCECSP").prop("disabled",true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#input_data_MAIN_SECURITY_FORCECSP").on("change keyup", function(){
|
||||
if ($(this).val() != "") {
|
||||
console.log("We show add button");
|
||||
$("#btn_MAIN_SECURITY_FORCECSP").prop("disabled",false);
|
||||
} else {
|
||||
console.log("We hide add button");
|
||||
$("#btn_MAIN_SECURITY_FORCECSP").prop("disabled",true);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>';
|
||||
|
||||
print dol_get_fiche_end();
|
||||
print '</div>';
|
||||
|
||||
// End of page
|
||||
llxFooter();
|
||||
$db->close();
|
||||
@@ -937,6 +937,11 @@ function security_prepare_head()
|
||||
$h++;
|
||||
}
|
||||
|
||||
$head[$h][0] = DOL_URL_ROOT."/admin/security_headers_http.php";
|
||||
$head[$h][1] = $langs->trans("MainHttpSecurityHeaders");
|
||||
$head[$h][2] = 'headers_http';
|
||||
$h++;
|
||||
|
||||
return $head;
|
||||
}
|
||||
|
||||
@@ -2197,3 +2202,154 @@ function email_admin_prepare_head()
|
||||
|
||||
return $head;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare array of directives for HTTP headers
|
||||
*
|
||||
* @return array<string,array<string,string>> Array of directives
|
||||
*/
|
||||
function GetContentPolicyDirectives()
|
||||
{
|
||||
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" => "require-trusted-types-for"),
|
||||
"trusted-types" => array("label" => "trusted-types", "data-directivetype" => "trusted-types"),
|
||||
"upgrade-insecure-requests" => array("label" => "upgrade-insecure-requests", "data-directivetype" => "none"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare array of sources for HTTP headers
|
||||
*
|
||||
* @return array<string,array<string,array<string,string>>> Array of sources
|
||||
*/
|
||||
function GetContentPolicySources()
|
||||
{
|
||||
return array(
|
||||
// Fetch directives
|
||||
"fetch" => array(
|
||||
"*" => array("label" => "*", "data-sourcetype" => "select"),
|
||||
"data" => array("label" => "data:", "data-sourcetype" => "data"),
|
||||
"self" => array("label" => "self", "data-sourcetype" => "quoted"),
|
||||
"unsafe-eval" => array("label" => "unsafe-eval", "data-sourcetype" => "quoted"),
|
||||
"wasm-unsafe-eval" => array("label" => "wasm-unsafe-eval", "data-sourcetype" => "quoted"),
|
||||
"unsafe-inline" => array("label" => "unsafe-inline", "data-sourcetype" => "quoted"),
|
||||
"unsafe-hashes" => array("label" => "unsafe-hashes", "data-sourcetype" => "quoted"),
|
||||
"inline-speculation-rules" => array("label" => "inline-speculation-rules", "data-sourcetype" => "quoted"),
|
||||
"strict-dynamic" => array("label" => "strict-dynamic", "data-sourcetype" => "quoted"),
|
||||
"report-sample" => array("label" => "report-sample", "data-sourcetype" => "quoted"),
|
||||
"host-source" => array("label" => "host-source (*.mydomain.com)", "data-sourcetype" => "input"),
|
||||
"scheme-source" => array("label" => "scheme-source", "data-sourcetype" => "input"),
|
||||
),
|
||||
// Document directives
|
||||
"document" => array(
|
||||
"none" => array("label" => "self", "data-sourcetype" => "quoted"),
|
||||
"self" => array("label" => "self", "data-sourcetype" => "quoted"),
|
||||
"host-source" => array("label" => "host-source (*.mydomain.com)", "data-sourcetype" => "input"),
|
||||
"scheme-source" => array("label" => "scheme-source (*.mydomain.com)", "data-sourcetype" => "input"),
|
||||
),
|
||||
// Navigation directives
|
||||
"navigation" => array(
|
||||
"none" => array("label" => "self", "data-sourcetype" => "quoted"),
|
||||
"self" => array("label" => "self", "data-sourcetype" => "quoted"),
|
||||
"host-source" => array("label" => "host-source (*.mydomain.com)", "data-sourcetype" => "input"),
|
||||
"scheme-source" => array("label" => "scheme-source", "data-sourcetype" => "input"),
|
||||
),
|
||||
// Reporting directives
|
||||
"reporting" => array(
|
||||
"report-to" => array("label" => "report-to", "data-sourcetype" => "input"),
|
||||
),
|
||||
// Other directives
|
||||
"require-trusted-types-for" => array(
|
||||
"script" => array("label" => "script", "data-sourcetype" => "select"),
|
||||
),
|
||||
"trusted-types" => array(
|
||||
"policyName" => array("label" => "policyName", "data-sourcetype" => "input"),
|
||||
"none" => array("label" => "none", "data-sourcetype" => "quoted"),
|
||||
"allow-duplicates" => array("label" => "allow-duplicates", "data-sourcetype" => "quoted"),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a Content Security Policy to an array
|
||||
*
|
||||
* @param string $forceCSP Content security policy string
|
||||
* @return array<string,array<string|int,array<string|int,string>|string>> Array of sources
|
||||
*/
|
||||
function GetContentPolicyToArray($forceCSP)
|
||||
{
|
||||
$forceCSPArr = array();
|
||||
$sourceCSPArr = GetContentPolicySources();
|
||||
$sourceCSPArrflatten = array();
|
||||
|
||||
// We remove a level for sources array
|
||||
foreach ($sourceCSPArr as $key => $arr) {
|
||||
$sourceCSPArrflatten = array_merge($sourceCSPArrflatten, array_keys($arr));
|
||||
}
|
||||
// Gerer le problème avec data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D qui est split + problème avec button ajouter
|
||||
$forceCSP = preg_replace('/;base64,/', "__semicolumnbase64__", $forceCSP);
|
||||
$securitypolicies = explode(";", $forceCSP);
|
||||
|
||||
// Loop on each security policy to create an array
|
||||
foreach ($securitypolicies as $key => $securitypolicy) {
|
||||
if ($securitypolicy == "") {
|
||||
continue;
|
||||
}
|
||||
$securitypolicy = preg_replace('/__semicolumnbase64__/', ";base64,", $securitypolicy);
|
||||
$securitypolicyarr = explode(" ", $securitypolicy);
|
||||
$directive = array_shift($securitypolicyarr);
|
||||
// Remove unwanted spaces
|
||||
while ($directive == "") {
|
||||
$directive = array_shift($securitypolicyarr);
|
||||
}
|
||||
if (empty($directive)) {
|
||||
continue;
|
||||
}
|
||||
$sources = $securitypolicyarr;
|
||||
if (empty($sources)) {
|
||||
$forceCSPArr[$directive] = array();
|
||||
} else {
|
||||
//Loop on each sources to add to the right directive array key
|
||||
foreach ($sources as $key2 => $source) {
|
||||
$source = str_replace("'", "", $source);
|
||||
if (empty($source)) {
|
||||
continue;
|
||||
}
|
||||
if (empty($forceCSPArr[$directive])) {
|
||||
$forceCSPArr[$directive] = array($source);
|
||||
} else {
|
||||
$forceCSPArr[$directive][] = $source;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $forceCSPArr;
|
||||
}
|
||||
|
||||
@@ -2697,3 +2697,13 @@ PDF_INVOICE_SHOW_BALANCE_SUMMARY= Show customer's previous and new balance
|
||||
SecurityModuleDeploymentSuccess=An external module has been deployed: %s
|
||||
SecurityModuleDeploymentError=A deployment of an external module has been tried and failed: %s
|
||||
DownloadOfModuleFileDisallowed=Download of module files not allowed (param $dolibarr_allow_download_external_modules must be set in conf.php)
|
||||
MainHttpSecurityHeaders=Main HTTP security headers
|
||||
MainSecurityForceRP=Main HTTP "Referer-Policy"
|
||||
MainSecurityForceSTS=Main HTTP "Strict-Transport-Security"
|
||||
MainSecurityForcePP=Main HTTP "Permissions Policy"
|
||||
MainContentSecurityPolicy=Main HTTP "Content-Security-Policy"
|
||||
MainSecurityForceCSPRO=Main HTTP "Content-Securit-Policy-Report-Only"
|
||||
MainSecurityPolicySucesfullyRemoved=Content Security Policy successfully removed
|
||||
MainErrorRemovingSecurityPolicy=An error occurred when trying to remove a Content Security Policy
|
||||
MainSecurityPolicySucesfullyAdded=Content Security Policy successfully added
|
||||
MainErrorAddingSecurityPolicy=An error occurred when trying to add a Content Security Policy
|
||||
|
||||
Reference in New Issue
Block a user