mirror of
https://github.com/Icinga/icingaweb2.git
synced 2026-04-27 17:16:42 -04:00
Use generators instead of iterating over arrays multiple times
This commit is contained in:
parent
e5d9443410
commit
aa419a00dd
1 changed files with 87 additions and 84 deletions
|
|
@ -11,7 +11,6 @@ use Icinga\Application\Hook\CspDirectiveHook;
|
|||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
use Icinga\Web\Navigation\NavigationItem;
|
||||
use Icinga\Web\Response;
|
||||
|
|
@ -67,51 +66,13 @@ class Csp
|
|||
|
||||
public static function collectContentSecurityPolicyDirectives(): array
|
||||
{
|
||||
$policyDirectives = self::fetchDashletNavigationItemConfigs();
|
||||
|
||||
// Allow modules to add their own csp directives in a limited fashion.
|
||||
foreach (CspDirectiveHook::all() as $hook) {
|
||||
$directives = [];
|
||||
try {
|
||||
foreach ($hook->getCspDirectives() as $directive => $policies) {
|
||||
// policy names contain only lowercase letters and '-'. Reject anything else.
|
||||
if (! preg_match('|^[a-z\-]+$|', $directive)) {
|
||||
$errorSource = get_class($hook);
|
||||
Logger::debug("$errorSource: Invalid CSP directive found: $directive");
|
||||
continue;
|
||||
}
|
||||
|
||||
// The default-src can only ever be 'self'. Disallow any updates to it.
|
||||
if ($directive === "default-src") {
|
||||
$errorSource = get_class($hook);
|
||||
Logger::debug("$errorSource: Changing default-src is forbidden.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($policies) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$directives[$directive] = $policies;
|
||||
}
|
||||
|
||||
if (count($directives) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$policyDirectives[] = [
|
||||
"directives" => $directives,
|
||||
"reason" => [
|
||||
"type" => "hook",
|
||||
"hook" => get_class($hook),
|
||||
],
|
||||
];
|
||||
} catch (Throwable $e) {
|
||||
Logger::error('Failed to CSP hook on request: %s', $e);
|
||||
}
|
||||
}
|
||||
|
||||
return $policyDirectives;
|
||||
// Create an array here because system origins should always come first.
|
||||
return array_merge(
|
||||
iterator_to_array(self::yieldSystemOrigins()),
|
||||
iterator_to_array(self::yieldNavigationOrigins()),
|
||||
iterator_to_array(self::yieldDashletItems()),
|
||||
iterator_to_array(self::yieldModuleOrigins()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -156,20 +117,7 @@ class Csp
|
|||
*/
|
||||
public static function getAutomaticContentSecurityPolicy(): string
|
||||
{
|
||||
$csp = static::getInstance();
|
||||
|
||||
if (empty($csp->styleNonce)) {
|
||||
throw new RuntimeException('No nonce set for CSS');
|
||||
}
|
||||
|
||||
// These are the default directives that should always be enforced. 'self' is valid for all
|
||||
// directives and will therefore not be listed here.
|
||||
$cspDirectives = [
|
||||
'style-src' => ["'nonce-{$csp->styleNonce}'"],
|
||||
'font-src' => ["data:"],
|
||||
'img-src' => ["data:"],
|
||||
'frame-src' => [],
|
||||
];
|
||||
$cspDirectives = [];
|
||||
|
||||
$policyDirectives = self::collectContentSecurityPolicyDirectives();
|
||||
|
||||
|
|
@ -186,7 +134,7 @@ class Csp
|
|||
foreach ($cspDirectives as $directive => $policyDirectives) {
|
||||
if (! empty($policyDirectives)) {
|
||||
$header .= ' ' .
|
||||
implode(' ', array_merge([$directive, "'self'"], array_unique($policyDirectives)))
|
||||
implode(' ', array_merge([$directive], array_unique($policyDirectives)))
|
||||
. ';';
|
||||
}
|
||||
}
|
||||
|
|
@ -249,17 +197,77 @@ class Csp
|
|||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and merges configurations for navigation menu items and dashlets.
|
||||
*
|
||||
* @return array An array containing both navigation items and dashlet configurations.
|
||||
*/
|
||||
protected static function fetchDashletNavigationItemConfigs(): array
|
||||
protected static function yieldSystemOrigins(): Generator
|
||||
{
|
||||
return array_merge(
|
||||
self::fetchNavigationItems(),
|
||||
self::fetchDashletsItems(),
|
||||
);
|
||||
$csp = static::getInstance();
|
||||
|
||||
if (empty($csp->styleNonce)) {
|
||||
throw new RuntimeException('No nonce set for CSS');
|
||||
}
|
||||
|
||||
$items = [
|
||||
'default-src' => ["'self'"],
|
||||
'style-src' => ["'self'", "'nonce-{$csp->styleNonce}'"],
|
||||
'font-src' => ["'self'", "data:"],
|
||||
'img-src' => ["'self'", "data:"],
|
||||
'frame-src' => ["'self'"],
|
||||
];
|
||||
|
||||
foreach ($items as $directive => $policies) {
|
||||
yield [
|
||||
'directives' => [
|
||||
$directive => $policies,
|
||||
],
|
||||
'reason' => [
|
||||
'type' => 'system',
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
protected static function yieldModuleOrigins(): Generator
|
||||
{
|
||||
// Allow modules to add their own csp directives in a limited fashion.
|
||||
foreach (CspDirectiveHook::all() as $hook) {
|
||||
$directives = [];
|
||||
try {
|
||||
foreach ($hook->getCspDirectives() as $directive => $policies) {
|
||||
// policy names contain only lowercase letters and '-'. Reject anything else.
|
||||
if (! preg_match('|^[a-z\-]+$|', $directive)) {
|
||||
$errorSource = get_class($hook);
|
||||
Logger::debug("$errorSource: Invalid CSP directive found: $directive");
|
||||
continue;
|
||||
}
|
||||
|
||||
// The default-src can only ever be 'self'. Disallow any updates to it.
|
||||
if ($directive === "default-src") {
|
||||
$errorSource = get_class($hook);
|
||||
Logger::debug("$errorSource: Changing default-src is forbidden.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($policies) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$directives[$directive] = $policies;
|
||||
}
|
||||
|
||||
if (count($directives) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
yield [
|
||||
"directives" => $directives,
|
||||
"reason" => [
|
||||
"type" => "hook",
|
||||
"hook" => get_class($hook),
|
||||
],
|
||||
];
|
||||
} catch (Throwable $e) {
|
||||
Logger::error('Failed to CSP hook on request: %s', $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -268,22 +276,21 @@ class Csp
|
|||
* Iterates through all registered navigation types, loads both user-specific
|
||||
* and shared configurations, and returns a list of menu items.
|
||||
*
|
||||
* @return array A list of CSP directives, one for each navigation-item that has an external URL.
|
||||
* @return Generator A list of CSP directives, one for each navigation-item that has an external URL.
|
||||
*/
|
||||
protected static function fetchNavigationItems(): array
|
||||
protected static function yieldNavigationOrigins(): Generator
|
||||
{
|
||||
$auth = Auth::getInstance();
|
||||
if (! $auth->isAuthenticated()) {
|
||||
return [];
|
||||
return;
|
||||
}
|
||||
|
||||
$origins = [];
|
||||
$navigationType = Navigation::getItemTypeConfiguration();
|
||||
foreach ($navigationType as $type => $_) {
|
||||
$navigation = new Navigation();
|
||||
foreach ($navigation->load($type) as $navItem) {
|
||||
foreach (self::yieldNavigation($navItem) as $name => $url) {
|
||||
$origins[] = [
|
||||
yield [
|
||||
'directives' => [
|
||||
'frame-src' => [$url->getScheme() . '://' . $url->getHost()],
|
||||
],
|
||||
|
|
@ -297,8 +304,6 @@ class Csp
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $origins;
|
||||
}
|
||||
|
||||
protected static function yieldNavigation(NavigationItem $item): Generator
|
||||
|
|
@ -321,14 +326,13 @@ class Csp
|
|||
/**
|
||||
* Fetches all dashlets for the current user that have an external URL.
|
||||
*
|
||||
* @return array A list of CSP directives, one for each dashlet that has an external URL.
|
||||
* @return Generator A list of CSP directives, one for each dashlet that has an external URL.
|
||||
*/
|
||||
protected static function fetchDashletsItems(): array
|
||||
protected static function yieldDashletItems(): Generator
|
||||
{
|
||||
$user = Auth::getInstance()->getUser();
|
||||
$origins = [];
|
||||
if ($user === null) {
|
||||
return $origins;
|
||||
return;
|
||||
}
|
||||
|
||||
$dashboard = new Dashboard();
|
||||
|
|
@ -351,7 +355,7 @@ class Csp
|
|||
continue;
|
||||
}
|
||||
|
||||
$origins[] = [
|
||||
yield [
|
||||
'directives' => [
|
||||
'frame-src' => [$absoluteUrl],
|
||||
],
|
||||
|
|
@ -364,6 +368,5 @@ class Csp
|
|||
];
|
||||
}
|
||||
}
|
||||
return $origins;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue