Vulnerability: Firefly III 4.7.17.5 is vulnerable to stored XSS due to the lack of filtration of user-supplied data in the liability name field. The JavaScript code is executed upon an error condition during a visit to the account show page.
Vulnerability ID: CVE-2019-14672
CWE: CWE-79
Severity score: 2.9
Project: https://github.com/firefly-iii/firefly-iii
Published: 2019-08-05
File: app/Support/Http/Controllers/UserNavigation.php
Commit message: Fix #2370
Commit author: James Cole

Vulnerable version (before fix):
 protected function redirectToOriginalAccount(Account $account)         /** @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]));             Log::error(sprintf('Expected a transaction. Account #%d has none. BEEP, error.', $account->id));              return redirect(route('index')); protected function redirectToOriginalAccount(Account $account)         $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]));             Log::error(sprintf('Expected an opposing transaction. Account #%d has none. BEEP, error.', $account->id));         } 

Fixed version (after fix):
 protected function redirectToOriginalAccount(Account $account)         /** @var Transaction $transaction */         $transaction = $account->transactions()->first();         if (null === $transaction) {             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')); protected function redirectToOriginalAccount(Account $account)         $opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first();          if (null === $opposingTransaction) {             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));         } 

Patch:
patch: @@ -124,7 +124,7 @@ protected function redirectToOriginalAccount(Account $account)
         /** @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 @@ protected function redirectToOriginalAccount(Account $account)
         $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));
         }
path: app/Support/Http/Controllers/UserNavigation.php