diff --git a/symfony/framework-bundle/7.2/config/packages/framework.yaml b/symfony/framework-bundle/7.2/config/packages/framework.yaml index 7e1ee1f1e..b4c42bd18 100644 --- a/symfony/framework-bundle/7.2/config/packages/framework.yaml +++ b/symfony/framework-bundle/7.2/config/packages/framework.yaml @@ -8,6 +8,11 @@ framework: #esi: true #fragments: true + # Enable stateless CSRF protection for forms and logins/logouts + form: { csrf_protection: { token_id: submit } } + csrf_protection: + stateless_token_ids: [submit, authenticate, logout] + when@test: framework: test: true diff --git a/symfony/stimulus-bundle/2.20/assets/bootstrap.js b/symfony/stimulus-bundle/2.20/assets/bootstrap.js new file mode 100644 index 000000000..2689398a6 --- /dev/null +++ b/symfony/stimulus-bundle/2.20/assets/bootstrap.js @@ -0,0 +1,2 @@ +// register any custom, 3rd party controllers here +// app.register('some_controller_name', SomeImportedController); diff --git a/symfony/stimulus-bundle/2.20/assets/controllers.json b/symfony/stimulus-bundle/2.20/assets/controllers.json new file mode 100644 index 000000000..a1c6e90cf --- /dev/null +++ b/symfony/stimulus-bundle/2.20/assets/controllers.json @@ -0,0 +1,4 @@ +{ + "controllers": [], + "entrypoints": [] +} diff --git a/symfony/stimulus-bundle/2.20/assets/controllers/csrf_protection_controller.js b/symfony/stimulus-bundle/2.20/assets/controllers/csrf_protection_controller.js new file mode 100644 index 000000000..6d42e5c9f --- /dev/null +++ b/symfony/stimulus-bundle/2.20/assets/controllers/csrf_protection_controller.js @@ -0,0 +1,60 @@ +var nameCheck = /^[-_a-zA-Z0-9]{4,22}$/; +var 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"]'); + + if (!csrfField) { + return; + } + + var csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value'); + var csrfToken = csrfField.value; + + if (!csrfCookie && nameCheck.test(csrfToken)) { + csrfField.setAttribute('data-csrf-protection-cookie-value', csrfCookie = csrfToken); + csrfField.value = csrfToken = btoa(String.fromCharCode.apply(null, (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(18)))); + } + + if (csrfCookie && tokenCheck.test(csrfToken)) { + var 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"]'); + + if (!csrfField) { + return; + } + + var csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value'); + + if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) { + event.detail.formSubmission.fetchRequest.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"]'); + + if (!csrfField) { + return; + } + + var 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'; + + document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie; + } +}); + +/* stimulusFetch: 'lazy' */ +export default 'csrf-protection-controller'; diff --git a/symfony/stimulus-bundle/2.20/assets/controllers/hello_controller.js b/symfony/stimulus-bundle/2.20/assets/controllers/hello_controller.js new file mode 100644 index 000000000..e847027bd --- /dev/null +++ b/symfony/stimulus-bundle/2.20/assets/controllers/hello_controller.js @@ -0,0 +1,16 @@ +import { Controller } from '@hotwired/stimulus'; + +/* + * This is an example Stimulus controller! + * + * Any element with a data-controller="hello" attribute will cause + * this controller to be executed. The name "hello" comes from the filename: + * hello_controller.js -> "hello" + * + * Delete this file or adapt it for your use! + */ +export default class extends Controller { + connect() { + this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js'; + } +} diff --git a/symfony/stimulus-bundle/2.20/manifest.json b/symfony/stimulus-bundle/2.20/manifest.json new file mode 100644 index 000000000..428949575 --- /dev/null +++ b/symfony/stimulus-bundle/2.20/manifest.json @@ -0,0 +1,41 @@ +{ + "bundles": { + "Symfony\\UX\\StimulusBundle\\StimulusBundle": ["all"] + }, + "copy-from-recipe": { + "assets/": "assets/" + }, + "aliases": ["stimulus", "stimulus-bundle"], + "conflict": { + "symfony/framework-bundle": "<7.2", + "symfony/security-csrf": "<7.2", + "symfony/webpack-encore-bundle": "<2.0", + "symfony/flex": "<1.20.0 || >=2.0.0,<2.3.0" + }, + "add-lines": [ + { + "file": "webpack.config.js", + "content": "\n // enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js)\n .enableStimulusBridge('./assets/controllers.json')", + "position": "after_target", + "target": ".splitEntryChunks()" + }, + { + "file": "assets/app.js", + "content": "import './bootstrap.js';", + "position": "top", + "warn_if_missing": true + }, + { + "file": "assets/bootstrap.js", + "content": "import { startStimulusApp } from '@symfony/stimulus-bridge';\n\n// Registers Stimulus controllers from controllers.json and in the controllers/ directory\nexport const app = startStimulusApp(require.context(\n '@symfony/stimulus-bridge/lazy-controller-loader!./controllers',\n true,\n /\\.[jt]sx?$/\n));", + "position": "top", + "requires": "symfony/webpack-encore-bundle" + }, + { + "file": "assets/bootstrap.js", + "content": "import { startStimulusApp } from '@symfony/stimulus-bundle';\n\nconst app = startStimulusApp();", + "position": "top", + "requires": "symfony/asset-mapper" + } + ] +} diff --git a/symfony/ux-turbo/2.20/manifest.json b/symfony/ux-turbo/2.20/manifest.json new file mode 100644 index 000000000..48149c60a --- /dev/null +++ b/symfony/ux-turbo/2.20/manifest.json @@ -0,0 +1,14 @@ +{ + "conflict": { + "symfony/framework-bundle": "<7.2", + "symfony/security-csrf": "<7.2" + }, + "add-lines": [ + { + "file": "config/packages/framework.yaml", + "position": "after_target", + "target": " csrf_protection:", + "content": " check_header: true" + } + ] +}
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: