@networkpro/web 1.25.5 β†’ 1.25.6

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.
@@ -21,31 +21,50 @@ jobs:
21
21
 
22
22
  env:
23
23
  PROBELY_API_KEY: ${{ secrets.PROBELY_API_KEY }}
24
- TARGET_ID: 3by8xa6kzArN
25
- API_BASE: https://api.probely.com/v2 # Always include /v2
24
+ TARGET_ID: ${{ secrets.PROBELY_TARGET_ID }}
25
+ API_BASE: https://api.probely.com
26
26
  MAX_WAIT_MINUTES: 60 # configurable
27
27
 
28
28
  steps:
29
29
  - name: Start Probely Scan
30
30
  id: start-scan
31
31
  run: |
32
+ curl_retry() {
33
+ curl --fail-with-body --retry 3 --retry-delay 5 --retry-max-time 30 "$@"
34
+ }
35
+
32
36
  echo "πŸ§ͺ Triggering Probely scan for target $TARGET_ID ..."
33
- response=$(curl -s -X POST "$API_BASE/targets/$TARGET_ID/scans/" \
37
+
38
+ response_file=$(mktemp)
39
+ http_code=$(curl_retry -s -w "%{http_code}" -o "$response_file" -X POST "$API_BASE/targets/$TARGET_ID/scan_now/" \
34
40
  -H "Authorization: JWT $PROBELY_API_KEY" \
35
41
  -H "Content-Type: application/json" \
36
42
  -d '{}')
37
43
 
38
- echo "Raw API response:"
39
- echo "$response" | jq .
44
+ echo "🌐 HTTP status: $http_code"
45
+ echo "πŸ“„ Raw API response:"
46
+ cat "$response_file"
47
+
48
+ if [ "$http_code" -ne 201 ]; then
49
+ echo "::error ::Unexpected HTTP response from Probely API: $http_code"
50
+ exit 1
51
+ fi
52
+
53
+ if ! jq . "$response_file" >/dev/null 2>&1; then
54
+ echo "::error ::Invalid JSON response from Probely API."
55
+ cat "$response_file"
56
+ exit 1
57
+ fi
40
58
 
41
- scan_id=$(echo "$response" | jq -r '.id // empty')
59
+ jq . "$response_file"
60
+ scan_id=$(jq -r '.id // empty' "$response_file")
42
61
 
43
62
  if [ -z "$scan_id" ]; then
44
- echo "::error ::Failed to start scan β€” check API key, target ID, or base URL."
63
+ echo "::error ::Scan ID not found in response. Check API key, target ID, or base URL."
45
64
  exit 1
46
65
  fi
47
66
 
48
- echo "scan_id=$scan_id" >> $GITHUB_ENV
67
+ echo "scan_id=$scan_id" >> "$GITHUB_ENV"
49
68
  echo "βœ… Scan started with ID: $scan_id"
50
69
 
51
70
  - name: Wait for Scan Completion
@@ -53,7 +72,7 @@ jobs:
53
72
  echo "⏳ Waiting for scan $scan_id to complete..."
54
73
  elapsed=0
55
74
  while [ $elapsed -lt $((MAX_WAIT_MINUTES * 60)) ]; do
56
- status=$(curl -s "$API_BASE/scans/$scan_id/" \
75
+ status=$(curl --fail-with-body -s "$API_BASE/targets/$TARGET_ID/scans/$scan_id/" \
57
76
  -H "Authorization: JWT $PROBELY_API_KEY" | jq -r '.status // empty')
58
77
 
59
78
  echo "⏱️ Status: $status (elapsed $elapsed sec)"
@@ -78,18 +97,19 @@ jobs:
78
97
  - name: Download Probely HTML Report
79
98
  run: |
80
99
  echo "πŸ“₯ Downloading report for scan $scan_id ..."
81
- curl -s "$API_BASE/scans/$scan_id/report/" \
100
+ curl -s "$API_BASE/targets/$TARGET_ID/scans/$scan_id/endpoints/" \
82
101
  -H "Authorization: JWT $PROBELY_API_KEY" \
83
- -o probely-report.html
102
+ -o probely-scan-coverage.csv
84
103
 
85
- if [ ! -s probely-report.html ]; then
104
+ if [ ! -s probely-scan-coverage.csv ]; then
86
105
  echo "::error ::Report file is empty or missing."
87
106
  exit 1
88
107
  fi
89
- echo "βœ… Report saved as probely-report.html"
108
+ echo "βœ… Report saved as probely-scan-coverage.csv"
90
109
 
91
110
  - name: Upload report artifact
92
111
  uses: actions/upload-artifact@v5
93
112
  with:
94
- name: probely-report
95
- path: probely-report.html
113
+ name: probely-scan-coverage
114
+ path: probely-scan-coverage.csv
115
+ # cspell:ignore mktemp
@@ -41,40 +41,5 @@
41
41
  "css.customData": [
42
42
  ".vscode/customData.json" // Path to your custom data file
43
43
  ],
44
- "markdown.validate.enabled": false,
45
- "markdown.validate.ignoredLinks": [
46
- "#bugs",
47
- "#features",
48
- "#top",
49
- "#ownership",
50
- "#trademark",
51
- "#branding",
52
- "#licensed-material",
53
- "#licenses",
54
- "#dlnotes",
55
- "#cc-by",
56
- "#gnu-gpl",
57
- "#third-party",
58
- "#prohibited-uses",
59
- "#disclaimer",
60
- "#contact",
61
- "#revisions",
62
- "#pledge",
63
- "#standards",
64
- "#response",
65
- "#enforce",
66
- "#attribute",
67
- "#version",
68
- "#structure",
69
- "#getting-started",
70
- "#configuration",
71
- "#sw-utilities",
72
- "#cspreport",
73
- "#testing",
74
- "#toolchain",
75
- "#toolconfig",
76
- "#scripts",
77
- "#license",
78
- "#questions"
79
- ]
44
+ "markdown.validate.enabled": false
80
45
  }
package/CHANGELOG.md CHANGED
@@ -22,6 +22,35 @@ This project attempts to follow [Keep a Changelog](https://keepachangelog.com/en
22
22
 
23
23
  ---
24
24
 
25
+ ## [1.25.6] - 2025-11-04
26
+
27
+ ### Security
28
+
29
+ - Hardened `Content-Security-Policy (CSP)` in `hooks.server.js`:
30
+ - Environment-specific policies for `production`, `audit`, `dev`, and `test`
31
+ - Added real CSP reporting endpoint (`csp.netwk.pro`) in production
32
+ - Report-only mode enabled in non-prod for safer diagnostics
33
+ - Added `/api/mock-csp` endpoint to capture and log CSP violation reports in non-prod environments
34
+
35
+ ### Changed
36
+
37
+ - Updated `README.md` with detailed explanation of the CSP enforcement strategy and future nonce-based roadmap
38
+ - Moved inline styles from `Badges.svelte` and `Logo.svelte` to external stylesheet (`default.css`)
39
+ - Regenerated `global.min.css` using LightningCSS to reflect updated external styles
40
+ - Bumped project version to `v1.25.6`
41
+ - Updated dependencies:
42
+ - `@eslint/js` `^9.39.0` β†’ `^9.39.1`
43
+ - `eslint` `^9.39.0` β†’ `^9.39.1`
44
+ - `eslint-plugin-jsdoc` `^61.1.11` β†’ `^61.1.12`
45
+ - `svelte` `5.43.2` β†’ `5.43.3`
46
+ - `posthog-js` `^1.284.0` β†’ `^1.285.1`
47
+
48
+ ### Fixed
49
+
50
+ - Updated `probely-scan.yml` GitHub workflow to utilize the correct API endpoint and cURL requests.
51
+
52
+ ---
53
+
25
54
  ## [1.25.5] - 2025-11-03
26
55
 
27
56
  ### Added
@@ -1702,7 +1731,8 @@ This enables analytics filtering and CSP hardening for the audit environment.
1702
1731
 
1703
1732
  <!-- Link references -->
1704
1733
 
1705
- [Unreleased]: https://github.com/netwk-pro/netwk-pro.github.io/compare/v1.25.5...HEAD
1734
+ [Unreleased]: https://github.com/netwk-pro/netwk-pro.github.io/compare/v1.25.6...HEAD
1735
+ [1.25.6]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.6
1706
1736
  [1.25.5]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.5
1707
1737
  [1.25.4]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.4
1708
1738
  [1.25.3]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.3
package/README.md CHANGED
@@ -173,23 +173,55 @@ This project includes custom runtime configuration files for enhancing security,
173
173
 
174
174
  ### πŸ” `hooks.server.js`
175
175
 
176
- Located at `src/hooks.server.js`, this file is responsible for injecting dynamic security headers. It includes:
176
+ Located at `src/hooks.server.js`, this file dynamically injects security headers depending on the environment. It includes:
177
+
178
+ - A **Content Security Policy (CSP)** with environment-based directives:
179
+ - **Production/Audit**: Enforced, hardened CSP
180
+ - **Test/Dev**: Uses `Content-Security-Policy-Report-Only` for safe diagnostics
181
+ - A **Permissions Policy** that disables nonessential browser APIs
182
+ - Standard HTTP security headers:
183
+ - `X-Content-Type-Options`
184
+ - `X-Frame-Options`
185
+ - `Referrer-Policy`
186
+ - `Strict-Transport-Security` (in non-test environments)
177
187
 
178
- - A Content Security Policy (CSP) configured with relaxed directives to permit inline scripts and styles (`'unsafe-inline'`)
179
- - A Permissions Policy to explicitly disable unnecessary browser APIs
180
- - Standard security headers such as `X-Content-Type-Options`, `X-Frame-Options`, and `Referrer-Policy`
188
+ ---
189
+
190
+ ### βš™οΈ CSP Behavior by Environment
191
+
192
+ | Environment | Header | Analytics Enabled | CSP Reporting |
193
+ | ------------ | ------------------------------------- | ----------------- | ------------- |
194
+ | `production` | `Content-Security-Policy` | βœ… Yes | βœ… Yes |
195
+ | `audit` | `Content-Security-Policy` | ❌ No | ❌ No |
196
+ | `dev` | `Content-Security-Policy-Report-Only` | ❌ No | βœ… Yes (mock) |
197
+ | `test` | `Content-Security-Policy-Report-Only` | ❌ No | βœ… Yes (mock) |
198
+
199
+ ---
200
+
201
+ ### πŸ§ͺ Reporting & Debugging
181
202
 
182
- > ℹ️ A stricter CSP (excluding `'unsafe-inline'`) was attempted but reverted due to framework-level and third-party script compatibility issues. The current policy allows inline scripts to ensure stability across SvelteKit and analytics features such as PostHog.
203
+ - In **non-production environments**, CSP headers are set to `report-only` mode.
204
+ - Violations are POSTed to `/api/mock-csp`, which logs reports to the console.
205
+ - In **production**, violations are sent to a real CSP collection endpoint (`https://csp.netwk.pro/.netlify/functions/csp-report`).
206
+
207
+ ---
208
+
209
+ ### ⚠️ Current Trade-Off
210
+
211
+ > Due to limitations in PostHog and certain SvelteKit internals, the current policy allows `'unsafe-inline'` for scripts and styles. A strict CSP using nonces was previously attempted but blocked critical functionality.
212
+
213
+ ---
183
214
 
184
- #### Future Improvements
215
+ ### πŸ“ˆ Future Improvements (Strict CSP Plan)
185
216
 
186
- To implement a strict nonce-based CSP in the future:
217
+ To move toward a strict, nonce-based CSP:
187
218
 
188
- 1. Add nonce generation and injection logic in `hooks.server.js`
189
- 2. Update all inline `<script>` tags (e.g. in `app.html`) to include `nonce="__cspNonce__"`
190
- 3. Ensure any analytics libraries or dynamic scripts support nonced or external loading
219
+ 1. Ensure **all inline scripts** are updated to include injected nonces (`nonce="%nonce%"`)
220
+ 2. Confirm **PostHog** or future analytics platforms support nonced or external scripts/stylesheets
221
+ 3. Review and refactor any components that rely on dynamic `style=` or `<style>` blocks without support for CSP nonces
222
+ 4. Move third-party scripts out of inline `<script>` tags where possible
191
223
 
192
- Note: Strict CSP adoption may require restructuring third-party integrations and deeper framework coordination.
224
+ > ℹ️ Nonce-based CSP is the most secure long-term path but requires cooperation from all dependencies β€” and possibly upstream fixes to analytics tooling or SvelteKit itself.
193
225
 
194
226
  &nbsp;
195
227
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@networkpro/web",
3
3
  "private": false,
4
- "version": "1.25.5",
4
+ "version": "1.25.6",
5
5
  "description": "Locking Down Networks, Unlocking Confidenceβ„’ | Security, Networking, Privacy β€” Network Pro Strategies",
6
6
  "keywords": [
7
7
  "advisory",
@@ -85,13 +85,13 @@
85
85
  },
86
86
  "dependencies": {
87
87
  "dompurify": "^3.3.0",
88
- "posthog-js": "^1.284.0",
88
+ "posthog-js": "^1.285.1",
89
89
  "semver": "^7.7.3",
90
- "svelte": "5.43.2"
90
+ "svelte": "5.43.3"
91
91
  },
92
92
  "devDependencies": {
93
93
  "@eslint/compat": "^1.4.1",
94
- "@eslint/js": "^9.39.0",
94
+ "@eslint/js": "^9.39.1",
95
95
  "@lhci/cli": "^0.15.1",
96
96
  "@playwright/test": "^1.56.1",
97
97
  "@sveltejs/adapter-vercel": "^6.1.1",
@@ -102,9 +102,9 @@
102
102
  "@vitest/coverage-v8": "3.2.4",
103
103
  "autoprefixer": "^10.4.21",
104
104
  "browserslist": "^4.27.0",
105
- "eslint": "^9.39.0",
105
+ "eslint": "^9.39.1",
106
106
  "eslint-config-prettier": "^10.1.8",
107
- "eslint-plugin-jsdoc": "^61.1.11",
107
+ "eslint-plugin-jsdoc": "^61.1.12",
108
108
  "eslint-plugin-svelte": "^3.13.0",
109
109
  "globals": "^16.5.0",
110
110
  "jsdom": "26.1.0",
@@ -16,27 +16,15 @@ export async function handle({ event, resolve }) {
16
16
  const response = await resolve(event);
17
17
 
18
18
  const env = detectEnvironment(event.url.hostname);
19
- const { isAudit, isDebug, isTest, isProd, mode, effective } = env;
19
+ const { isAudit, isDebug, isTest, isProd } = env;
20
20
 
21
- // Show logs in dev only
22
- if (isDebug) {
23
- console.log('[CSP Debug ENV]', {
24
- mode,
25
- effective,
26
- hostname: event.url.hostname,
27
- isAudit,
28
- isTest,
29
- isProd,
30
- });
31
- }
32
-
33
- // Determine report URI
34
21
  const reportUri =
35
22
  isProd && !isTest && !isAudit
36
23
  ? 'https://csp.netwk.pro/.netlify/functions/csp-report'
37
24
  : '/api/mock-csp';
38
25
 
39
- // Base hardened policy
26
+ console.log('[CSP] Report URI set to:', reportUri);
27
+
40
28
  const cspDirectives = [
41
29
  "default-src 'self';",
42
30
  "script-src 'self' 'unsafe-inline' https://us.i.posthog.com https://us-assets.i.posthog.com;",
@@ -69,8 +57,10 @@ export async function handle({ event, resolve }) {
69
57
  cspDirectives[4] = "connect-src 'self';";
70
58
  }
71
59
 
72
- // πŸ“‹ Attach CSP report directives ONLY in production
73
- if (isProd && !isAudit && !isTest) {
60
+ // πŸ“‹ Add reporting for environments that support it
61
+ const shouldReport = !isAudit && !isTest;
62
+
63
+ if (shouldReport) {
74
64
  cspDirectives.push(`report-uri ${reportUri};`, 'report-to csp-endpoint;');
75
65
 
76
66
  response.headers.set(
@@ -84,8 +74,20 @@ export async function handle({ event, resolve }) {
84
74
  );
85
75
  }
86
76
 
87
- // βœ… Apply final CSP
88
- response.headers.set('Content-Security-Policy', cspDirectives.join(' '));
77
+ // βœ… Apply CSP β€” enforce in prod or audit, report-only in dev/test
78
+ const cspHeader =
79
+ (isProd || isAudit) && !isTest
80
+ ? 'Content-Security-Policy'
81
+ : 'Content-Security-Policy-Report-Only';
82
+
83
+ response.headers.set(cspHeader, cspDirectives.join(' '));
84
+
85
+ // Log applied CSP headers in debug/audit/test
86
+ if (isDebug || isAudit) {
87
+ console.info(`[CSP] Applied header: ${cspHeader}`);
88
+ console.info(`[CSP] Policy:`, cspDirectives.join(' '));
89
+ console.info(`[CSP] Reporting to: ${reportUri}`);
90
+ }
89
91
 
90
92
  // Standard security headers
91
93
  response.headers.set(
@@ -29,8 +29,6 @@ This file is part of Network Pro.
29
29
  * @property {string} src - The source URL of the badge image.
30
30
  * @property {string} alt - The alt text for the badge image.
31
31
  * @property {string} [class] - The CSS class for styling the badge (optional).
32
- * @property {string} width - The width of the badge (CSS value).
33
- * @property {string} height - The height of the badge (CSS value).
34
32
  */
35
33
 
36
34
  /**
@@ -42,15 +40,13 @@ This file is part of Network Pro.
42
40
  href: ccbyLink,
43
41
  src: ccBadge,
44
42
  alt: 'Creative Commons BY',
45
- width: '160px',
46
- height: '24px',
43
+ class: 'badge badge--cc',
47
44
  },
48
45
  {
49
46
  href: gplLink,
50
47
  src: gplBadge,
51
48
  alt: 'GPL 3.0 or Later',
52
- width: '120px',
53
- height: '24px',
49
+ class: 'badge badge--gpl',
54
50
  },
55
51
  ];
56
52
  </script>
@@ -68,8 +64,7 @@ This file is part of Network Pro.
68
64
  loading="lazy"
69
65
  src={badge.src}
70
66
  alt={badge.alt}
71
- style:width={badge.width}
72
- style:height={badge.height} />
67
+ class={badge.class} />
73
68
  </a>
74
69
  </td>
75
70
  {/each}
@@ -7,9 +7,12 @@ This file is part of Network Pro.
7
7
  ========================================================================== -->
8
8
 
9
9
  <script>
10
+ import { CONSTANTS } from '$lib';
10
11
  // Import logo images
11
12
  import { logoPng, logoWbp } from '$lib';
12
13
 
14
+ const { COMPANY_INFO } = CONSTANTS;
15
+
13
16
  /**
14
17
  * Decoding mode for the image.
15
18
  * @type {"sync" | "async" | "auto"}
@@ -23,16 +26,16 @@ This file is part of Network Pro.
23
26
  export let loading = 'eager';
24
27
 
25
28
  /**
26
- * CSS class for the logo image.
29
+ * Optional extra CSS classes for the logo image.
27
30
  * @type {string}
28
31
  */
29
- export let className = 'logo';
32
+ export let className = '';
30
33
 
31
34
  /**
32
35
  * Alt text for the logo image.
33
36
  * @type {string}
34
37
  */
35
- export let alt = 'Network Pro Strategies';
38
+ export let alt = COMPANY_INFO.NAME;
36
39
 
37
40
  /**
38
41
  * First part of the company slogan.
@@ -58,18 +61,6 @@ This file is part of Network Pro.
58
61
  */
59
62
  export let showTagline = true;
60
63
 
61
- /**
62
- * Width of the logo in pixels.
63
- * @type {string}
64
- */
65
- export let width = '250px';
66
-
67
- /**
68
- * Height of the logo in pixels.
69
- * @type {string}
70
- */
71
- export let height = '250px';
72
-
73
64
  /**
74
65
  * Fetch priority for the logo image.
75
66
  * @type {"high" | "low" | "auto"}
@@ -83,12 +74,10 @@ This file is part of Network Pro.
83
74
  <img
84
75
  {decoding}
85
76
  {loading}
86
- class={className}
77
+ class={`logo${className ? ` ${className}` : ''}`}
87
78
  src={logoPng}
88
79
  {alt}
89
- {fetchpriority}
90
- style:width
91
- style:height />
80
+ {fetchpriority} />
92
81
  </picture>
93
82
 
94
83
  {#if showSlogan}
@@ -396,6 +396,8 @@ footer .container {
396
396
 
397
397
  .logo {
398
398
  display: block;
399
+ width: var(--logo-width, 250px);
400
+ height: var(--logo-height, 250px);
399
401
  margin-left: auto;
400
402
  margin-right: auto;
401
403
  }
@@ -709,3 +711,16 @@ footer .container {
709
711
  #toc a:hover {
710
712
  text-decoration: underline;
711
713
  }
714
+
715
+ .badge {
716
+ display: inline-block;
717
+ height: 24px;
718
+ }
719
+
720
+ .badge--cc {
721
+ width: 160px;
722
+ }
723
+
724
+ .badge--gpl {
725
+ width: 120px;
726
+ }
@@ -3,4 +3,4 @@ Copyright Β© 2025 Network Pro Strategies (Network Proβ„’)
3
3
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
4
4
  This file is part of Network Pro.
5
5
  ========================================================================== */
6
- html{-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{margin:.67em 0;font-size:2em}hr{box-sizing:content-box}pre{font-family:monospace;font-size:1em}a{background-color:#0000}abbr[title]{border-bottom:none;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted buttontext}fieldset{padding:.35em .75em .625em}legend{color:inherit;box-sizing:border-box;white-space:normal;max-width:100%;padding:0;display:table}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}details{display:block}summary{display:list-item}template{display:none}html{color:#222;scroll-behavior:smooth;font-size:1em;line-height:1.4}::-moz-selection{text-shadow:none;background:#191919}::selection{text-shadow:none;background:#191919}hr{border:0;border-top:1px solid #ccc;height:1px;margin:1em 0;padding:0;display:block;overflow:visible}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}body{color:#fafafa;background-color:#191919;margin:10px;font-family:Arial,Helvetica,sans-serif}a{text-decoration:none}a:link{color:#ffc627}a:hover,a:active{color:#ffc627;text-decoration:underline}a:focus{color:#111;background-color:#ffc627}a:visited,a:visited:hover{color:#cba557}a:visited:focus,a:visited:focus-visible{color:#111!important}.hidden,[hidden]{display:none!important}.visually-hidden{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.visually-hidden.focusable:active,.visually-hidden.focusable:focus{clip:auto;width:auto;height:auto;white-space:inherit;margin:0;position:static;overflow:visible}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:"";display:table}.clearfix:after{clear:both}@media print{*,:before,:after{color:#000!important;box-shadow:none!important;text-shadow:none!important;background:#fff!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href)")"}abbr[title]:after{content:" (" attr(title)")"}a[href^=\#]:after,a[href^=javascript\:]:after{content:""}pre{white-space:pre-wrap!important}pre,blockquote{break-inside:avoid;border:1px solid #999}tr,img{break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{break-after:avoid}}.full-width-section{background-position:50%;background-size:cover;width:100%;max-width:1920px;margin:0 auto}.container{max-width:1200px;margin:0 auto;padding:0 12px}.readable{max-width:900px;margin:0 auto}header,footer{width:100%}header .container,footer .container{max-width:1200px;margin:0 auto;padding:20px 12px}.gh{border-collapse:collapse;border-spacing:0;margin:0 auto}.gh td,.gh th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.gh .gh-tcell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.gh,.gh col{width:auto!important}.gh-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.soc{border-collapse:collapse;border-spacing:0;margin:0 auto}.soc td,.soc th{border-collapse:collapse;word-break:normal;padding:8px;overflow:hidden}.soc .soc-fa{text-align:center;vertical-align:middle}@media screen and (width<=767px){.soc,.soc col{width:auto!important}.soc-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.foss{border-collapse:collapse;border-spacing:0}.foss td,.foss th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.foss .foss-cell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.foss,.foss col{width:auto!important}.foss-wrap{-webkit-overflow-scrolling:touch;overflow-x:auto}}.bnav{text-align:center;border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav td,.bnav th{text-align:center;vertical-align:middle;word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav .bnav-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav,.bnav col{width:auto!important}.bnav-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.bnav2{border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav2 td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav2 th{word-break:normal;border-style:none;padding:12px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.bnav2 .bnav2-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav2,.bnav2 col{width:auto!important}.bnav2-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.pgp{border-collapse:collapse;border-spacing:0;margin:0 auto}.pgp td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp th{word-break:normal;border:1px solid #000;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp .pgp-col1{text-align:right;vertical-align:middle;padding-right:1rem}.pgp .pgp-col2{text-align:left;vertical-align:middle;padding-left:1rem}@media screen and (width<=767px){.pgp,.pgp col{width:auto!important}.pgp-wrap{-webkit-overflow-scrolling:touch;margin:2rem 0 auto;overflow-x:auto}}#service-summary{color:#e6e6e6;margin-top:2rem;margin-bottom:2.5rem}.service-table{color:#e6e6e6;border-collapse:collapse;background-color:#191919;width:100%;font-size:.95rem}.service-table th,.service-table td{text-align:left;vertical-align:top;border-bottom:1px solid #333;padding:.75rem 1rem}.service-table th{color:#ffc627;text-transform:uppercase;background-color:#222;font-size:.85rem;font-weight:600}.service-table a{color:#ffc627;font-weight:500;text-decoration:none}.service-table a:hover{text-decoration:underline}.service-table tr:hover{background-color:#222;transition:background-color .3s,box-shadow .3s;box-shadow:0 0 10px 2px #ffc62740}.service-table tr.selected{background-color:#222;border-left:4px solid #ffc627;transition:background-color .3s,border-left-color .3s}.service-table tr.selected:hover{background-color:#252525;box-shadow:0 0 12px 3px #ffc62759}@media (width<=768px){.service-table{font-size:.9rem}.service-table th,.service-table td{padding:.5rem}}.logo{margin-left:auto;margin-right:auto;display:block}.index-title1{text-align:center;font-style:italic;font-weight:700}.index-title2{letter-spacing:-.015em;text-align:center;font-variant:small-caps;font-size:1.25rem;line-height:1.625rem}.index1{letter-spacing:-.035em;text-align:center;font-style:italic;font-weight:700;line-height:2.125rem}.index2{letter-spacing:-.035em;text-align:center;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.index3{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem}.index4{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem;text-decoration:underline}.subhead{letter-spacing:-.035em;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.bold{font-weight:700}.emphasis{font-style:italic}.uline{text-decoration:underline}.bolditalic{font-style:italic;font-weight:700}.bquote{border-left:3px solid #9e9e9e;margin-left:30px;padding-left:10px;font-style:italic}.small-text{font-size:.75rem;line-height:1.125rem}.large-text-center{text-align:center;font-size:1.25rem;line-height:1.75rem}.prewrap{white-space:pre-wrap;display:block}.hr-styled{width:75%;margin:auto}.center-text{text-align:center}.copyright{text-align:center;font-size:.75rem;line-height:1.125rem}.gold{color:#ffc627}.visited{color:#cba557}.goldseparator{color:#ffc627;margin:0 .5rem}.center-nav{text-align:center;padding:5px;font-size:1rem;line-height:1.5rem}.block{overflow-wrap:break-word;resize:none;white-space:normal;word-break:normal;background:0 0;border:none;border-radius:0;outline:none;width:100%;font-family:monospace;font-size:.875rem;line-height:1.125rem}.full-width-section.centered{flex-direction:column;justify-content:center;min-height:80vh;display:flex}.fingerprint{white-space:pre-line;font-family:monospace;display:block}.pgp-image{width:150px;height:150px}.spacer{margin:2rem 0}.separator{margin:0 .5rem}.emoji{margin-right:8px}.headline{margin-bottom:4px;font-style:italic;font-weight:700;display:block}.label{font-family:inherit;font-weight:700}.description{font-family:inherit;font-style:normal;font-weight:400;display:inline}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.pgp-entry{flex-wrap:wrap;align-items:center;gap:2rem;margin-bottom:2rem;display:flex}.pgp-text{flex:2;min-width:250px}.pgp-qr{flex:1;min-width:150px}.obtainium-direct-label{margin:.25rem 0 .75rem;font-weight:700}.obtainium-manual-label{margin-top:.75rem;font-weight:700}.obtainium-img{width:185px;height:55px;margin-bottom:.25rem}.obtainium-margin{margin-left:4px}.obtainium-fa-down{color:#ffc627;margin-left:4px}.obtainium-icon{width:50px;height:50px}.proton-img{width:168px;height:24px}.redirect-text{text-align:center;margin-top:4rem}.redirect-content{text-align:center;margin-top:2rem;font-family:system-ui,sans-serif}.loading-spinner{border:4px solid #ddd;border-top-color:#ffc627;border-radius:50%;width:48px;height:48px;margin:2rem auto;animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.cc-link{text-decoration:none}.cc-img{vertical-align:text-bottom;margin-left:3px;display:inline-block;height:18px!important}#toc ul{padding-left:1.5rem;list-style-type:disc}#toc a{color:var(--color-primary,#ffc627);text-decoration:none}#toc a:hover{text-decoration:underline}
6
+ html{-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{margin:.67em 0;font-size:2em}hr{box-sizing:content-box}pre{font-family:monospace;font-size:1em}a{background-color:#0000}abbr[title]{border-bottom:none;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted buttontext}fieldset{padding:.35em .75em .625em}legend{color:inherit;box-sizing:border-box;white-space:normal;max-width:100%;padding:0;display:table}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}details{display:block}summary{display:list-item}template{display:none}html{color:#222;scroll-behavior:smooth;font-size:1em;line-height:1.4}::-moz-selection{text-shadow:none;background:#191919}::selection{text-shadow:none;background:#191919}hr{border:0;border-top:1px solid #ccc;height:1px;margin:1em 0;padding:0;display:block;overflow:visible}audio,canvas,iframe,img,svg,video{vertical-align:middle}fieldset{border:0;margin:0;padding:0}textarea{resize:vertical}body{color:#fafafa;background-color:#191919;margin:10px;font-family:Arial,Helvetica,sans-serif}a{text-decoration:none}a:link{color:#ffc627}a:hover,a:active{color:#ffc627;text-decoration:underline}a:focus{color:#111;background-color:#ffc627}a:visited,a:visited:hover{color:#cba557}a:visited:focus,a:visited:focus-visible{color:#111!important}.hidden,[hidden]{display:none!important}.visually-hidden{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.visually-hidden.focusable:active,.visually-hidden.focusable:focus{clip:auto;width:auto;height:auto;white-space:inherit;margin:0;position:static;overflow:visible}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:"";display:table}.clearfix:after{clear:both}@media print{*,:before,:after{color:#000!important;box-shadow:none!important;text-shadow:none!important;background:#fff!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href)")"}abbr[title]:after{content:" (" attr(title)")"}a[href^=\#]:after,a[href^=javascript\:]:after{content:""}pre{white-space:pre-wrap!important}pre,blockquote{break-inside:avoid;border:1px solid #999}tr,img{break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{break-after:avoid}}.full-width-section{background-position:50%;background-size:cover;width:100%;max-width:1920px;margin:0 auto}.container{max-width:1200px;margin:0 auto;padding:0 12px}.readable{max-width:900px;margin:0 auto}header,footer{width:100%}header .container,footer .container{max-width:1200px;margin:0 auto;padding:20px 12px}.gh{border-collapse:collapse;border-spacing:0;margin:0 auto}.gh td,.gh th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.gh .gh-tcell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.gh,.gh col{width:auto!important}.gh-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.soc{border-collapse:collapse;border-spacing:0;margin:0 auto}.soc td,.soc th{border-collapse:collapse;word-break:normal;padding:8px;overflow:hidden}.soc .soc-fa{text-align:center;vertical-align:middle}@media screen and (width<=767px){.soc,.soc col{width:auto!important}.soc-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.foss{border-collapse:collapse;border-spacing:0}.foss td,.foss th{border-collapse:collapse;word-break:normal;padding:10px 5px;overflow:hidden}.foss .foss-cell{text-align:center;vertical-align:middle}@media screen and (width<=767px){.foss,.foss col{width:auto!important}.foss-wrap{-webkit-overflow-scrolling:touch;overflow-x:auto}}.bnav{text-align:center;border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav td,.bnav th{text-align:center;vertical-align:middle;word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav .bnav-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav,.bnav col{width:auto!important}.bnav-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.bnav2{border-collapse:collapse;border-spacing:0;margin:0 auto}.bnav2 td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;font-weight:700;line-height:1.125rem;overflow:hidden}.bnav2 th{word-break:normal;border-style:none;padding:12px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.bnav2 .bnav2-cell{text-align:center;vertical-align:middle;align-content:center}@media screen and (width<=767px){.bnav2,.bnav2 col{width:auto!important}.bnav2-wrap{-webkit-overflow-scrolling:touch;margin:auto 0;overflow-x:auto}}.pgp{border-collapse:collapse;border-spacing:0;margin:0 auto}.pgp td{word-break:normal;border-style:none;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp th{word-break:normal;border:1px solid #000;padding:10px;font-size:.875rem;line-height:1.125rem;overflow:hidden}.pgp .pgp-col1{text-align:right;vertical-align:middle;padding-right:1rem}.pgp .pgp-col2{text-align:left;vertical-align:middle;padding-left:1rem}@media screen and (width<=767px){.pgp,.pgp col{width:auto!important}.pgp-wrap{-webkit-overflow-scrolling:touch;margin:2rem 0 auto;overflow-x:auto}}#service-summary{color:#e6e6e6;margin-top:2rem;margin-bottom:2.5rem}.service-table{color:#e6e6e6;border-collapse:collapse;background-color:#191919;width:100%;font-size:.95rem}.service-table th,.service-table td{text-align:left;vertical-align:top;border-bottom:1px solid #333;padding:.75rem 1rem}.service-table th{color:#ffc627;text-transform:uppercase;background-color:#222;font-size:.85rem;font-weight:600}.service-table a{color:#ffc627;font-weight:500;text-decoration:none}.service-table a:hover{text-decoration:underline}.service-table tr:hover{background-color:#222;transition:background-color .3s,box-shadow .3s;box-shadow:0 0 10px 2px #ffc62740}.service-table tr.selected{background-color:#222;border-left:4px solid #ffc627;transition:background-color .3s,border-left-color .3s}.service-table tr.selected:hover{background-color:#252525;box-shadow:0 0 12px 3px #ffc62759}@media (width<=768px){.service-table{font-size:.9rem}.service-table th,.service-table td{padding:.5rem}}.logo{width:var(--logo-width,250px);height:var(--logo-height,250px);margin-left:auto;margin-right:auto;display:block}.index-title1{text-align:center;font-style:italic;font-weight:700}.index-title2{letter-spacing:-.015em;text-align:center;font-variant:small-caps;font-size:1.25rem;line-height:1.625rem}.index1{letter-spacing:-.035em;text-align:center;font-style:italic;font-weight:700;line-height:2.125rem}.index2{letter-spacing:-.035em;text-align:center;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.index3{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem}.index4{letter-spacing:-.035em;text-align:center;font-size:1.5rem;line-height:1.75rem;text-decoration:underline}.subhead{letter-spacing:-.035em;font-variant:small-caps;font-size:1.5rem;line-height:1.75rem}.bold{font-weight:700}.emphasis{font-style:italic}.uline{text-decoration:underline}.bolditalic{font-style:italic;font-weight:700}.bquote{border-left:3px solid #9e9e9e;margin-left:30px;padding-left:10px;font-style:italic}.small-text{font-size:.75rem;line-height:1.125rem}.large-text-center{text-align:center;font-size:1.25rem;line-height:1.75rem}.prewrap{white-space:pre-wrap;display:block}.hr-styled{width:75%;margin:auto}.center-text{text-align:center}.copyright{text-align:center;font-size:.75rem;line-height:1.125rem}.gold{color:#ffc627}.visited{color:#cba557}.goldseparator{color:#ffc627;margin:0 .5rem}.center-nav{text-align:center;padding:5px;font-size:1rem;line-height:1.5rem}.block{overflow-wrap:break-word;resize:none;white-space:normal;word-break:normal;background:0 0;border:none;border-radius:0;outline:none;width:100%;font-family:monospace;font-size:.875rem;line-height:1.125rem}.full-width-section.centered{flex-direction:column;justify-content:center;min-height:80vh;display:flex}.fingerprint{white-space:pre-line;font-family:monospace;display:block}.pgp-image{width:150px;height:150px}.spacer{margin:2rem 0}.separator{margin:0 .5rem}.emoji{margin-right:8px}.headline{margin-bottom:4px;font-style:italic;font-weight:700;display:block}.label{font-family:inherit;font-weight:700}.description{font-family:inherit;font-style:normal;font-weight:400;display:inline}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.pgp-entry{flex-wrap:wrap;align-items:center;gap:2rem;margin-bottom:2rem;display:flex}.pgp-text{flex:2;min-width:250px}.pgp-qr{flex:1;min-width:150px}.obtainium-direct-label{margin:.25rem 0 .75rem;font-weight:700}.obtainium-manual-label{margin-top:.75rem;font-weight:700}.obtainium-img{width:185px;height:55px;margin-bottom:.25rem}.obtainium-margin{margin-left:4px}.obtainium-fa-down{color:#ffc627;margin-left:4px}.obtainium-icon{width:50px;height:50px}.proton-img{width:168px;height:24px}.redirect-text{text-align:center;margin-top:4rem}.redirect-content{text-align:center;margin-top:2rem;font-family:system-ui,sans-serif}.loading-spinner{border:4px solid #ddd;border-top-color:#ffc627;border-radius:50%;width:48px;height:48px;margin:2rem auto;animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.cc-link{text-decoration:none}.cc-img{vertical-align:text-bottom;margin-left:3px;display:inline-block;height:18px!important}#toc ul{padding-left:1.5rem;list-style-type:disc}#toc a{color:var(--color-primary,#ffc627);text-decoration:none}#toc a:hover{text-decoration:underline}.badge{height:24px;display:inline-block}.badge--cc{width:160px}.badge--gpl{width:120px}
@@ -7,10 +7,19 @@ This file is part of Network Pro.
7
7
  ========================================================================== */
8
8
 
9
9
  /** @type {import('@sveltejs/kit').RequestHandler} */
10
- export async function POST({ request }) {
11
- console.log('πŸ”Ά [Mock CSP] Report received during dev/CI');
10
+ export async function OPTIONS() {
11
+ return new Response(null, {
12
+ status: 204,
13
+ headers: {
14
+ 'Access-Control-Allow-Origin': '*',
15
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
16
+ 'Access-Control-Allow-Headers': 'Content-Type',
17
+ },
18
+ });
19
+ }
12
20
 
13
- // Optional: read/validate body for debugging
21
+ /** @type {import('@sveltejs/kit').RequestHandler} */
22
+ export async function POST({ request }) {
14
23
  try {
15
24
  const data = await request.json();
16
25
  console.log('πŸ”Ά [Mock CSP] Payload:', data);
@@ -18,5 +27,12 @@ export async function POST({ request }) {
18
27
  console.warn('⚠️ [Mock CSP] No JSON body provided.');
19
28
  }
20
29
 
21
- return new Response(null, { status: 204 });
30
+ return new Response(null, {
31
+ status: 204,
32
+ headers: {
33
+ 'Access-Control-Allow-Origin': '*',
34
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
35
+ 'Access-Control-Allow-Headers': 'Content-Type',
36
+ },
37
+ });
22
38
  }