@produtype/core 0.80.0 → 0.81.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.
@@ -165,6 +165,26 @@ async function detectSecurity(ctx) {
165
165
  /X-Frame-Options/i,
166
166
  /SECURE_HSTS_SECONDS/,
167
167
  /securityHeaders/i,
168
+ /**
169
+ * Django's spelling of the same decisions.
170
+ *
171
+ * The list held the HTTP header names and one Django setting, so a project that
172
+ * writes `X_FRAME_OPTIONS = "SAMEORIGIN"` — the setting, with underscores, which
173
+ * is the only way to say it in Django — matched nothing. paperless-ngx sets it
174
+ * and was told at `high` to add security headers.
175
+ *
176
+ * Only the settings that choose a policy. `SECURE_PROXY_SSL_HEADER` is not one
177
+ * of them: it tells Django how to tell it is behind HTTPS, and every deployment
178
+ * behind a proxy needs it whether or not anybody thought about headers. And
179
+ * `SecurityMiddleware` itself stays out, for the reason recorded further down —
180
+ * `django-admin startproject` writes it into every new project.
181
+ */
182
+ /^\s*X_FRAME_OPTIONS\s*=/m,
183
+ /^\s*SECURE_CONTENT_TYPE_NOSNIFF\s*=/m,
184
+ /^\s*SECURE_BROWSER_XSS_FILTER\s*=/m,
185
+ /^\s*SECURE_REFERRER_POLICY\s*=/m,
186
+ /^\s*SECURE_CROSS_ORIGIN_OPENER_POLICY\s*=/m,
187
+ /^\s*CSP_DEFAULT_SRC\s*=/m,
168
188
  ], 20);
169
189
  const helmet = helmetDep || headerSignals.length > 0;
170
190
  /**
@@ -319,9 +339,30 @@ async function detectSecurity(ctx) {
319
339
  if (at !== -1) {
320
340
  const window = lines.slice(at, Math.min(lines.length, at + 12)).join('\n');
321
341
  if (STARLETTE_CORS_CALL.test(window)) {
322
- const allowList = /allow_origins\s*=\s*\[([^\]]*)\]/.exec(window);
323
342
  const hit = { file, line: at + 1, snippet: lines[at].trim().slice(0, 200) };
324
- if (!allowList || /^\s*["']\*["']\s*,?\s*$/.test(allowList[1]))
343
+ const allowList = /allow_origins\s*=\s*\[([^\]]*)\]/.exec(window);
344
+ if (allowList) {
345
+ if (/^\s*["']\*["']\s*,?\s*$/.test(allowList[1]))
346
+ corsLoose.push(hit);
347
+ else
348
+ corsStrict.push(hit);
349
+ continue;
350
+ }
351
+ /**
352
+ * `allow_origins=allowed_origins`, which is how a real application writes it.
353
+ *
354
+ * mealie builds the list from its settings and passes the name, and calling
355
+ * that "no explicit origin restrictions" at `high` reads as advice to add
356
+ * the allowlist it has. The wildcard is written literally when it is meant —
357
+ * `allow_origins=["*"]` — so a name is followed to its assignment, and a
358
+ * value assembled somewhere this cannot see is a configuration rather than a
359
+ * wildcard.
360
+ */
361
+ const named = /allow_origins\s*=\s*([A-Za-z_][\w.]*)/.exec(window);
362
+ const assigned = named
363
+ ? new RegExp(`^\\s*${named[1].split('.').pop()}\\s*=\\s*\\[([^\\]]*)\\]`, 'm').exec(text)
364
+ : null;
365
+ if (assigned && /^\s*["']\*["']\s*,?\s*$/.test(assigned[1]))
325
366
  corsLoose.push(hit);
326
367
  else
327
368
  corsStrict.push(hit);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@produtype/core",
3
- "version": "0.80.0",
3
+ "version": "0.81.0",
4
4
  "description": "Deterministic CLI and library that analyzes a web application repository and reports how far it is from production-ready for the kind of product it is meant to be.",
5
5
  "license": "MIT",
6
6
  "bin": {