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:
Serreau Jovann
2026-03-23 19:02:28 +01:00
parent 419c7f0a19
commit 10d9051880
3 changed files with 108 additions and 39 deletions

View File

@@ -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 %}