-
-
Notifications
You must be signed in to change notification settings - Fork 487
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[stimulus-bundle] Export CSRF protection helpers (#1375)
- Loading branch information
Showing
1 changed file
with
40 additions
and
22 deletions.
There are no files selected for viewing
62 changes: 40 additions & 22 deletions
62
symfony/stimulus-bundle/2.20/assets/controllers/csrf_protection_controller.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,79 @@ | ||
var nameCheck = /^[-_a-zA-Z0-9]{4,22}$/; | ||
var tokenCheck = /^[-_/+a-zA-Z0-9]{24,}$/; | ||
const nameCheck = /^[-_a-zA-Z0-9]{4,22}$/; | ||
const tokenCheck = /^[-_\/+a-zA-Z0-9]{24,}$/; | ||
|
||
// Generate and double-submit a CSRF token in a form field and a cookie, as defined by Symfony's SameOriginCsrfTokenManager | ||
document.addEventListener('submit', function (event) { | ||
var csrfField = event.target.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]'); | ||
generateCsrfToken(event.target); | ||
}, true); | ||
|
||
// When @hotwired/turbo handles form submissions, send the CSRF token in a header in addition to a cookie | ||
// The `framework.csrf_protection.check_header` config option needs to be enabled for the header to be checked | ||
document.addEventListener('turbo:submit-start', function (event) { | ||
const h = generateCsrfHeaders(event.detail.formSubmission); | ||
Object.keys(h).map(function (k) { | ||
event.detail.formSubmission.fetchRequest.headers[k] = h[k]; | ||
}); | ||
}); | ||
|
||
// When @hotwired/turbo handles form submissions, remove the CSRF cookie once a form has been submitted | ||
document.addEventListener('turbo:submit-end', function (event) { | ||
removeCsrfToken(event.detail.formSubmission.formElement); | ||
}); | ||
|
||
export function generateCsrfToken (formElement) { | ||
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]'); | ||
|
||
if (!csrfField) { | ||
return; | ||
} | ||
|
||
var csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value'); | ||
var csrfToken = csrfField.value; | ||
let csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value'); | ||
let csrfToken = csrfField.value; | ||
|
||
if (!csrfCookie && nameCheck.test(csrfToken)) { | ||
csrfField.setAttribute('data-csrf-protection-cookie-value', csrfCookie = csrfToken); | ||
csrfField.defaultValue = csrfToken = btoa(String.fromCharCode.apply(null, (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(18)))); | ||
csrfField.dispatchEvent(new Event('change', {bubbles: true})); | ||
csrfField.dispatchEvent(new Event('change', { bubbles: true })); | ||
} | ||
|
||
if (csrfCookie && tokenCheck.test(csrfToken)) { | ||
var cookie = csrfCookie + '_' + csrfToken + '=' + csrfCookie + '; path=/; samesite=strict'; | ||
const cookie = csrfCookie + '_' + csrfToken + '=' + csrfCookie + '; path=/; samesite=strict'; | ||
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie; | ||
} | ||
}); | ||
} | ||
|
||
// When @hotwired/turbo handles form submissions, send the CSRF token in a header in addition to a cookie | ||
// The `framework.csrf_protection.check_header` config option needs to be enabled for the header to be checked | ||
document.addEventListener('turbo:submit-start', function (event) { | ||
var csrfField = event.detail.formSubmission.formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]'); | ||
export function generateCsrfHeaders (formElement) { | ||
const headers = {}; | ||
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]'); | ||
|
||
if (!csrfField) { | ||
return; | ||
return headers; | ||
} | ||
|
||
var csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value'); | ||
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value'); | ||
|
||
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) { | ||
event.detail.formSubmission.fetchRequest.headers[csrfCookie] = csrfField.value; | ||
headers[csrfCookie] = csrfField.value; | ||
} | ||
}); | ||
|
||
// When @hotwired/turbo handles form submissions, remove the CSRF cookie once a form has been submitted | ||
document.addEventListener('turbo:submit-end', function (event) { | ||
var csrfField = event.detail.formSubmission.formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]'); | ||
return headers; | ||
} | ||
|
||
export function removeCsrfToken (formElement) { | ||
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]'); | ||
|
||
if (!csrfField) { | ||
return; | ||
} | ||
|
||
var csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value'); | ||
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value'); | ||
|
||
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) { | ||
var cookie = csrfCookie + '_' + csrfField.value + '=0; path=/; samesite=strict; max-age=0'; | ||
const cookie = csrfCookie + '_' + csrfField.value + '=0; path=/; samesite=strict; max-age=0'; | ||
|
||
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie; | ||
} | ||
}); | ||
} | ||
|
||
/* stimulusFetch: 'lazy' */ | ||
export default 'csrf-protection-controller'; |