diff --git a/src/Model/Table/SettingsTable.php b/src/Model/Table/SettingsTable.php
index 7fe18ac..2eb941c 100644
--- a/src/Model/Table/SettingsTable.php
+++ b/src/Model/Table/SettingsTable.php
@@ -3,6 +3,7 @@ namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
+use Cake\Filesystem\File;
use Cake\Core\Configure;
use Cake\Error\Debugger;
@@ -73,10 +74,18 @@ class SettingsTable extends AppTable
}
}
$setting['value'] = $value ?? '';
+ if (isset($setting['test'])) {
+ $validationResult = $this->SettingsProvider->evaluateFunctionForSetting($setting['test'], $setting);
+ if ($validationResult !== true) {
+ $errors[] = $validationResult;
+ $setting['errorMessage'] = $validationResult;
+ }
+ }
if (empty($errors) && !empty($setting['beforeSave'])) {
$beforeSaveResult = $this->SettingsProvider->evaluateFunctionForSetting($setting['beforeSave'], $setting);
if ($beforeSaveResult !== true) {
$errors[] = $beforeSaveResult;
+ $setting['errorMessage'] = $validationResult;
}
}
if (empty($errors)) {
@@ -85,6 +94,8 @@ class SettingsTable extends AppTable
if (!empty($setting['afterSave'])) {
$this->SettingsProvider->evaluateFunctionForSetting($setting['afterSave'], $setting);
}
+ } else {
+ $errors[] = __('Could not save settings on disk');
}
}
return $errors;
@@ -129,8 +140,16 @@ class SettingsTable extends AppTable
$settings = $this->readSettings();
$settings[$name] = $value;
$settings = json_encode($settings, JSON_PRETTY_PRINT);
- file_put_contents(CONFIG . 'config.json', $settings);
- $this->loadSettings();
- return true;
+ $path = CONFIG . 'config.json';
+ $file = new File($path);
+ if ($file->writable()) {
+ $success = file_put_contents($path, $settings);
+ if ($success) {
+ $this->loadSettings();
+ }
+ } else {
+ $success = false;
+ }
+ return $success;
}
}
diff --git a/src/Model/Table/UserSettingsTable.php b/src/Model/Table/UserSettingsTable.php
index bdfe535..cc4b5db 100644
--- a/src/Model/Table/UserSettingsTable.php
+++ b/src/Model/Table/UserSettingsTable.php
@@ -135,4 +135,18 @@ class UserSettingsTable extends AppTable
}
return $result;
}
+
+ /**
+ * validURI - Ensure the provided URI can be safely put as a link
+ *
+ * @param String $uri
+ * @return bool if the URI is safe to be put as a link
+ */
+ public function validURI(String $uri): bool
+ {
+ $parsed = parse_url($uri);
+ $isLocalPath = empty($parsed['scheme']) && empty($parsed['domain']) && !empty($parsed['path']);
+ $isValidURL = !empty($parsed['scheme']) && in_array($parsed['scheme'], ['http', 'https']) && filter_var($uri, FILTER_SANITIZE_URL);
+ return $isLocalPath || $isValidURL;
+ }
}
diff --git a/templates/Instance/home.php b/templates/Instance/home.php
index 6d91266..e5803c2 100644
--- a/templates/Instance/home.php
+++ b/templates/Instance/home.php
@@ -1,5 +1,9 @@
user_settings_by_name['ui.bookmarks']['value']) ? json_decode($loggedUser->user_settings_by_name['ui.bookmarks']['value'], true) : [];
+$this->userSettingsTable = TableRegistry::getTableLocator()->get('UserSettings');
?>
@@ -9,18 +13,24 @@ $bookmarks = !empty($loggedUser->user_settings_by_name['ui.bookmarks']['value'])
= __('Bookmarks') ?>
-
+
-
+
= __('No bookmarks') ?>
diff --git a/templates/UserSettings/delete_bookmark.php b/templates/UserSettings/delete_my_bookmark.php
similarity index 100%
rename from templates/UserSettings/delete_bookmark.php
rename to templates/UserSettings/delete_my_bookmark.php
diff --git a/templates/UserSettings/save_bookmark.php b/templates/UserSettings/save_my_bookmark.php
similarity index 100%
rename from templates/UserSettings/save_bookmark.php
rename to templates/UserSettings/save_my_bookmark.php
diff --git a/templates/UserSettings/set_setting.php b/templates/UserSettings/set_my_setting.php
similarity index 100%
rename from templates/UserSettings/set_setting.php
rename to templates/UserSettings/set_my_setting.php
diff --git a/templates/element/Settings/field.php b/templates/element/Settings/field.php
index 34bdde8..d52bc14 100644
--- a/templates/element/Settings/field.php
+++ b/templates/element/Settings/field.php
@@ -13,11 +13,11 @@
(!empty($setting['error']) ? $appView->get('variantFromSeverity')[$setting['severity']] : ''),
],
($setting['type'] == 'textarea' ? '' : 'type') => ($setting['type'] == 'textarea' ? '' : 'text'),
- 'id' => $settingId,
- 'data-setting-name' => $settingName,
- 'value' => isset($setting['value']) ? $setting['value'] : "",
- 'placeholder' => $setting['default'] ?? '',
- 'aria-describedby' => "{$settingId}Help"
+ 'id' => h($settingId),
+ 'data-setting-name' => h($settingName),
+ 'value' => isset($setting['value']) ? h($setting['value']) : "",
+ 'placeholder' => empty($setting['default']) ? '' : h($setting['default']),
+ 'aria-describedby' => h("{$settingId}Help")
]
);
})($settingName, $setting, $this);
@@ -28,13 +28,13 @@
return $this->Bootstrap->switch([
'label' => h($setting['description']),
'checked' => !empty($setting['value']),
- 'id' => $settingId,
+ 'id' => h($settingId),
'class' => [
(!empty($setting['error']) ? 'is-invalid' : ''),
(!empty($setting['error']) ? $appView->get('variantFromSeverity')[$setting['severity']] : ''),
],
'attrs' => [
- 'data-setting-name' => $settingName
+ 'data-setting-name' => h($settingName)
]
]);
})($settingName, $setting, $this);
@@ -53,16 +53,16 @@
'type' => 'number',
'min' => '0',
'step' => 1,
- 'id' => $settingId,
- 'data-setting-name' => $settingName,
- 'aria-describedby' => "{$settingId}Help"
+ 'id' => h($settingId),
+ 'data-setting-name' => h($settingName),
+ 'aria-describedby' => h("{$settingId}Help")
]);
})($settingName, $setting, $this);
} elseif ($setting['type'] == 'select' || $setting['type'] == 'multi-select') {
$input = (function ($settingName, $setting, $appView) {
$settingId = str_replace('.', '_', $settingName);
- $setting['value'] = $setting['value'] ?? '';
+ $setting['value'] = empty($setting['value']) ? '' : h($setting['value']);
if ($setting['type'] == 'multi-select') {
if (!is_array($setting['value'])) {
$firstChar = substr($setting['value'], 0, 1);
@@ -77,7 +77,7 @@
foreach ($setting['options'] as $key => $value) {
$optionParam = [
'class' => [],
- 'value' => $key,
+ 'value' => h($key),
];
if ($setting['type'] == 'multi-select') {
if (in_array($key, $setting['value'])) {
@@ -100,10 +100,10 @@
(!empty($setting['error']) ? $appView->get('variantFromSeverity')[$setting['severity']] : ''),
],
($setting['type'] == 'multi-select' ? 'multiple' : '') => ($setting['type'] == 'multi-select' ? 'multiple' : ''),
- 'id' => $settingId,
- 'data-setting-name' => $settingName,
- 'placeholder' => $setting['default'] ?? '',
- 'aria-describedby' => "{$settingId}Help"
+ 'id' => h($settingId),
+ 'data-setting-name' => h($settingName),
+ 'placeholder' => empty($setting['default']) ? '' : h($setting['default']),
+ 'aria-describedby' => h("{$settingId}Help")
], $options);
})($settingName, $setting, $this);
}
diff --git a/templates/element/layouts/sidebar/bookmark-entry.php b/templates/element/layouts/sidebar/bookmark-entry.php
index f2d01c1..a33ffe4 100644
--- a/templates/element/layouts/sidebar/bookmark-entry.php
+++ b/templates/element/layouts/sidebar/bookmark-entry.php
@@ -1,5 +1,8 @@
userSettingsTable = TableRegistry::getTableLocator()->get('UserSettings');
$seed = 'sb-' . mt_rand();
$icon = $entry['icon'] ?? '';
@@ -14,6 +17,8 @@
$active = true;
}
+ $validURI = $this->userSettingsTable->validURI($url);
+
echo $this->Bootstrap->button([
'nodeType' => 'a',
'text' => h($label),
@@ -22,9 +27,9 @@
'outline' => !$active,
'size' => 'sm',
'icon' => h($icon),
- 'class' => ['mb-1'],
+ 'class' => ['mb-1', !$validURI ? 'disabled' : ''],
'params' => [
- 'href' => h($url),
+ 'href' => $validURI ? h($url) : '#',
]
]);
?>