mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-07 19:58:24 +00:00
Error corrections and temp debugger
This commit is contained in:
@@ -131,8 +131,18 @@ export default function ContactForm({ turnstileSiteKey }: Props) {
|
||||
// widget rapidly flashing. Instead we leave the widget in its errored
|
||||
// state; the next form submission will reset it for a fresh attempt.
|
||||
// The error surfaces as the token being empty, which onSubmit reports.
|
||||
'error-callback': () => {
|
||||
'error-callback': (error: unknown) => {
|
||||
turnstileTokenRef.current = '';
|
||||
// TEMPORARY DIAGNOSTIC: Turnstile passes an error code/string when the
|
||||
// widget fails its challenge (e.g. "110200-domain-not-authorized",
|
||||
// "300030-..."). The 400 on challenges.cloudflare.com/cdn-cgi/
|
||||
// challenge-platform/... originates here. Log it so the browser
|
||||
// console shows the exact reason. Remove once resolved.
|
||||
console.warn('[contact] turnstile widget error:', error, {
|
||||
sitekey: turnstileSiteKey,
|
||||
origin: location.origin,
|
||||
hostname: location.hostname,
|
||||
});
|
||||
},
|
||||
theme: 'dark',
|
||||
},
|
||||
|
||||
+24
-7
@@ -31,9 +31,22 @@
|
||||
* need the origin. (SRI is intentionally NOT applied to the Turnstile
|
||||
* loader — see ContactForm.tsx for why.)
|
||||
*
|
||||
* Everything else is 'self': fonts are self-hosted (Fontsource woff2), images
|
||||
* are self-hosted (board-game covers fetched at build time, local hero
|
||||
* portrait), and there is no analytics/tracking.
|
||||
* Fonts: Fontsource variable fonts (woff2) are self-hosted, but Vite inlines
|
||||
* font subsets below build.assetsInlineLimit (default 4096 bytes) as
|
||||
* data:font/woff2 base64 URIs. At least two small cyrillic-ext subsets fall
|
||||
* under that threshold, so font-src MUST include data: or they are blocked.
|
||||
*
|
||||
* Images: board-game covers are self-hosted (fetched at build time by
|
||||
* scripts/fetch-bgg-covers.mjs) and the hero portrait is local, but TCG deck
|
||||
* featured-card art is hotlinked live from Archidekt's card-image bucket at
|
||||
* storage.googleapis.com (see src/lib/archidekt.ts). img-src therefore allows
|
||||
* that origin in addition to self/data. There is no analytics/tracking.
|
||||
*
|
||||
* script-src deliberately does NOT include data: even though Turnstile's
|
||||
* challenge runtime injects an empty data:application/javascript, resource
|
||||
* (which CSP therefore blocks — it's functionally inert). Allowing data:
|
||||
* in script-src would permit arbitrary scripts via data URIs, a serious
|
||||
* regression. The block is harmless; do not "fix" it by widening script-src.
|
||||
*/
|
||||
import { defineMiddleware } from 'astro:middleware';
|
||||
|
||||
@@ -50,10 +63,14 @@ const CONTENT_SECURITY_POLICY = [
|
||||
// Astro/Tailwind inject inline styles at runtime, so 'unsafe-inline' stays
|
||||
// for now. Tightening this requires hashing the generated styles.
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
// All images self-hosted; data: covers inlined SVG / data-URI usage.
|
||||
"img-src 'self' data:",
|
||||
// Self-hosted variable fonts (woff2) via @fontsource-variable.
|
||||
"font-src 'self'",
|
||||
// Self-hosted images + data: for inlined SVG/data-URIs. The
|
||||
// storage.googleapis.com origin serves TCG deck featured-card art hotlinked
|
||||
// from Archidekt's card-image bucket (see src/lib/archidekt.ts).
|
||||
"img-src 'self' data: https://storage.googleapis.com",
|
||||
// Self-hosted variable fonts (woff2) via @fontsource-variable. data: is
|
||||
// required because Vite inlines font subsets under assetsInlineLimit as
|
||||
// data:font/woff2 base64 URIs.
|
||||
"font-src 'self' data:",
|
||||
// Same-origin fetch/XHR (the contact form POSTs to /api/contact) plus the
|
||||
// Turnstile siteverify calls the widget makes from the browser.
|
||||
"connect-src 'self' https://challenges.cloudflare.com",
|
||||
|
||||
@@ -94,7 +94,17 @@ async function verifyTurnstile(token: string | undefined, ip: string): Promise<b
|
||||
method: 'POST',
|
||||
body: form,
|
||||
});
|
||||
const data = (await res.json()) as { success: boolean };
|
||||
const data = (await res.json()) as { success: boolean; 'error-codes'?: string[] };
|
||||
// TEMPORARY DIAGNOSTIC: Cloudflare returns HTTP 200 even on failure, with the
|
||||
// reason in error-codes (e.g. "invalid-input-secret", "invalid-input-response",
|
||||
// "timeout-or-duplicate"). Log it so we can see why verification is failing.
|
||||
// Remove this log once the Turnstile issue is resolved.
|
||||
if (!data.success) {
|
||||
console.warn('[contact] turnstile siteverify failed:', {
|
||||
status: res.status,
|
||||
errorCodes: data['error-codes'] ?? [],
|
||||
});
|
||||
}
|
||||
return Boolean(data.success);
|
||||
} catch (err) {
|
||||
// Network error talking to Cloudflare. Fail closed: reject the submission
|
||||
|
||||
Reference in New Issue
Block a user