@networkpro/web 1.7.9 → 1.9.0

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/README.md CHANGED
@@ -17,6 +17,8 @@ This file is part of Network Pro.
17
17
  [![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat)](https://github.com/prettier/prettier) [![stylelint](https://img.shields.io/badge/stylelint-%23747474?style=flat&logo=stylelint&logoSize=auto&labelColor=%23263238)](https://stylelint.io/)
18
18
  [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/netwk-pro/netwk-pro.github.io/blob/master/CODE_OF_CONDUCT.md)
19
19
 
20
+ <section id="top">
21
+
20
22
  ## 🚀 Project Overview
21
23
 
22
24
  This GitHub repository powers the official web presence of **[Network Pro Strategies](https://netwk.pro/about)** — a privacy-first consultancy specializing in cybersecurity, network engineering, and information security. We also lead public advocacy efforts promoting digital privacy and responsible cyber policy.
@@ -26,36 +28,64 @@ Built with [SvelteKit](https://kit.svelte.dev/) and deployed via [Netlify](https
26
28
 
27
29
  All infrastructure and data flows are designed with **maximum transparency, self-hosting, and user privacy** in mind.
28
30
 
29
- ### 📁 Repository Structure
31
+ </section>
32
+
33
+ ### Table of Contents
34
+
35
+ - [Repository Structure](#structure)
36
+ - [Getting Started](#getting-started)
37
+ - [Configuration](#configuration)
38
+ - [Service Worker Utilities](#sw-utilities)
39
+ - [CSP Report Handler](#cspreport)
40
+ - [Testing](#testing)
41
+ - [Recommended Toolchain](#toolchain)
42
+ - [Tooling Configuration](#toolconfig)
43
+ - [Available Scripts](#scripts)
44
+ - [License](#license)
45
+ - [Questions](#questions)
46
+
47
+ ---
48
+
49
+ <section id="structure">
50
+
51
+ ## 📁 Repository Structure
30
52
 
31
53
  ```bash
32
- .
33
- ├── .github/workflows/ # CI workflows and automation
34
- ├── .vscode/
35
- ├── customData.json # Custom CSS data for FontAwesome icons
36
- │ ├── extensions.json # Recommended VSCodium / VS Code extensions
37
- │ ├── extensions.jsonc # Commented version of extensions.json for reference
38
- └── settings.json # User settings for VSCodium / VS Code
39
- ├── netlify-functions/
40
- │ └── cspReport.js # Serverless function to receive and log CSP violation reports
41
- ├── scripts/ # General utility scripts
42
- ├── src/
43
- ├── lib/ # Reusable components, styles, utilities
44
- ├── routes/ # SvelteKit routes (+page.svelte, +page.server.js)
45
- ├── hooks.client.ts # Handles PWA install prompt and logs client errors
46
- │ ├── hooks.server.js # Injects CSP headers and permissions policy
47
- │ ├── app.html # SvelteKit entry HTML with CSP/meta/bootentry
48
- └── service-worker.js # Custom Service Worker
49
- ├── static/ # Static assets served at root
50
- │ ├── manifest.json # Manifest file for PWA configuration
51
- │ ├── robots.txt # Instructions for web robots
52
- │ └── sitemap.xml # Sitemap for search engines
53
- ├── tests/
54
- ├── e2e/ # End-to-end Playwright tests
55
- │ └── unit/ # Vite unit tests
56
- ├── _redirects # Netlify redirects
57
- ├── netlify.toml # Netlify configuration
58
- └── ...
54
+ .
55
+ ├── .github/
56
+ │ └── workflows/ # CI workflows (e.g. test, deploy)
57
+ ├── .vscode/
58
+ │ ├── customData.json # Custom CSS IntelliSense (e.g. FontAwesome)
59
+ │ ├── extensions.json # Recommended VS Code / VSCodium extensions
60
+ ├── extensions.jsonc # Commented version of extensions.json
61
+ │ └── settings.json # Workspace settings
62
+ ├── netlify/
63
+ ├── edge-functions/
64
+ │ │ └── csp-report.js # Receives CSP violation reports
65
+ └── netlify.toml # Netlify configuration
66
+ ├── scripts/ # General-purpose utility scripts
67
+ ├── src/
68
+ │ ├── app.html # Entry HTML (CSP meta, bootstrapping)
69
+ │ ├── hooks.client.ts # PWA install prompt & client-side logging
70
+ ├── hooks.server.js # Injects CSP headers and permissions policy
71
+ ├── lib/ # Components, utilities, types, styles
72
+ ├── components/ # Svelte components
73
+ ├── data/ # Custom data (e.g. JSON, metadata, constants)
74
+ └── utils/ # Helper utilities
75
+ ├── routes/ # SvelteKit pages (+page.svelte, +server.js)
76
+ └── service-worker.js # Custom PWA service worker
77
+ ├── static/ # Public assets served at site root
78
+ ├── disableSw.js # Service worker bypass (via ?nosw param)
79
+ ├── manifest.json # PWA metadata
80
+ │ ├── robots.txt # SEO: allow/disallow crawlers
81
+ │ └── sitemap.xml # SEO: full site map
82
+ ├── tests/
83
+ │ ├── e2e/ # Playwright end-to-end tests
84
+ │ ├── internal/ # Internal audit/test helpers
85
+ │ │ └── auditCoverage.test.js # Warns about untested source modules
86
+ │ └── unit/ # Vitest unit tests
87
+ ├── _redirects # Netlify redirect rules
88
+ └── package.json # Project manifest (scripts, deps, etc.)
59
89
  ```
60
90
 
61
91
  &nbsp;
@@ -74,8 +104,14 @@ tests/
74
104
  └── ...
75
105
  ```
76
106
 
107
+ </section>
108
+
109
+ <sub>[Back to top](#top)</sub>
110
+
77
111
  ---
78
112
 
113
+ <section id="getting-started">
114
+
79
115
  ## 🛠 Getting Started
80
116
 
81
117
  ### 📦 Environment Setup
@@ -119,9 +155,9 @@ npm install
119
155
  > You can also use `bootstrap.local.sh` to automate the steps above and more (optional).
120
156
  > `ENV_MODE` controls local tooling behavior — it is not used by the app runtime directly.
121
157
 
122
- ---
158
+ &nbsp;
123
159
 
124
- #### 💾 Version Enforcement
160
+ ### 💾 Version Enforcement
125
161
 
126
162
  To ensure consistent environments across contributors and CI systems, this project enforces specific Node.js and npm versions via the `"engines"` field in `package.json`:
127
163
 
@@ -185,7 +221,13 @@ node -v # Should fall within engines.node
185
221
  npm -v # Should fall within engines.npm
186
222
  ```
187
223
 
188
- &nbsp;
224
+ </section>
225
+
226
+ <sub>[Back to top](#top)</sub>
227
+
228
+ ---
229
+
230
+ <section id="configuration">
189
231
 
190
232
  ## 🛡️ Configuration
191
233
 
@@ -208,36 +250,117 @@ To re-enable nonce generation for inline scripts in the future:
208
250
 
209
251
  > 💡 The `[headers]` block in `netlify.toml` has been deprecated — all headers are now set dynamically from within SvelteKit.
210
252
 
211
- ---
253
+ &nbsp;
212
254
 
213
255
  ### 🧭 `hooks.client.ts`
214
256
 
215
- This lightweight hook enhances client experience:
257
+ Located at `src/hooks.client.ts`, this file is currently limited to handling uncaught client-side errors via the `handleError()` lifecycle hook.
258
+
259
+ Client-side PWA logic (such as handling the `beforeinstallprompt` event, checking browser compatibility, and registering the service worker) has been moved to `src/lib/registerServiceWorker.js` for better modularity and testability.
216
260
 
217
- - Handles the `beforeinstallprompt` event to support progressive web app (PWA) install flows
218
- - Provides a `handleError()` hook that logs uncaught client-side errors
261
+ > 💡 This separation ensures that error handling is isolated from PWA lifecycle logic, making both concerns easier to maintain.
219
262
 
220
- Located at `src/hooks.client.ts`, it is automatically used by the SvelteKit runtime during client boot.
263
+ </section>
264
+
265
+ <sub>[Back to top](#top)</sub>
221
266
 
222
267
  ---
223
268
 
224
- ### 📣 CSP Report Handler
269
+ <section id="sw-utilities">
270
+
271
+ ## ⚙️ Service Worker Utilities
272
+
273
+ This project includes modular service worker management to support PWA functionality, update lifecycles, and debugging workflows.
274
+
275
+ ### ✅ `registerServiceWorker.js`
276
+
277
+ Located at `src/lib/registerServiceWorker.js`, this module handles:
278
+
279
+ - **Service worker registration** (`service-worker.js`)
280
+ - **Update lifecycle**: prompts users when new content is available
281
+ - **Cache hygiene**: removes unexpected caches not prefixed with `cache-`
282
+ - **Install prompt support**: dispatches a `pwa-install-available` event for custom handling
283
+ - **Firefox compatibility**: skips registration in Firefox during localhost development
284
+
285
+ This function is typically called during client boot from `+layout.svelte` or another root-level component.
286
+
287
+ > ℹ️ The service worker will not register if the `?nosw` flag is present or if `window.__DISABLE_SW__` is set (see below).
288
+
289
+ &nbsp;
290
+
291
+ ### 🧹 `unregisterServiceWorker.js`
292
+
293
+ Located at `src/lib/unregisterServiceWorker.js`, this utility allows for manual deactivation of service workers during debugging or user opt-out flows.
294
+
295
+ It unregisters **all active service worker registrations** and logs the result.
296
+
297
+ &nbsp;
298
+
299
+ ### 🚫 `disableSw.js`
300
+
301
+ Located at `static/disableSw.js`, this file sets a global flag if the URL contains the `?nosw` query parameter:
302
+
303
+ ```js
304
+ if (location.search.includes("nosw")) {
305
+ window.__DISABLE_SW__ = true;
306
+ }
307
+ ```
308
+
309
+ This flag is used by `registerServiceWorker.js` to bypass registration. It's helpful for testing environments, browser compatibility checks, or simulating first-load conditions without service worker interference.
310
+
311
+ To use:
312
+
313
+ ```bash
314
+ https://netwk.pro/?nosw
315
+ ```
316
+
317
+ > 💡 `disableSw.js` is loaded via a `<script>` tag in `app.html` from the `static` directory. This ensures the `__DISABLE_SW__` flag is set before any service worker logic runs.
318
+
319
+ &nbsp;
320
+
321
+ #### 🔧 Example Usage
322
+
323
+ To register the service worker conditionally, call the function from client code:
324
+
325
+ ```js
326
+ import { registerServiceWorker } from "$lib/registerServiceWorker.js";
327
+
328
+ registerServiceWorker();
329
+ ```
330
+
331
+ You can optionally import unregisterServiceWorker() in a debug menu or settings panel to let users opt out of offline behavior.
332
+
333
+ </section>
334
+
335
+ <sub>[Back to top](#top)</sub>
336
+
337
+ ---
338
+
339
+ <section id="cspreport">
340
+
341
+ ## 📣 CSP Report Handler
225
342
 
226
343
  To receive and inspect CSP violation reports in development or production, the repo includes a Netlify-compatible function at:
227
344
 
228
345
  ```bash
229
- netlify-functions/cspReport.js
346
+ netlify/edge-functions/csp-report.js
230
347
  ```
231
348
 
232
- This function receives reports sent to `/functions/cspReport` and logs them to the console. You can later integrate with logging tools or alerts (e.g., via email, Slack, or SIEM ingestion).
349
+ This Edge Function receives Content Security Policy (CSP) violation reports at `/api/csp-report` and logs relevant details to the console. High-risk violations (e.g., `script-src`, `form-action`) also trigger real-time alerts via `ntfy`. You can further integrate with logging tools, SIEM platforms, or notification systems as needed.
233
350
 
234
351
  Make sure to include the `report-uri` directive in your CSP header:
235
352
 
236
353
  ```bash
237
- Content-Security-Policy: ...; report-uri /.netlify/functions/cspReport;
354
+ Content-Security-Policy: ...; report-uri /api/csp-report;
238
355
  ```
239
356
 
240
- &nbsp;
357
+ </section>
358
+
359
+ <sub>[Back to top](#top)</sub>
360
+
361
+ ---
362
+
363
+ <section id="testing">
241
364
 
242
365
  ## 🧪 Testing
243
366
 
@@ -299,8 +422,14 @@ npm run test:coverage # Collect code coverage reports
299
422
  npm run test:e2e # Runs Playwright E2E tests (with one retry on failure)
300
423
  ```
301
424
 
425
+ <!-- markdownlint-disable MD028 -->
426
+
427
+ > The unit test suite includes a coverage audit (`auditCoverage.test.js`) that warns when source files in `src/` or `scripts/` do not have corresponding unit tests. This helps track test completeness without failing CI.
428
+
302
429
  > Playwright will retry failed tests once `(--retries=1)` to reduce false negatives from transient flakiness (network, render delay, etc.).
303
430
 
431
+ <!-- markdownlint-enable MD028 -->
432
+
304
433
  Audit your app using Lighthouse:
305
434
 
306
435
  ```bash
@@ -332,8 +461,14 @@ You can also audit locally using Chrome DevTools → Lighthouse tab for on-the-f
332
461
 
333
462
  <!-- markdownlint-disable MD028 -->
334
463
 
464
+ </section>
465
+
466
+ <sub>[Back to top](#top)</sub>
467
+
335
468
  ---
336
469
 
470
+ <section id="toolchain">
471
+
337
472
  ## 🛠 Recommended Toolchain
338
473
 
339
474
  To streamline development and align with project conventions, we recommend the following setup — especially for contributors without a strong existing preference.
@@ -371,8 +506,14 @@ npm run lint:fix
371
506
  npm run format:fix
372
507
  ```
373
508
 
509
+ </section>
510
+
511
+ <sub>[Back to top](#top)</sub>
512
+
374
513
  ---
375
514
 
515
+ <section id="toolconfig">
516
+
376
517
  ## ⚙️ Tooling Configuration
377
518
 
378
519
  All linting, formatting, and version settings are defined in versioned project config files:
@@ -395,8 +536,14 @@ These are the same rules used by CI and automation, so aligning your local setup
395
536
 
396
537
  > Note: `.vscode/extensions.json` defines a minimal recommended dev stack for VSCodium / VS Code. These extensions are **optional but thoughtfully curated** to improve developer experience without introducing bloat.
397
538
 
539
+ </section>
540
+
541
+ <sub>[Back to top](#top)</sub>
542
+
398
543
  ---
399
544
 
545
+ <section id="scripts">
546
+
400
547
  ## 📜 Available Scripts
401
548
 
402
549
  The following CLI commands are available via `npm run <script>` or `pnpm run <script>`.
@@ -412,20 +559,20 @@ The following CLI commands are available via `npm run <script>` or `pnpm run <sc
412
559
  | `build:netlify` | Build using Netlify CLI |
413
560
  | `css:bundle` | Bundle and minify CSS |
414
561
 
415
- ---
562
+ &nbsp;
416
563
 
417
564
  ### ✅ Pre-check / Sync
418
565
 
419
- | Script | Description |
420
- | ------------- | ------------------------------------------------------------ |
421
- | `prepare` | Run SvelteKit sync |
422
- | `check` | Run SvelteKit sync and type check with `svelte-check` |
423
- | `check:watch` | Watch mode for type checks |
424
- | `check:node` | Validate Node & npm versions match package.json `engines` |
425
- | `checkout` | Full local validation: check versions, test, lint, typecheck |
426
- | `verify` | Alias for `checkout` |
566
+ | Script | Description |
567
+ | ------------- | ----------------------------------------------------------------------------------- |
568
+ | `prepare` | Run SvelteKit sync |
569
+ | `check` | Run SvelteKit sync and type check with `svelte-check` |
570
+ | `check:watch` | Watch mode for type checks |
571
+ | `check:node` | Validate Node & npm versions match package.json `engines` |
572
+ | `checkout` | Full local validation: check versions, test (incl. audit coverage), lint, typecheck |
573
+ | `verify` | Alias for `checkout` |
427
574
 
428
- ---
575
+ &nbsp;
429
576
 
430
577
  ### 🧹 Cleanup & Maintenance
431
578
 
@@ -435,7 +582,7 @@ The following CLI commands are available via `npm run <script>` or `pnpm run <sc
435
582
  | `clean` | Fully reset environment and reinstall |
436
583
  | `upgrade` | Update all dependencies via `npm-check-updates` |
437
584
 
438
- ---
585
+ &nbsp;
439
586
 
440
587
  <!-- markdownlint-disable MD024 -->
441
588
 
@@ -443,17 +590,17 @@ The following CLI commands are available via `npm run <script>` or `pnpm run <sc
443
590
 
444
591
  <!-- markdownlint-enable MD024 -->
445
592
 
446
- | Script | Description |
447
- | --------------- | ------------------------------------------------------ |
448
- | `test` | Alias for `test:all` |
449
- | `test:all` | Run both client and server test suites |
450
- | `test:client` | Run client tests with Vitest |
451
- | `test:server` | Run server-side tests with Vitest |
452
- | `test:watch` | Watch mode for client tests |
453
- | `test:coverage` | Collect coverage from both client and server |
454
- | `test:e2e` | Runs E2E tests with up to 1 automatic retry on failure |
593
+ | Script | Description |
594
+ | --------------- | ------------------------------------------------------------- |
595
+ | `test` | Alias for `test:all` |
596
+ | `test:all` | Run both client and server test suites (incl. audit coverage) |
597
+ | `test:client` | Run client tests with Vitest |
598
+ | `test:server` | Run server-side tests with Vitest |
599
+ | `test:watch` | Watch mode for client tests |
600
+ | `test:coverage` | Collect coverage from both client and server |
601
+ | `test:e2e` | Runs E2E tests with up to 1 automatic retry on failure |
455
602
 
456
- ---
603
+ &nbsp;
457
604
 
458
605
  ### 🧼 Linting & Formatting
459
606
 
@@ -468,7 +615,7 @@ The following CLI commands are available via `npm run <script>` or `pnpm run <sc
468
615
  | `format` | Run Prettier formatting check |
469
616
  | `format:fix` | Auto-format code using Prettier |
470
617
 
471
- ---
618
+ &nbsp;
472
619
 
473
620
  ### 💡 Lighthouse / Performance
474
621
 
@@ -477,17 +624,17 @@ The following CLI commands are available via `npm run <script>` or `pnpm run <sc
477
624
  | `lhci` | Alias for Lighthouse CI |
478
625
  | `lhci:run` | Run Lighthouse CI autorun |
479
626
 
480
- ---
627
+ &nbsp;
481
628
 
482
629
  ### 📋 Audits / Validation
483
630
 
484
- | Script | Description |
485
- | --------------- | -------------------------------------------- |
486
- | `audit:scripts` | Check for untested utility scripts |
487
- | `head:flatten` | Flatten headers for Netlify |
488
- | `head:validate` | Validate headers file against project config |
631
+ | Script | Description |
632
+ | ---------------- | ---------------------------------------------------- |
633
+ | `audit:coverage` | Warn about untested modules in `src/` and `scripts/` |
634
+ | `head:flatten` | Flatten headers for Netlify |
635
+ | `head:validate` | Validate headers file against project config |
489
636
 
490
- ---
637
+ &nbsp;
491
638
 
492
639
  ### 🔄 Lifecycle Hooks
493
640
 
@@ -495,10 +642,14 @@ The following CLI commands are available via `npm run <script>` or `pnpm run <sc
495
642
  | ------------- | ----------------------------------- |
496
643
  | `postinstall` | Ensures version check after install |
497
644
 
498
- &nbsp;
645
+ </section>
646
+
647
+ <sub>[Back to top](#top)</sub>
499
648
 
500
649
  ---
501
650
 
651
+ <section id="license">
652
+
502
653
  ## 🧾 License
503
654
 
504
655
  This project is licensed under:
@@ -509,11 +660,21 @@ This project is licensed under:
509
660
 
510
661
  Source code, branding, and visual assets are subject to reuse and distribution terms specified on our [Legal, Copyright, and Licensing page](https://netwk.pro/license).
511
662
 
512
- &nbsp;
663
+ </section>
664
+
665
+ <sub>[Back to top](#top)</sub>
666
+
667
+ ---
668
+
669
+ <section id="questions">
513
670
 
514
671
  ## 🙋‍♂️Questions?
515
672
 
516
- Reach out via [netwk.pro/contact](https://netwk.pro/contact), open an issue on this repo, or email us directly at `contact (at) s.neteng.pro`.
673
+ Reach out via our [Contact Form](https://netwk.pro/contact), open an issue on this repo, or email us directly at `support (at) neteng.pro`.
674
+
675
+ </section>
676
+
677
+ <sub>[Back to top](#top)</sub>
517
678
 
518
679
  &nbsp;
519
680
 
@@ -522,7 +683,7 @@ _Designed for professionals. Hardened for privacy. Built with intent._
522
683
 
523
684
  ---
524
685
 
525
- <div style="font-size: 12px; text-align: center;">
686
+ <span style="font-size: 12px; text-align: center;">
526
687
 
527
688
  Copyright &copy; 2025
528
689
  **[Network Pro Strategies](https://netwk.pro) (Network Pro&trade;)**
@@ -531,4 +692,6 @@ Network Pro&trade;, the shield logo, and the "Locking Down Networks&trade;" slog
531
692
 
532
693
  Licensed under **[CC BY 4.0](https://netwk.pro/license#cc-by)** and the **[GNU GPL](https://netwk.pro/license#gnu-gpl)**, as published by the [Free Software Foundation](https://www.fsf.org), either version 3 of the License, or (at your option) any later version.
533
694
 
534
- </div>
695
+ </span>
696
+
697
+ <!-- cspell:ignore cspreport toolconfig -->
package/cspell.json CHANGED
@@ -19,6 +19,7 @@
19
19
  "homescreen",
20
20
  "Izzy",
21
21
  "lhci",
22
+ "lifecycles",
22
23
  "lighthouseci",
23
24
  "lighthouserc",
24
25
  "lightningcss",
@@ -35,6 +36,7 @@
35
36
  "nosniff",
36
37
  "nosw",
37
38
  "npmjs",
39
+ "ntfy",
38
40
  "obtainium",
39
41
  "posthog",
40
42
  "SIEM",
@@ -45,6 +47,7 @@
45
47
  "subsites",
46
48
  "Supercookie",
47
49
  "supercookies",
50
+ "unregisters",
48
51
  "urlcheck",
49
52
  "vcard",
50
53
  "vite",
@@ -0,0 +1,151 @@
1
+ /* ==========================================================================
2
+ edge-functions/csp-report.js
3
+
4
+ Copyright © 2025 Network Pro Strategies (Network Pro™)
5
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
6
+ This file is part of Network Pro.
7
+ ========================================================================== */
8
+
9
+ /**
10
+ * @file csp-report.js
11
+ * @description Netlify Edge Function to handle CSP violation reports.
12
+ *
13
+ * Accepts POST requests to /api/csp-report and logs relevant CSP reports.
14
+ * Filters out common low-value reports (e.g., img-src) to reduce invocation
15
+ * cost. Alerts on high-risk violations via ntfy topic.
16
+ *
17
+ * @module netlify/edge-functions
18
+ * @author SunDevil311
19
+ * @updated 2025-05-31
20
+ */
21
+
22
+ /**
23
+ * Netlify Edge Function entry point for CSP reporting.
24
+ *
25
+ * @param {Request} request - The incoming HTTP request object
26
+ * @param {import('@netlify/edge-functions').Context} _context - The Netlify Edge Function context (unused)
27
+ * @returns {Promise<Response>} HTTP Response with status 204 or 405
28
+ */
29
+ export default async (request, _context) => {
30
+ if (request.method !== "POST") {
31
+ return new Response("Method Not Allowed", { status: 405 });
32
+ }
33
+
34
+ try {
35
+ const body = await request.json();
36
+ const report = body["csp-report"];
37
+
38
+ // Ignore if report is missing or malformed
39
+ if (!report || typeof report !== "object") {
40
+ return new Response(null, { status: 204 });
41
+ }
42
+
43
+ const violated = report["violated-directive"] ?? "";
44
+ const blockedUri = report["blocked-uri"] ?? "";
45
+
46
+ // Filter: Skip img-src violations and empty URIs
47
+ const ignored = [
48
+ violated.startsWith("img-src"),
49
+ blockedUri === "",
50
+ blockedUri === "about",
51
+ blockedUri.startsWith("chrome-extension://"),
52
+ blockedUri.startsWith("moz-extension://"),
53
+ ].some(Boolean);
54
+
55
+ if (ignored) {
56
+ return new Response(null, { status: 204 });
57
+ }
58
+
59
+ // Send alert for high-risk directives
60
+ await sendToNtfy(violated, blockedUri, report);
61
+
62
+ // Log useful violations
63
+ console.log("[CSP-Edge] Violation:", {
64
+ directive: violated,
65
+ uri: blockedUri,
66
+ referrer: report["referrer"],
67
+ source: report["source-file"],
68
+ line: report["line-number"],
69
+ });
70
+ } catch (err) {
71
+ console.warn("[CSP-Edge] Failed to parse CSP report:", err.message);
72
+ }
73
+
74
+ return new Response(null, { status: 204 });
75
+ };
76
+
77
+ const recentViolations = new Map();
78
+ const VIOLATION_TTL_MS = 60_000;
79
+
80
+ /**
81
+ * Sends a high-priority alert to your ntfy topic for high-risk CSP violations.
82
+ * Applies rate-limiting to avoid sending duplicate alerts within 60 seconds.
83
+ *
84
+ * @param {string} violated - The violated CSP directive
85
+ * @param {string} blockedUri - The URI that was blocked
86
+ * @param {Record<string, any>} report - The full CSP report object
87
+ */
88
+ async function sendToNtfy(violated, blockedUri, report) {
89
+ const highRiskDirectives = [
90
+ "script-src",
91
+ "form-action",
92
+ "frame-ancestors",
93
+ "base-uri",
94
+ ];
95
+
96
+ const directiveKey = violated.split(" ")[0]; // strip fallback values or sources
97
+ const isHighRisk = highRiskDirectives.includes(directiveKey);
98
+ console.log(`[CSP-Edge] Checking directive: ${directiveKey}`);
99
+ if (!isHighRisk) return;
100
+
101
+ const key = `${violated}|${blockedUri}`;
102
+ const now = Date.now();
103
+
104
+ // Skip and log if violation was reported recently
105
+ if (
106
+ recentViolations.has(key) &&
107
+ now - recentViolations.get(key) < VIOLATION_TTL_MS
108
+ ) {
109
+ console.log(`[CSP-Edge] Skipped duplicate alert for ${key}`);
110
+ return;
111
+ }
112
+
113
+ // Record the current timestamp
114
+ recentViolations.set(key, now);
115
+
116
+ // Cleanup old entries (memory-safe for low volume)
117
+ for (const [k, t] of recentViolations.entries()) {
118
+ if (now - t > VIOLATION_TTL_MS) {
119
+ recentViolations.delete(k);
120
+ }
121
+ }
122
+
123
+ const topicUrl = "https://ntfy.neteng.pro/csp-alerts";
124
+
125
+ const message = [
126
+ `🚨 CSP Violation Detected`,
127
+ `Directive: ${violated}`,
128
+ `Blocked URI: ${blockedUri}`,
129
+ `Referrer: ${report.referrer || "N/A"}`,
130
+ `Source: ${report["source-file"] || "N/A"}`,
131
+ `Line: ${report["line-number"] || "N/A"}`,
132
+ ].join("\n");
133
+
134
+ await fetch(topicUrl, {
135
+ method: "POST",
136
+ headers: {
137
+ "Content-Type": "text/plain",
138
+ "X-Title": "High-Risk CSP Violation",
139
+ "X-Priority": "5",
140
+ },
141
+ body: message,
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Configuration block for the Edge Function.
147
+ * This sets the endpoint route to /api/csp-report
148
+ */
149
+ export const config = {
150
+ path: "/api/csp-report",
151
+ };
package/netlify.toml CHANGED
@@ -11,9 +11,9 @@
11
11
  targetPort = 5173
12
12
  port = 8888
13
13
 
14
- [functions."*"]
15
- node_bundler = "esbuild"
16
- included_files = ["netlify-functions/**"]
14
+ [[edge_functions]]
15
+ path = "/api/csp-report"
16
+ function = "csp-report"
17
17
 
18
18
  [[plugins]]
19
19
  package = "netlify-plugin-submit-sitemap"