@djangocfg/nextjs 2.1.481 → 2.1.483
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/index.d.mts +54 -20
- package/dist/config/index.mjs +8 -3
- package/dist/config/index.mjs.map +1 -1
- package/dist/index.mjs +8 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
- package/src/config/createNextConfig.ts +26 -18
- package/src/config/csp.ts +42 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/nextjs",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.483",
|
|
4
4
|
"description": "Next.js server utilities: sitemap, health, OG images, contact forms, navigation, config",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nextjs",
|
|
@@ -144,9 +144,9 @@
|
|
|
144
144
|
"ai-docs": "tsx src/ai/cli.ts"
|
|
145
145
|
},
|
|
146
146
|
"peerDependencies": {
|
|
147
|
-
"@djangocfg/devtools": "^2.1.
|
|
148
|
-
"@djangocfg/i18n": "^2.1.
|
|
149
|
-
"@djangocfg/ui-core": "^2.1.
|
|
147
|
+
"@djangocfg/devtools": "^2.1.483",
|
|
148
|
+
"@djangocfg/i18n": "^2.1.483",
|
|
149
|
+
"@djangocfg/ui-core": "^2.1.483",
|
|
150
150
|
"next": "^16.2.2"
|
|
151
151
|
},
|
|
152
152
|
"peerDependenciesMeta": {
|
|
@@ -168,11 +168,11 @@
|
|
|
168
168
|
"serwist": "^9.5.11"
|
|
169
169
|
},
|
|
170
170
|
"devDependencies": {
|
|
171
|
-
"@djangocfg/devtools": "^2.1.
|
|
172
|
-
"@djangocfg/i18n": "^2.1.
|
|
173
|
-
"@djangocfg/layouts": "^2.1.
|
|
174
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
175
|
-
"@djangocfg/ui-core": "^2.1.
|
|
171
|
+
"@djangocfg/devtools": "^2.1.483",
|
|
172
|
+
"@djangocfg/i18n": "^2.1.483",
|
|
173
|
+
"@djangocfg/layouts": "^2.1.483",
|
|
174
|
+
"@djangocfg/typescript-config": "^2.1.483",
|
|
175
|
+
"@djangocfg/ui-core": "^2.1.483",
|
|
176
176
|
"@types/node": "^25.9.5",
|
|
177
177
|
"@types/react": "19.2.15",
|
|
178
178
|
"@types/react-dom": "19.2.3",
|
|
@@ -94,18 +94,19 @@ export interface BaseNextConfigOptions {
|
|
|
94
94
|
* additive; omit a key to leave that directive untouched.
|
|
95
95
|
* - omitted — tightest default (`frame-src 'self'`, etc.), same as today.
|
|
96
96
|
*
|
|
97
|
-
* **
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
97
|
+
* **Scripts nothing in this file can see are already handled.** Cloudflare
|
|
98
|
+
* Web Analytics (injected at the edge) and Google Analytics (injected at
|
|
99
|
+
* runtime by `BaseApp analytics={{ googleTrackingId }}`) are both switched on
|
|
100
|
+
* outside `next.config.ts`, so their origins are in the baseline and you do
|
|
101
|
+
* NOT need to list them — see `CSP_BASELINE_VENDORS`. Turn them off with
|
|
102
|
+
* `baseline: false`.
|
|
103
103
|
*
|
|
104
104
|
* @example
|
|
105
105
|
* ```ts
|
|
106
106
|
* // Declare the third parties this app actually uses — origins come from
|
|
107
107
|
* // the shared registry, so a vendor host change is fixed in one place.
|
|
108
|
-
*
|
|
108
|
+
* // (Cloudflare + GA are already in the baseline; no need to list them.)
|
|
109
|
+
* createBaseNextConfig({ csp: { vendors: ['stripe'] } })
|
|
109
110
|
*
|
|
110
111
|
* // Escape hatch for something not in the registry yet
|
|
111
112
|
* createBaseNextConfig({ csp: { frameSrc: ['https://www.youtube-nocookie.com'] } })
|
|
@@ -125,13 +126,18 @@ export interface BaseNextConfigOptions {
|
|
|
125
126
|
*/
|
|
126
127
|
vendors?: (CspVendor | (string & {}))[];
|
|
127
128
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
129
|
+
* Drop the baseline vendors (see `CSP_BASELINE_VENDORS`: Cloudflare Web
|
|
130
|
+
* Analytics + Google Analytics). They are on by default because each is
|
|
131
|
+
* switched on somewhere `next.config.ts` cannot read — the Cloudflare
|
|
132
|
+
* beacon is injected at the edge, and gtag.js is injected at runtime by
|
|
133
|
+
* `BaseApp analytics={{ googleTrackingId }}` — so an opt-in flag would
|
|
134
|
+
* only ever be discovered as a production console error.
|
|
135
|
+
*
|
|
136
|
+
* Set `false` for an app you know mounts neither, to get back the
|
|
137
|
+
* tightest `script-src`.
|
|
132
138
|
* @default true
|
|
133
139
|
*/
|
|
134
|
-
|
|
140
|
+
baseline?: boolean;
|
|
135
141
|
/** Extra `frame-src` origins (iframes this app embeds). */
|
|
136
142
|
frameSrc?: string[];
|
|
137
143
|
/** Extra `script-src` origins (third-party SDK scripts). */
|
|
@@ -287,13 +293,15 @@ export function createBaseNextConfig(
|
|
|
287
293
|
// "which hosts does Stripe need" is answered in exactly one place. The
|
|
288
294
|
// legacy `stripe: true` flag is just sugar for `vendors: ['stripe']` and
|
|
289
295
|
// resolves through that same registry — no second copy of the origins.
|
|
290
|
-
// Baseline vendors apply unless explicitly opted out.
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
296
|
+
// Baseline vendors apply unless explicitly opted out. They are baseline
|
|
297
|
+
// rather than per-app because each is enabled somewhere this file cannot
|
|
298
|
+
// read — Cloudflare's beacon is injected by the edge after this header is
|
|
299
|
+
// emitted, and gtag.js is injected at runtime by `BaseApp analytics={{
|
|
300
|
+
// googleTrackingId }}` from the app's settings.ts. Leaving either opt-in
|
|
301
|
+
// guarantees a silent production breakage in every app that forgets it
|
|
302
|
+
// (which was all of them). See CSP_BASELINE_VENDORS for the full rule.
|
|
295
303
|
const vendorNames = [
|
|
296
|
-
...(cspExtra?.
|
|
304
|
+
...(cspExtra?.baseline === false ? [] : CSP_BASELINE_VENDORS),
|
|
297
305
|
...(options.stripe ? ['stripe'] : []),
|
|
298
306
|
...(cspExtra?.vendors ?? []),
|
|
299
307
|
];
|
package/src/config/csp.ts
CHANGED
|
@@ -116,17 +116,50 @@ export type CspVendor = keyof typeof CSP_VENDOR_PRESETS;
|
|
|
116
116
|
/**
|
|
117
117
|
* Vendors applied to every app unless explicitly disabled.
|
|
118
118
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* 2026-07-22: all six DjangoCFG-served hosts were violating their own CSP.)
|
|
119
|
+
* The rule for this list: an origin belongs here when the app CANNOT DECLARE IT
|
|
120
|
+
* FROM `next.config.ts`, because whatever turns the script on lives somewhere
|
|
121
|
+
* the config file cannot read. Leaving such a vendor opt-in does not produce a
|
|
122
|
+
* build error or a dev warning — it produces a production-only console error
|
|
123
|
+
* that nobody sees until a user reports a broken page.
|
|
125
124
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
125
|
+
* - `cloudflare` — the Web Analytics beacon is rewritten into the HTML AT THE
|
|
126
|
+
* EDGE, after Next.js has already sent the CSP header. Nothing in the source
|
|
127
|
+
* tree reveals it. (Observed 2026-07-22: all six DjangoCFG-served hosts were
|
|
128
|
+
* violating their own CSP.)
|
|
129
|
+
*
|
|
130
|
+
* - `google-analytics` — gtag.js is injected AT RUNTIME by `@djangocfg/layouts`
|
|
131
|
+
* (`BaseApp analytics={{ googleTrackingId }}` → the lazy `ga4()` transport).
|
|
132
|
+
* That id is an ordinary TS value in the app's `settings.ts`, not an env var,
|
|
133
|
+
* so `next.config.ts` — a different module, evaluated in a different process,
|
|
134
|
+
* before any of that code runs — has no way to see whether GA is on. The app
|
|
135
|
+
* author enables analytics in one file and silently breaks CSP in another.
|
|
136
|
+
* (Observed 2026-07-23: cmdop `apps/web` blocked on
|
|
137
|
+
* `googletagmanager.com/gtag/js?id=G-YJ0F48S83F`.)
|
|
138
|
+
*
|
|
139
|
+
* - `youtube` / `vimeo` — `VideoPlayer` (`@djangocfg/ui-tools`) mounts these
|
|
140
|
+
* iframes AT RUNTIME from a URL inside a chat message (see
|
|
141
|
+
* `chat/messages/blocks/renderers/VideoBlock.tsx`). The app author enables a
|
|
142
|
+
* chat; whether any user ever pastes a YouTube link is not knowable at build
|
|
143
|
+
* time, so no `next.config.ts` can declare it honestly.
|
|
144
|
+
*
|
|
145
|
+
* Note what these have in common: the script/frame is turned on by the RUNTIME
|
|
146
|
+
* (the edge, a package, or an end user), never by the app author editing the
|
|
147
|
+
* config. Stripe is the counter-example and deliberately NOT here — a developer
|
|
148
|
+
* decides to mount a payment form, knows they did, and can say `vendors:
|
|
149
|
+
* ['stripe']`; putting it in the baseline would hand `frame-src js.stripe.com`
|
|
150
|
+
* to marketing sites that never take a payment.
|
|
151
|
+
*
|
|
152
|
+
* Do NOT add ordinary third parties here — anything an app knowingly mounts and
|
|
153
|
+
* can name in its own config belongs in `csp.vendors`, so unrelated apps keep
|
|
154
|
+
* the tighter policy. The cost of an entry here is that every app permits these
|
|
155
|
+
* hosts; that is only worth paying when the alternative is silent breakage.
|
|
128
156
|
*/
|
|
129
|
-
export const CSP_BASELINE_VENDORS: CspVendor[] = [
|
|
157
|
+
export const CSP_BASELINE_VENDORS: CspVendor[] = [
|
|
158
|
+
'cloudflare',
|
|
159
|
+
'google-analytics',
|
|
160
|
+
'youtube',
|
|
161
|
+
'vimeo',
|
|
162
|
+
];
|
|
130
163
|
|
|
131
164
|
/**
|
|
132
165
|
* Merge the named vendor presets into a single set of extra origins.
|