From 8717f469b10e9f7e1547c6f70f7d24e1359d28d4 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 3 Aug 2019 05:08:20 +0200 Subject: [PATCH 1/3] Fix #2370 --- app/Support/Http/Controllers/UserNavigation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Support/Http/Controllers/UserNavigation.php b/app/Support/Http/Controllers/UserNavigation.php index 66e30c5c02..1cac4ed6b0 100644 --- a/app/Support/Http/Controllers/UserNavigation.php +++ b/app/Support/Http/Controllers/UserNavigation.php @@ -124,7 +124,7 @@ trait UserNavigation /** @var Transaction $transaction */ $transaction = $account->transactions()->first(); if (null === $transaction) { - app('session')->flash('error', trans('firefly.account_missing_transaction', ['name' => $account->name, 'id' => $account->id])); + app('session')->flash('error', trans('firefly.account_missing_transaction', ['name' => e($account->name), 'id' => $account->id])); Log::error(sprintf('Expected a transaction. Account #%d has none. BEEP, error.', $account->id)); return redirect(route('index')); @@ -135,7 +135,7 @@ trait UserNavigation $opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first(); if (null === $opposingTransaction) { - app('session')->flash('error', trans('firefly.account_missing_transaction', ['name' => $account->name, 'id' => $account->id])); + app('session')->flash('error', trans('firefly.account_missing_transaction', ['name' => e($account->name), 'id' => $account->id])); Log::error(sprintf('Expected an opposing transaction. Account #%d has none. BEEP, error.', $account->id)); } From 2d7494f8cd53942b661774c9aabf3a0a9cf4053c Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 3 Aug 2019 05:08:35 +0200 Subject: [PATCH 2/3] Fix similar XSS issues. --- app/Http/Controllers/Admin/LinkController.php | 6 +++--- app/Http/Controllers/CurrencyController.php | 18 +++++++++--------- .../Import/JobConfigurationController.php | 4 ++-- .../Import/PrerequisitesController.php | 6 +++--- app/Http/Controllers/PiggyBankController.php | 4 ++-- app/Http/Controllers/ProfileController.php | 10 +++++----- .../Transaction/SingleController.php | 4 ++-- .../views/v1/form/assetAccountCheckList.twig | 2 +- .../views/v1/import/bunq/choose-accounts.twig | 2 +- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/app/Http/Controllers/Admin/LinkController.php b/app/Http/Controllers/Admin/LinkController.php index 4a3a3afcea..1188f28de3 100644 --- a/app/Http/Controllers/Admin/LinkController.php +++ b/app/Http/Controllers/Admin/LinkController.php @@ -86,7 +86,7 @@ class LinkController extends Controller public function delete(Request $request, LinkTypeRepositoryInterface $repository, LinkType $linkType) { if (!$linkType->editable) { - $request->session()->flash('error', (string)trans('firefly.cannot_edit_link_type', ['name' => $linkType->name])); + $request->session()->flash('error', (string)trans('firefly.cannot_edit_link_type', ['name' => e($linkType->name)])); return redirect(route('admin.links.index')); } @@ -143,7 +143,7 @@ class LinkController extends Controller public function edit(Request $request, LinkType $linkType) { if (!$linkType->editable) { - $request->session()->flash('error', (string)trans('firefly.cannot_edit_link_type', ['name' => $linkType->name])); + $request->session()->flash('error', (string)trans('firefly.cannot_edit_link_type', ['name' => e($linkType->name)])); return redirect(route('admin.links.index')); } @@ -246,7 +246,7 @@ class LinkController extends Controller public function update(LinkTypeFormRequest $request, LinkTypeRepositoryInterface $repository, LinkType $linkType) { if (!$linkType->editable) { - $request->session()->flash('error', (string)trans('firefly.cannot_edit_link_type', ['name' => $linkType->name])); + $request->session()->flash('error', (string)trans('firefly.cannot_edit_link_type', ['name' => e($linkType->name)])); return redirect(route('admin.links.index')); } diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index 45d932919b..ee89e3b4f7 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -77,7 +77,7 @@ class CurrencyController extends Controller /** @var User $user */ $user = auth()->user(); if (!$this->userRepository->hasRole($user, 'owner')) { - $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => config('firefly.site_owner')])); + $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); return redirect(route('currencies.index')); } @@ -131,7 +131,7 @@ class CurrencyController extends Controller $user = auth()->user(); if (!$this->userRepository->hasRole($user, 'owner')) { // @codeCoverageIgnoreStart - $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => config('firefly.site_owner')])); + $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); Log::channel('audit')->info(sprintf('Tried to visit page to delete currency %s but is not site owner.', $currency->code)); return redirect(route('currencies.index')); @@ -139,7 +139,7 @@ class CurrencyController extends Controller } if ($this->repository->currencyInUse($currency)) { - $request->session()->flash('error', (string)trans('firefly.cannot_delete_currency', ['name' => $currency->name])); + $request->session()->flash('error', (string)trans('firefly.cannot_delete_currency', ['name' => e($currency->name)])); Log::channel('audit')->info(sprintf('Tried to visit page to delete currency %s but currency is in use.', $currency->code)); return redirect(route('currencies.index')); @@ -167,7 +167,7 @@ class CurrencyController extends Controller $user = auth()->user(); if (!$this->userRepository->hasRole($user, 'owner')) { // @codeCoverageIgnoreStart - $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => config('firefly.site_owner')])); + $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); Log::channel('audit')->info(sprintf('Tried to delete currency %s but is not site owner.', $currency->code)); return redirect(route('currencies.index')); @@ -175,7 +175,7 @@ class CurrencyController extends Controller } if ($this->repository->currencyInUse($currency)) { - $request->session()->flash('error', (string)trans('firefly.cannot_delete_currency', ['name' => $currency->name])); + $request->session()->flash('error', (string)trans('firefly.cannot_delete_currency', ['name' => e($currency->name)])); Log::channel('audit')->info(sprintf('Tried to delete currency %s but is in use.', $currency->code)); return redirect(route('currencies.index')); @@ -203,7 +203,7 @@ class CurrencyController extends Controller $user = auth()->user(); if (!$this->userRepository->hasRole($user, 'owner')) { // @codeCoverageIgnoreStart - $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => config('firefly.site_owner')])); + $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); Log::channel('audit')->info(sprintf('Tried to disable currency %s but is not site owner.', $currency->code)); return redirect(route('currencies.index')); @@ -211,7 +211,7 @@ class CurrencyController extends Controller } if ($this->repository->currencyInUse($currency)) { - $request->session()->flash('error', (string)trans('firefly.cannot_disable_currency', ['name' => $currency->name])); + $request->session()->flash('error', (string)trans('firefly.cannot_disable_currency', ['name' => e($currency->name)])); Log::channel('audit')->info(sprintf('Tried to disable currency %s but is in use.', $currency->code)); return redirect(route('currencies.index')); @@ -251,7 +251,7 @@ class CurrencyController extends Controller $user = auth()->user(); if (!$this->userRepository->hasRole($user, 'owner')) { // @codeCoverageIgnoreStart - $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => config('firefly.site_owner')])); + $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); Log::channel('audit')->info(sprintf('Tried to edit currency %s but is not owner.', $currency->code)); return redirect(route('currencies.index')); @@ -395,7 +395,7 @@ class CurrencyController extends Controller $data = $request->getCurrencyData(); if (!$this->userRepository->hasRole($user, 'owner')) { // @codeCoverageIgnoreStart - $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => config('firefly.site_owner')])); + $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); Log::channel('audit')->info('Tried to update (POST) currency without admin rights.', $data); return redirect(route('currencies.index')); diff --git a/app/Http/Controllers/Import/JobConfigurationController.php b/app/Http/Controllers/Import/JobConfigurationController.php index 30460f3dea..20a7f0966a 100644 --- a/app/Http/Controllers/Import/JobConfigurationController.php +++ b/app/Http/Controllers/Import/JobConfigurationController.php @@ -77,7 +77,7 @@ class JobConfigurationController extends Controller $allowed = ['has_prereq', 'need_job_config']; if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) { Log::error(sprintf('Job has state "%s", but we only accept %s', $importJob->status, json_encode($allowed))); - session()->flash('error', (string)trans('import.bad_job_status', ['status' => $importJob->status])); + session()->flash('error', (string)trans('import.bad_job_status', ['status' => e($importJob->status)])); return redirect(route('import.index')); } @@ -127,7 +127,7 @@ class JobConfigurationController extends Controller // catch impossible status: $allowed = ['has_prereq', 'need_job_config']; if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) { - session()->flash('error', (string)trans('import.bad_job_status', ['status' => $importJob->status])); + session()->flash('error', (string)trans('import.bad_job_status', ['status' => e($importJob->status)])); return redirect(route('import.index')); } diff --git a/app/Http/Controllers/Import/PrerequisitesController.php b/app/Http/Controllers/Import/PrerequisitesController.php index 74835d62f9..da9c5ba82c 100644 --- a/app/Http/Controllers/Import/PrerequisitesController.php +++ b/app/Http/Controllers/Import/PrerequisitesController.php @@ -76,7 +76,7 @@ class PrerequisitesController extends Controller $allowed = ['new']; if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) { Log::error(sprintf('Job has state "%s" but this Prerequisites::index() only accepts %s', $importJob->status, json_encode($allowed))); - session()->flash('error', (string)trans('import.bad_job_status', ['status' => $importJob->status])); + session()->flash('error', (string)trans('import.bad_job_status', ['status' => e($importJob->status)])); return redirect(route('import.index')); } @@ -129,7 +129,7 @@ class PrerequisitesController extends Controller $allowed = ['new']; if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) { Log::error(sprintf('Job has state "%s" but this Prerequisites::post() only accepts %s', $importJob->status, json_encode($allowed))); - session()->flash('error', (string)trans('import.bad_job_status', ['status' => $importJob->status])); + session()->flash('error', (string)trans('import.bad_job_status', ['status' => e($importJob->status)])); return redirect(route('import.index')); } @@ -148,7 +148,7 @@ class PrerequisitesController extends Controller Log::debug(sprintf('Result of storePrerequisites has message count: %d', $result->count())); if ($result->count() > 0) { - $request->session()->flash('error', $result->first()); + $request->session()->flash('error', e($result->first())); // redirect back to job, if has job: return redirect(route('import.prerequisites.index', [$importProvider, $importJob->key ?? '']))->withInput(); diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 6261a88888..412d6a0f88 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -320,7 +320,7 @@ class PiggyBankController extends Controller 'error', (string)trans( 'firefly.cannot_add_amount_piggy', - ['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => $piggyBank->name] + ['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)] ) ); @@ -363,7 +363,7 @@ class PiggyBankController extends Controller 'error', (string)trans( 'firefly.cannot_remove_from_piggy', - ['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => $piggyBank->name] + ['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)] ) ); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 472255734d..2adbaf40c3 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -89,7 +89,7 @@ class ProfileController extends Controller $loginProvider = config('firefly.login_provider'); if ('eloquent' !== $loginProvider) { // @codeCoverageIgnoreStart - $request->session()->flash('error', trans('firefly.login_provider_local_only', ['login_provider' => $loginProvider])); + $request->session()->flash('error', trans('firefly.login_provider_local_only', ['login_provider' => e($loginProvider)])); return redirect(route('profile.index')); // @codeCoverageIgnoreEnd @@ -115,7 +115,7 @@ class ProfileController extends Controller $loginProvider = config('firefly.login_provider'); if ('eloquent' !== $loginProvider) { // @codeCoverageIgnoreStart - $request->session()->flash('error', trans('firefly.login_provider_local_only', ['login_provider' => $loginProvider])); + $request->session()->flash('error', trans('firefly.login_provider_local_only', ['login_provider' => e($loginProvider)])); return redirect(route('profile.index')); // @codeCoverageIgnoreEnd @@ -200,7 +200,7 @@ class ProfileController extends Controller $loginProvider = config('firefly.login_provider'); if ('eloquent' !== $loginProvider) { // @codeCoverageIgnoreStart - $request->session()->flash('warning', trans('firefly.delete_local_info_only', ['login_provider' => $loginProvider])); + $request->session()->flash('warning', trans('firefly.delete_local_info_only', ['login_provider' => e($loginProvider)])); // @codeCoverageIgnoreEnd } $title = auth()->user()->email; @@ -296,7 +296,7 @@ class ProfileController extends Controller $loginProvider = config('firefly.login_provider'); if ('eloquent' !== $loginProvider) { // @codeCoverageIgnoreStart - $request->session()->flash('error', trans('firefly.login_provider_local_only', ['login_provider' => $loginProvider])); + $request->session()->flash('error', trans('firefly.login_provider_local_only', ['login_provider' => e($loginProvider)])); return redirect(route('profile.index')); // @codeCoverageIgnoreEnd @@ -350,7 +350,7 @@ class ProfileController extends Controller $loginProvider = config('firefly.login_provider'); if ('eloquent' !== $loginProvider) { // @codeCoverageIgnoreStart - $request->session()->flash('error', trans('firefly.login_provider_local_only', ['login_provider' => $loginProvider])); + $request->session()->flash('error', trans('firefly.login_provider_local_only', ['login_provider' => e($loginProvider)])); return redirect(route('profile.index')); // @codeCoverageIgnoreEnd diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index bbf82c8737..50a44d616b 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -396,7 +396,7 @@ class SingleController extends Controller // store the journal only, flash the rest. Log::debug(sprintf('Count of error messages is %d', $this->attachments->getErrors()->count())); if (\count($this->attachments->getErrors()->get('attachments')) > 0) { - session()->flash('error', $this->attachments->getErrors()->get('attachments')); + session()->flash('error', e($this->attachments->getErrors()->get('attachments'))); } // flash messages if (\count($this->attachments->getMessages()->get('attachments')) > 0) { @@ -463,7 +463,7 @@ class SingleController extends Controller // @codeCoverageIgnoreStart if (\count($this->attachments->getErrors()->get('attachments')) > 0) { - session()->flash('error', $this->attachments->getErrors()->get('attachments')); + session()->flash('error', e($this->attachments->getErrors()->get('attachments'))); } if (\count($this->attachments->getMessages()->get('attachments')) > 0) { session()->flash('info', $this->attachments->getMessages()->get('attachments')); diff --git a/resources/views/v1/form/assetAccountCheckList.twig b/resources/views/v1/form/assetAccountCheckList.twig index 129dc11356..d47ea5d1e3 100644 --- a/resources/views/v1/form/assetAccountCheckList.twig +++ b/resources/views/v1/form/assetAccountCheckList.twig @@ -12,7 +12,7 @@ {% else %} {{ Form.checkbox(name~'[]', id, false, options) }} {% endif %} - {{ account }} + {{ account|escape }} </label> </div> {% endfor %} diff --git a/resources/views/v1/import/bunq/choose-accounts.twig b/resources/views/v1/import/bunq/choose-accounts.twig index 4d5f395f30..b43e6f3d48 100644 --- a/resources/views/v1/import/bunq/choose-accounts.twig +++ b/resources/views/v1/import/bunq/choose-accounts.twig @@ -64,7 +64,7 @@ {% endif %} {% endfor %} {% if account.status != 'ACTIVE' %} - <li>{{ trans('import.bunq_account_status_'~account.status) }}</li> + <li>{{ trans('import.bunq_account_status_'~account.status|escape) }}</li> {% endif %} {% if account.type == 'MonetaryAccountSavings' %} <li>{{ trans('import.bunq_savings_goal', {'amount': account.savingsGoal.currency ~' '~account.savingsGoal.value,'percentage' : account.savingsGoal.percentage}) }}</li> From ec8e1d5a7a199843c9b0095213b94f0830656c09 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 3 Aug 2019 05:10:06 +0200 Subject: [PATCH 3/3] Version update --- .sandstorm/changelog.md | 4 ++++ .sandstorm/sandstorm-pkgdef.capnp | 4 ++-- .travis.yml | 2 +- changelog.md | 5 +++++ config/firefly.php | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.sandstorm/changelog.md b/.sandstorm/changelog.md index 99ac9cb943..3e742969dd 100644 --- a/.sandstorm/changelog.md +++ b/.sandstorm/changelog.md @@ -1,3 +1,7 @@ +# 4.7.17.6 (API 0.9.2) + +- XSS issue in liability account redirect, found by [@0x2500](https://github.com/0x2500). + # 4.7.17.5 (API 0.9.2) - Several XSS issues, found by [@0x2500](https://github.com/0x2500). diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index eae7372e28..ee3ce6388e 100644 --- a/.sandstorm/sandstorm-pkgdef.capnp +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -15,8 +15,8 @@ const pkgdef :Spk.PackageDefinition = ( manifest = ( appTitle = (defaultText = "Firefly III"), - appVersion = 31, - appMarketingVersion = (defaultText = "4.7.17.5"), + appVersion = 32, + appMarketingVersion = (defaultText = "4.7.17.6"), actions = [ # Define your "new document" handlers here. diff --git a/.travis.yml b/.travis.yml index cc58593925..e7c74e3738 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: required language: bash env: - - VERSION=4.7.17.5 + - VERSION=4.7.17.6 dist: xenial diff --git a/changelog.md b/changelog.md index 01b7c9a969..47c7f4f1c3 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.7.17.6 (API 0.9.2)] - 2019-08-02 + +### Security +- XSS issue in liability account redirect, found by [@0x2500](https://github.com/0x2500). + ## [4.7.17.5 (API 0.9.2)] - 2019-08-02 ### Security diff --git a/config/firefly.php b/config/firefly.php index 5592d289ca..e2efe34136 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -93,7 +93,7 @@ return [ 'is_demo_site' => false, ], 'encryption' => null === env('USE_ENCRYPTION') || env('USE_ENCRYPTION') === true, - 'version' => '4.7.17.5', + 'version' => '4.7.17.6', 'api_version' => '0.9.2', 'db_version' => 10, 'maxUploadSize' => 15242880,