Moderate severityNVD Advisory· Published Nov 13, 2021· Updated Aug 3, 2024
Cross-Site Request Forgery (CSRF) in area17/twill
CVE-2021-3932
Description
twill is vulnerable to Cross-Site Request Forgery (CSRF)
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
area17/twillPackagist | < 1.2.5 | 1.2.5 |
area17/twillPackagist | >= 2.0.0, < 2.5.3 | 2.5.3 |
Affected products
1- Range: unspecified
Patches
281d80d1fbbddUse POST action for logout links
6 files changed · +26 −3
frontend/js/behaviors/logoutButton.js+16 −0 added@@ -0,0 +1,16 @@ +// POST logout action + +const logoutButton = function () { + const logoutForm = document.querySelector('[data-logout-form]') + + if (!logoutForm) return + + document.body.addEventListener('click', e => { + if (e.target.hasAttribute('data-logout-btn')) { + e.preventDefault() + logoutForm.submit() + } + }) +} + +export default logoutButton
frontend/js/main.js+2 −0 modified@@ -4,12 +4,14 @@ import 'styles/app.scss' import Vue from 'vue' import navToggle from '@/behaviors/navToggle' import showEnvLine from '@/behaviors/showEnvLine' +import logoutButton from '@/behaviors/logoutButton' import search from '@/main-search' import merge from 'lodash/merge' const A17Init = function () { navToggle() showEnvLine() + logoutButton() } // User header dropdown
routes/auth.php+1 −1 modified@@ -5,7 +5,7 @@ if (config('twill.enabled.users-management')) { Route::get('login', 'LoginController@showLoginForm')->name('login.form'); Route::post('login', 'LoginController@login')->name('login'); - Route::get('logout', 'LoginController@logout')->name('logout'); + Route::post('logout', 'LoginController@logout')->name('logout'); Route::get('password/reset', 'ForgotPasswordController@showLinkRequestForm')->name('password.reset.link'); Route::post('password/email', 'ForgotPasswordController@sendResetLinkEmail')->name('password.reset.email');
views/layouts/main.blade.php+5 −0 modified@@ -71,6 +71,11 @@ @include('twill::partials.footer') </section> </div> + + <form class="visually-hidden" method="POST" action="{{ route('admin.logout') }}" data-logout-form> + @csrf + </form> + <script> window['{{ config('twill.js_namespace') }}'] = {}; window['{{ config('twill.js_namespace') }}'].version = '{{ config('twill.version') }}';
views/partials/navigation/_overlay_navigation.blade.php+1 −1 modified@@ -22,7 +22,7 @@ @if(isset($currentUser)) <a href="{{ route('admin.users.index') }}">{{ twillTrans('twill::lang.nav.cms-users') }}</a><br /> <a href="{{ route('admin.users.edit', $currentUser->id) }}">{{ twillTrans('twill::lang.nav.settings') }}</a><br /> - <a href="{{ route('admin.logout') }}">{{ twillTrans('twill::lang.nav.logout') }}</a> + <a href="#" data-logout-btn>{{ twillTrans('twill::lang.nav.logout') }}</a> @endif </div> </div>
views/partials/navigation/_user.blade.php+1 −1 modified@@ -14,7 +14,7 @@ <a href="{{ route('admin.users.index') }}">{{ twillTrans('twill::lang.nav.cms-users') }}</a> @endcan <a href="{{ route('admin.users.edit', $currentUser->id) }}">{{ twillTrans('twill::lang.nav.settings') }}</a> - <a href="{{ route('admin.logout') }}">{{ twillTrans('twill::lang.nav.logout') }}</a> + <a href="#" data-logout-btn>{{ twillTrans('twill::lang.nav.logout') }}</a> </div> </a17-dropdown> @endif
5cded9fcUse POST action for logout links
6 files changed · +26 −3
frontend/js/behaviors/logoutButton.js+16 −0 added@@ -0,0 +1,16 @@ +// POST logout action + +const logoutButton = function () { + const logoutForm = document.querySelector('[data-logout-form]') + + if (!logoutForm) return + + document.body.addEventListener('click', e => { + if (e.target.hasAttribute('data-logout-btn')) { + e.preventDefault() + logoutForm.submit() + } + }) +} + +export default logoutButton
frontend/js/main.js+2 −0 modified@@ -4,12 +4,14 @@ import 'styles/app.scss' import Vue from 'vue' import navToggle from '@/behaviors/navToggle' import showEnvLine from '@/behaviors/showEnvLine' +import logoutButton from '@/behaviors/logoutButton' import search from '@/main-search' import merge from 'lodash/merge' const A17Init = function () { navToggle() showEnvLine() + logoutButton() } // User header dropdown
routes/auth.php+1 −1 modified@@ -5,7 +5,7 @@ if (config('twill.enabled.users-management')) { Route::get('login', 'LoginController@showLoginForm')->name('login.form'); Route::post('login', 'LoginController@login')->name('login'); - Route::get('logout', 'LoginController@logout')->name('logout'); + Route::post('logout', 'LoginController@logout')->name('logout'); Route::get('password/reset', 'ForgotPasswordController@showLinkRequestForm')->name('password.reset.link'); Route::post('password/email', 'ForgotPasswordController@sendResetLinkEmail')->name('password.reset.email');
views/layouts/main.blade.php+5 −0 modified@@ -71,6 +71,11 @@ @include('twill::partials.footer') </section> </div> + + <form class="visually-hidden" method="POST" action="{{ route('admin.logout') }}" data-logout-form> + @csrf + </form> + <script> window['{{ config('twill.js_namespace') }}'] = {}; window['{{ config('twill.js_namespace') }}'].version = '{{ config('twill.version') }}';
views/partials/navigation/_overlay_navigation.blade.php+1 −1 modified@@ -22,7 +22,7 @@ @if(isset($currentUser)) <a href="{{ route('admin.users.index') }}">{{ twillTrans('twill::lang.nav.cms-users') }}</a><br /> <a href="{{ route('admin.users.edit', $currentUser->id) }}">{{ twillTrans('twill::lang.nav.settings') }}</a><br /> - <a href="{{ route('admin.logout') }}">{{ twillTrans('twill::lang.nav.logout') }}</a> + <a href="#" data-logout-btn>{{ twillTrans('twill::lang.nav.logout') }}</a> @endif </div> </div>
views/partials/navigation/_user.blade.php+1 −1 modified@@ -14,7 +14,7 @@ <a href="{{ route('admin.users.index') }}">{{ twillTrans('twill::lang.nav.cms-users') }}</a> @endcan <a href="{{ route('admin.users.edit', $currentUser->id) }}">{{ twillTrans('twill::lang.nav.settings') }}</a> - <a href="{{ route('admin.logout') }}">{{ twillTrans('twill::lang.nav.logout') }}</a> + <a href="#" data-logout-btn>{{ twillTrans('twill::lang.nav.logout') }}</a> </div> </a17-dropdown> @endif
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-f99g-pg48-wrfcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-3932ghsaADVISORY
- github.com/area17/twill/commit/5cded9fcghsaWEB
- github.com/area17/twill/commit/81d80d1fbbdd8bb73c020f03c623fd4487bd9b78ghsax_refsource_MISCWEB
- github.com/area17/twill/commits/bab94c1eghsaWEB
- github.com/area17/twill/releases/tag/1.2.5ghsaWEB
- github.com/area17/twill/releases/tag/2.5.3ghsaWEB
- huntr.dev/bounties/6ef21e34-f6d9-445a-b657-375c53dc2b43ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.