diff --git a/apps/installer/package.json b/apps/installer/package.json
index 4bb19b87..63c49336 100644
--- a/apps/installer/package.json
+++ b/apps/installer/package.json
@@ -15,7 +15,7 @@
},
"dependencies": {
"@hono/node-server": "^2.1.5",
- "@profullstack/footer": "^0.2.6",
+ "@profullstack/footer": "^0.4.0",
"hono": "^4.13.14"
},
"devDependencies": {
diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx
index 626ee202..ff7a6bf4 100644
--- a/apps/web/src/components/footer.tsx
+++ b/apps/web/src/components/footer.tsx
@@ -1,4 +1,5 @@
import Link from 'next/link';
+import { headers } from 'next/headers';
import { Footer as PfsFooter } from '@profullstack/footer/react';
import { useTranslations } from 'next-intl';
import { Logo } from '@/components/Logo';
@@ -61,6 +62,17 @@ const socialLinks = [
},
];
+/**
+ * The ring footer with the request's CSP nonce (middleware sets x-nonce), so the
+ * newsletter form's inline script (@profullstack/footer 0.4.0) runs under
+ * script-src 'nonce-...'.
+ */
+async function RingFooter({ tagline }: { tagline: string }) {
+ const nonce = (await headers()).get('x-nonce');
+ // exactOptionalPropertyTypes: pass nonce only when there is one.
+ return ;
+}
+
export function Footer() {
const t = useTranslations('footer');
return (
@@ -96,7 +108,7 @@ export function Footer() {
{/* Copyright + Profullstack ring, from @profullstack/footer */}
-
+
);
diff --git a/apps/web/src/middleware.ts b/apps/web/src/middleware.ts
index a038fdeb..8c4dba1d 100644
--- a/apps/web/src/middleware.ts
+++ b/apps/web/src/middleware.ts
@@ -44,7 +44,8 @@ function buildCsp(nonce: string, embeddable: boolean): string {
// path keeps the restrictive list.
embeddable ? 'frame-ancestors *' : "frame-ancestors 'self' chrome-extension:",
"base-uri 'self'",
- "form-action 'self'",
+ // The footer's newsletter form posts to pit.world when script is off.
+ "form-action 'self' https://pit.world",
].join('; ');
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f0edaa9a..de0925cc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -236,8 +236,8 @@ importers:
specifier: ^2.1.5
version: 2.1.5(hono@4.12.25)
'@profullstack/footer':
- specifier: ^0.2.6
- version: 0.2.6(react@19.3.1)
+ specifier: ^0.4.0
+ version: 0.4.0(react@19.3.1)
hono:
specifier: ^4.12.4
version: 4.12.25
@@ -390,7 +390,7 @@ importers:
version: 1.0.3
'@profullstack/footer':
specifier: latest
- version: 0.3.1(react@19.3.1)
+ version: 0.4.0(react@19.3.1)
'@profullstack/live-translate':
specifier: ^0.3.0
version: 0.3.0
@@ -2949,18 +2949,8 @@ packages:
'@profullstack/emailer@1.0.3':
resolution: {integrity: sha512-VyAtCcfnSI6LOwJvWogCr3ZNmEsaZw+iuWrqnosLsdcKsbRyCrCmXigMNZJZEjlgYlR+lK//PwNPG+xtCFvWAw==}
- '@profullstack/footer@0.2.6':
- resolution: {integrity: sha512-BKwbDszAc7e0mYIBiPjgr/5WS8WyDt8IW718s/zW/8pCWOIB1BGwSNs6fmImRSo5kqIjQKUVbXp22jZQ5GEipA==}
- engines: {node: '>=18'}
- hasBin: true
- peerDependencies:
- react: '>=18'
- peerDependenciesMeta:
- react:
- optional: true
-
- '@profullstack/footer@0.3.1':
- resolution: {integrity: sha512-uiiCgEuOZAFd4UKpOUIhAseVLTXp+tbGF9kosYDuW2M/+/A3QcWtUCu9lYGSNfOUnvnlscqrLjJC2uYZlJmufQ==}
+ '@profullstack/footer@0.4.0':
+ resolution: {integrity: sha512-EJZnMUQmWDi9Gzx6BAjogloeaPFFSSCHtHJuC9dsXiOz8M87hfn6Z+Zx9AMYWk8s6xemPrPlkXKGB3PZugkcEA==}
engines: {node: '>=18'}
hasBin: true
peerDependencies:
@@ -12304,11 +12294,7 @@ snapshots:
'@profullstack/emailer@1.0.3': {}
- '@profullstack/footer@0.2.6(react@19.3.1)':
- optionalDependencies:
- react: 19.3.1
-
- '@profullstack/footer@0.3.1(react@19.3.1)':
+ '@profullstack/footer@0.4.0(react@19.3.1)':
optionalDependencies:
react: 19.3.1