mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-03-27 10:43:41 -04:00
tec: Allow to change CSP header from controllers
For an extension, I needed to call a script from an external domain. Unfortunately, the CSP headers didn't allow this domain and I had to patch manually the FreshRSS FrontController for my extension. It's obviously not a long-term solution since it has nothing to do in the core of FRSS, and I don't want to apply this patch manually at each update. With this patch, I allow changing the CSP header from inside the controller actions. It allows extensions to modify headers. It's also an opportunity to remove a bit of code from the FrontController. I wasn't happy with the previous implementation anyhow. Reference: https://github.com/flusio/xExtension-Flus/commit/ed12d56#diff-ff12e33ed31b23bda327499fa6e84eccR143
This commit is contained in:
@@ -83,6 +83,13 @@ class FreshRSS_index_Controller extends Minz_ActionController {
|
||||
Minz_Error::error(404);
|
||||
}
|
||||
};
|
||||
|
||||
$this->_csp([
|
||||
'default-src' => "'self'",
|
||||
'frame-src' => '*',
|
||||
'img-src' => '* data:',
|
||||
'media-src' => '*',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,6 +128,13 @@ class FreshRSS_index_Controller extends Minz_ActionController {
|
||||
$title = '(' . FreshRSS_Context::$get_unread . ') ' . $title;
|
||||
}
|
||||
Minz_View::prependTitle($title . ' · ');
|
||||
|
||||
$this->_csp([
|
||||
'default-src' => "'self'",
|
||||
'frame-src' => '*',
|
||||
'img-src' => '* data:',
|
||||
'media-src' => '*',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,11 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
|
||||
Minz_Error::error(403);
|
||||
}
|
||||
|
||||
$this->_csp([
|
||||
'default-src' => "'self'",
|
||||
'style-src' => "'self' 'unsafe-inline'",
|
||||
]);
|
||||
|
||||
Minz_View::prependTitle(_t('admin.stats.title') . ' · ');
|
||||
}
|
||||
|
||||
|
||||
@@ -124,23 +124,6 @@ class FreshRSS extends Minz_FrontController {
|
||||
}
|
||||
|
||||
public static function preLayout() {
|
||||
switch (Minz_Request::controllerName()) {
|
||||
case 'index':
|
||||
$urlToAuthorize = array_filter(array_map(function ($a) {
|
||||
if (isset($a['method']) && $a['method'] === 'POST') {
|
||||
return $a['url'];
|
||||
}
|
||||
}, FreshRSS_Context::$user_conf->sharing));
|
||||
$connectSrc = count($urlToAuthorize) ? sprintf("; connect-src 'self' %s", implode(' ', $urlToAuthorize)) : '';
|
||||
header(sprintf("Content-Security-Policy: default-src 'self'; frame-src *; img-src * data:; media-src *%s", $connectSrc));
|
||||
break;
|
||||
case 'stats':
|
||||
header("Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'");
|
||||
break;
|
||||
default:
|
||||
header("Content-Security-Policy: default-src 'self'");
|
||||
break;
|
||||
}
|
||||
header("X-Content-Type-Options: nosniff");
|
||||
|
||||
FreshRSS_Share::load(join_path(APP_PATH, 'shares.php'));
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
class Minz_ActionController {
|
||||
protected $view;
|
||||
private $csp_policies = array(
|
||||
'default-src' => "'self'",
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructeur
|
||||
@@ -27,6 +30,39 @@ class Minz_ActionController {
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set CSP policies.
|
||||
*
|
||||
* A default-src directive should always be given.
|
||||
*
|
||||
* References:
|
||||
* - https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
|
||||
* - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src
|
||||
*
|
||||
* @param array $policies An array where keys are directives and values are sources.
|
||||
*/
|
||||
protected function _csp($policies) {
|
||||
if (!isset($policies['default-src'])) {
|
||||
$action = Minz_Request::controllerName() . '#' . Minz_Request::actionName();
|
||||
Minz_Log::warning(
|
||||
"Default CSP policy is not declared for action {$action}.",
|
||||
ADMIN_LOG
|
||||
);
|
||||
}
|
||||
$this->csp_policies = $policies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send HTTP Content-Security-Policy header based on declared policies.
|
||||
*/
|
||||
public function declareCspHeader() {
|
||||
$policies = [];
|
||||
foreach ($this->csp_policies as $directive => $sources) {
|
||||
$policies[] = $directive . ' ' . $sources;
|
||||
}
|
||||
header('Content-Security-Policy: ' . implode('; ', $policies));
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthodes à redéfinir (ou non) par héritage
|
||||
* firstAction est la première méthode exécutée par le Dispatcher
|
||||
|
||||
@@ -50,6 +50,7 @@ class Minz_Dispatcher {
|
||||
$this->controller->lastAction ();
|
||||
|
||||
if (!self::$needsReset) {
|
||||
$this->controller->declareCspHeader();
|
||||
$this->controller->view ()->build ();
|
||||
}
|
||||
} catch (Minz_Exception $e) {
|
||||
|
||||
Reference in New Issue
Block a user