Remove inline script from API doc, add CSP policy section
Security: - Move env switcher logic to assets/modules/api-env-switcher.js (no inline script) - Register in app.js via initApiEnvSwitcher() - Compliant with CSP script-src (no unsafe-inline needed for this page) API doc: - Add CSP policy section showing all authorized origins per directive - Table: script-src, connect-src, style-src, img-src, font-src, frame-src, form-action, object-src, worker-src - Note: inline scripts not allowed, must use nonce or external file Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import { initCommissionCalculator } from "./modules/commission-calculator.js"
|
||||
import { initCart } from "./modules/cart.js"
|
||||
import { initStripePayment } from "./modules/stripe-payment.js"
|
||||
import { initShare } from "./modules/share.js"
|
||||
import { initApiEnvSwitcher } from "./modules/api-env-switcher.js"
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initMobileMenu()
|
||||
@@ -25,6 +26,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
initCart()
|
||||
initStripePayment()
|
||||
initShare()
|
||||
initApiEnvSwitcher()
|
||||
|
||||
document.querySelectorAll('[data-confirm]').forEach(form => {
|
||||
form.addEventListener('submit', (e) => {
|
||||
|
||||
48
assets/modules/api-env-switcher.js
Normal file
48
assets/modules/api-env-switcher.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const ENVS = {
|
||||
sandbox: {
|
||||
prefix: '/api/sandbox',
|
||||
baseUrl: 'https://ticket.e-cosplay.fr/api/sandbox',
|
||||
color: 'text-orange-400',
|
||||
btnBg: 'bg-orange-500',
|
||||
desc: 'Environnement de test. Les donnees ne sont pas modifiees.',
|
||||
},
|
||||
live: {
|
||||
prefix: '/api/live',
|
||||
baseUrl: 'https://ticket.e-cosplay.fr/api/live',
|
||||
color: 'text-green-400',
|
||||
btnBg: 'bg-green-600',
|
||||
desc: 'Environnement de production. Les donnees sont reelles.',
|
||||
},
|
||||
}
|
||||
|
||||
const BTN_BASE = 'env-btn px-5 py-2 font-black uppercase text-xs tracking-widest transition-all cursor-pointer '
|
||||
|
||||
function switchEnv(env) {
|
||||
const config = ENVS[env]
|
||||
if (!config) return
|
||||
|
||||
document.querySelectorAll('.env-btn').forEach(btn => {
|
||||
const isActive = btn.dataset.env === env
|
||||
btn.className = BTN_BASE + (isActive ? config.btnBg + ' text-white' : 'bg-gray-800 text-gray-400 hover:text-white')
|
||||
})
|
||||
|
||||
const baseUrlEl = document.getElementById('env-base-url')
|
||||
if (baseUrlEl) baseUrlEl.textContent = config.baseUrl
|
||||
|
||||
const descEl = document.getElementById('env-description')
|
||||
if (descEl) descEl.textContent = config.desc
|
||||
|
||||
document.querySelectorAll('.api-env-prefix').forEach(el => {
|
||||
el.textContent = config.prefix
|
||||
el.className = 'api-env-prefix ' + config.color
|
||||
})
|
||||
}
|
||||
|
||||
export function initApiEnvSwitcher() {
|
||||
const switcher = document.getElementById('env-switcher')
|
||||
if (!switcher) return
|
||||
|
||||
document.querySelectorAll('.env-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => switchEnv(btn.dataset.env))
|
||||
})
|
||||
}
|
||||
@@ -276,45 +276,64 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-brutal overflow-hidden mt-12">
|
||||
<div class="bg-gray-900 text-white px-6 py-3">
|
||||
<h2 class="text-[10px] font-black uppercase tracking-widest">Politique de securite (CSP)</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<p class="font-bold text-sm text-gray-700 mb-4">E-Ticket applique une politique Content-Security-Policy stricte. Voici les origines autorisees par directive :</p>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-xs">
|
||||
<thead>
|
||||
<tr class="bg-gray-100">
|
||||
<th class="px-3 py-2 text-left font-black uppercase tracking-widest">Directive</th>
|
||||
<th class="px-3 py-2 text-left font-black uppercase tracking-widest">Origines autorisees</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="border-b border-gray-100">
|
||||
<td class="px-3 py-2 font-mono font-bold text-indigo-600">script-src</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600">'self' static.cloudflareinsights.com challenges.cloudflare.com cdn.jsdelivr.net js.stripe.com</td>
|
||||
</tr>
|
||||
<tr class="border-b border-gray-100">
|
||||
<td class="px-3 py-2 font-mono font-bold text-indigo-600">connect-src</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600">'self' cloudflareinsights.com static.cloudflareinsights.com challenges.cloudflare.com nominatim.openstreetmap.org cdn.jsdelivr.net api.stripe.com</td>
|
||||
</tr>
|
||||
<tr class="border-b border-gray-100">
|
||||
<td class="px-3 py-2 font-mono font-bold text-indigo-600">style-src</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600">'self' fonts.googleapis.com cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline'</td>
|
||||
</tr>
|
||||
<tr class="border-b border-gray-100">
|
||||
<td class="px-3 py-2 font-mono font-bold text-indigo-600">img-src</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600">'self' data: *.tile.openstreetmap.org *.basemaps.cartocdn.com cdn.jsdelivr.net</td>
|
||||
</tr>
|
||||
<tr class="border-b border-gray-100">
|
||||
<td class="px-3 py-2 font-mono font-bold text-indigo-600">font-src</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600">'self' cdnjs.cloudflare.com fonts.googleapis.com fonts.gstatic.com</td>
|
||||
</tr>
|
||||
<tr class="border-b border-gray-100">
|
||||
<td class="px-3 py-2 font-mono font-bold text-indigo-600">frame-src</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600">'self' stripe.com *.stripe.com js.stripe.com cloudflare.com *.cloudflareinsights.com challenges.cloudflare.com</td>
|
||||
</tr>
|
||||
<tr class="border-b border-gray-100">
|
||||
<td class="px-3 py-2 font-mono font-bold text-indigo-600">form-action</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600">'self' auth.esy-web.dev *.stripe.com checkout.stripe.com</td>
|
||||
</tr>
|
||||
<tr class="border-b border-gray-100">
|
||||
<td class="px-3 py-2 font-mono font-bold text-indigo-600">object-src</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600">'none'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-3 py-2 font-mono font-bold text-indigo-600">worker-src</td>
|
||||
<td class="px-3 py-2 font-mono text-gray-600">'self' blob:</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p class="mt-4 text-xs font-bold text-gray-400">Les scripts inline ne sont pas autorises. Tous les scripts doivent etre servis depuis une origine autorisee ou utiliser un nonce CSP.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const envs = {
|
||||
sandbox: { prefix: '/api/sandbox', baseUrl: 'https://ticket.e-cosplay.fr/api/sandbox', color: 'text-orange-400', btnBg: 'bg-orange-500', desc: 'Environnement de test. Les donnees ne sont pas modifiees.' },
|
||||
live: { prefix: '/api/live', baseUrl: 'https://ticket.e-cosplay.fr/api/live', color: 'text-green-400', btnBg: 'bg-green-600', desc: 'Environnement de production. Les donnees sont reelles.' },
|
||||
}
|
||||
|
||||
let current = 'sandbox'
|
||||
|
||||
const buttons = document.querySelectorAll('.env-btn')
|
||||
const baseUrlEl = document.getElementById('env-base-url')
|
||||
const descEl = document.getElementById('env-description')
|
||||
const prefixes = document.querySelectorAll('.api-env-prefix')
|
||||
|
||||
function switchEnv(env) {
|
||||
current = env
|
||||
const config = envs[env]
|
||||
|
||||
buttons.forEach(btn => {
|
||||
const isActive = btn.dataset.env === env
|
||||
btn.className = 'env-btn px-5 py-2 font-black uppercase text-xs tracking-widest transition-all cursor-pointer ' +
|
||||
(isActive ? config.btnBg + ' text-white' : 'bg-gray-800 text-gray-400 hover:text-white')
|
||||
})
|
||||
|
||||
baseUrlEl.textContent = config.baseUrl
|
||||
descEl.textContent = config.desc
|
||||
|
||||
prefixes.forEach(el => {
|
||||
el.textContent = config.prefix
|
||||
el.className = 'api-env-prefix ' + config.color
|
||||
})
|
||||
}
|
||||
|
||||
buttons.forEach(btn => {
|
||||
btn.addEventListener('click', () => switchEnv(btn.dataset.env))
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user