new: [security] added functionality to tighten bookmark creation rules

- site admins can now limit the baseurls of the provided bookmark URLs to a list of values via the server settings
pull/187/head
iglocska 2024-11-22 12:48:37 +01:00
parent ab331dcfb9
commit 55cac2e2e6
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
3 changed files with 64 additions and 0 deletions

View File

@ -79,6 +79,10 @@ class UserSettingsController extends AppController
if (empty($currentUser['role']['perm_community_admin'])) {
$data['user_id'] = $currentUser->id;
}
$validationResult = $this->UserSettings->validateUserSetting($data, $currentUser);
if (!$validationResult !== true) {
throw new MethodNotAllowedException(__('You cannot create the given user setting. Reason: {0}', $validationResult));
}
return $data;
}
]);
@ -131,6 +135,10 @@ class UserSettingsController extends AppController
if ($data['user_id'] != $entity->user_id) {
throw new MethodNotAllowedException(__('You cannot assign the setting to a different user.'));
}
$validationResult = $this->UserSettings->validateUserSetting($data);
if ($validationResult !== true) {
throw new MethodNotAllowedException(__('Setting value: {0}', $validationResult));
}
return $data;
}
]);

View File

@ -338,6 +338,17 @@ class CerebrateSettingsProvider extends BaseSettingsProvider
],
]
],
'Restrictions' => [
'Allowed bookmark domains' => [
'security.restrictions.allowed_bookmark_domains' => [
'name' => __('Allowed bookmark domains'),
'type' => 'string',
'severity' => 'info',
'description' => __('Comma separated list of allowed bookmark domains. Leave empty to allow all domains.'),
'default' => '',
],
],
],
'Development' => [
'Debugging' => [
'debug' => [

View File

@ -5,6 +5,7 @@ namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Core\Configure;
require_once(APP . 'Model' . DS . 'Table' . DS . 'SettingProviders' . DS . 'UserSettingsProvider.php');
use App\Settings\SettingsProvider\UserSettingsProvider;
@ -102,6 +103,14 @@ class UserSettingsTable extends AppTable
'name' => $data['bookmark_name'],
'url' => $data['bookmark_url'],
];
$restricted_domains = Configure::read('Security.restrictions.allowed_bookmark_domains');
if (!empty($restricted_domains)) {
$restricted_domains = explode(',', $restricted_domains);
$parsed = parse_url($bookmarkData['url']);
if (!in_array($parsed['host'], $restricted_domains)) {
return null;
}
}
if (is_null($setting)) { // setting not found, create it
$bookmarksData = json_encode([$bookmarkData]);
$result = $this->createSetting($user, $this->BOOKMARK_SETTING_NAME, $bookmarksData);
@ -153,4 +162,40 @@ class UserSettingsTable extends AppTable
}
return $isLocalPath || $isValidURL;
}
public function validateUserSetting($data): bool|string
{
$errors = [];
$json_fields = ['ui.bookmarks'];
if (in_array($data['name'], $json_fields)) {
$decoded = json_decode($data['value'], true);
if (json_last_error() !== JSON_ERROR_NONE) {
return __('Invalid JSON data');
}
$value = $decoded;
} else {
$value = $data['value'];
}
if ($data['name'] === 'ui.bookmarks') {
if (array_values($value) !== $value) {
$value = [$value];
}
$restricted_domains = Configure::read('security.restrictions.allowed_bookmark_domains');
if (!empty($restricted_domains)) {
$restricted_domains = explode(',', $restricted_domains);
foreach ($restricted_domains as &$rd) {
$rd = trim($rd);
}
}
foreach ($value as $bookmark) {
if (!empty($restricted_domains)) {
$parsed = parse_url($bookmark['url']);
if (!in_array($parsed['host'], $restricted_domains)) {
return __('Invalid domain for bookmark. The only domains allowed are: {0}', implode(', ', $restricted_domains));
}
}
}
}
return true;
}
}