@produtype/core 0.79.0 → 0.80.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.
@@ -189,6 +189,22 @@ function isTestOrExamplePath(file) {
189
189
  * band — against a line written to be fake.
190
190
  */
191
191
  || /[-_]tests?\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(file)
192
+ /**
193
+ * And the third spelling of it.
194
+ *
195
+ * netbox keeps `netbox/netbox/configuration_testing.py`, whose first three lines
196
+ * say it is a base configuration for testing and not intended for production. Its
197
+ * `SECRET_KEY = 'abcdefg...'` was the single `critical` in netbox's report — the
198
+ * severity that bars a report from the top band — raised against a line written to
199
+ * be thrown away.
200
+ *
201
+ * A directory called `testing/` has counted as tests here for a while, so the
202
+ * suffix is the same decision in the same ecosystem. It does cost a product file
203
+ * genuinely named `ab_testing.py`, which would go unread rather than be read
204
+ * wrongly — the cheaper of the two errors, and the same trade the `testing/`
205
+ * directory rule already makes.
206
+ */
207
+ || /[-_]testing\.py$/i.test(file)
192
208
  || /\.(test|spec)\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(file)
193
209
  /**
194
210
  * Sample code, which is what a documentation repository is made of.
@@ -28,6 +28,21 @@ const WILDCARD_ORIGIN = /(Access-Control-Allow-Origin["'\s:,]+\*)|(["']\*["'])/i
28
28
  */
29
29
  const IMPORTS_FLASK_CORS = /from\s+flask_cors\s+import|import\s+flask_cors/;
30
30
  const FLASK_CORS_CALL = /\bCORS\s*\(/;
31
+ /**
32
+ * Starlette's, which is how every FastAPI application does it.
33
+ *
34
+ * The full-stack FastAPI template — the one the framework's own organisation
35
+ * publishes — was told at `high` to add cross-origin handling, above
36
+ * `app.add_middleware(CORSMiddleware, allow_origins=[settings.FRONTEND_HOST])`. The
37
+ * class comes from the framework and `add_middleware` is its contract; neither is a
38
+ * word the author picked.
39
+ *
40
+ * The allowlist decides which case it is, read from the same call: `allow_origins`
41
+ * holding a bare `"*"` is the wide-open one, and anything else is a list somebody
42
+ * chose.
43
+ */
44
+ const IMPORTS_STARLETTE_CORS = /from\s+(?:starlette|fastapi)\.middleware(?:\.cors)?\s+import[^\n]*CORSMiddleware/;
45
+ const STARLETTE_CORS_CALL = /add_middleware\s*\(\s*\n?\s*CORSMiddleware/;
31
46
  /**
32
47
  * The line with its quoted text removed.
33
48
  *
@@ -298,6 +313,21 @@ async function detectSecurity(ctx) {
298
313
  corsLoose.push(m);
299
314
  }
300
315
  }
316
+ if (IMPORTS_STARLETTE_CORS.test(text)) {
317
+ const lines = text.split(/\r?\n/);
318
+ const at = lines.findIndex((line) => /add_middleware\s*\(/.test(line) && (0, textSearch_1.isCitableLine)(line));
319
+ if (at !== -1) {
320
+ const window = lines.slice(at, Math.min(lines.length, at + 12)).join('\n');
321
+ if (STARLETTE_CORS_CALL.test(window)) {
322
+ const allowList = /allow_origins\s*=\s*\[([^\]]*)\]/.exec(window);
323
+ const hit = { file, line: at + 1, snippet: lines[at].trim().slice(0, 200) };
324
+ if (!allowList || /^\s*["']\*["']\s*,?\s*$/.test(allowList[1]))
325
+ corsLoose.push(hit);
326
+ else
327
+ corsStrict.push(hit);
328
+ }
329
+ }
330
+ }
301
331
  if (!IMPORTS_CORS.test(text))
302
332
  continue;
303
333
  const boundNames = new Set((corsBindings ?? []).filter((use) => use.file === file).map((use) => use.name));
@@ -308,6 +338,41 @@ async function detectSecurity(ctx) {
308
338
  corsLoose.push(...detected.loose);
309
339
  corsStrict.push(...detected.strict);
310
340
  }
341
+ /**
342
+ * Django's answer, which is a string in a list.
343
+ *
344
+ * netbox installs `corsheaders.middleware.CorsMiddleware` and was reported as having
345
+ * no cross-origin configuration at all — then told, at `high`, to add the handling it
346
+ * has. Nothing here looked for it: the reading was built around a call expression,
347
+ * and django-cors-headers is applied by naming it in `MIDDLEWARE` and configured by
348
+ * setting names the package itself defines.
349
+ *
350
+ * The package name and its setting names are the anchor, and neither is the author's
351
+ * to choose. Installed with no allowlist the package denies every cross-origin
352
+ * request, so the middleware alone is the strict case; `CORS_ALLOW_ALL_ORIGINS` — and
353
+ * `CORS_ORIGIN_ALLOW_ALL`, which is what it was called before version 3.5 — is the
354
+ * one line that opens it.
355
+ */
356
+ const django = await findDjangoSettings(ctx);
357
+ if (django) {
358
+ const middlewareLine = django.text
359
+ .split(/\r?\n/)
360
+ .findIndex((line) => /corsheaders\.middleware\.CorsMiddleware/.test(line));
361
+ if (middlewareLine !== -1) {
362
+ const openedUp = /CORS_(?:ALLOW_ALL_ORIGINS|ORIGIN_ALLOW_ALL)\s*=\s*True/.exec(django.text);
363
+ const hit = {
364
+ file: django.file,
365
+ line: middlewareLine + 1,
366
+ snippet: openedUp
367
+ ? openedUp[0]
368
+ : django.text.split(/\r?\n/)[middlewareLine].trim().slice(0, 200),
369
+ };
370
+ if (openedUp)
371
+ corsLoose.push(hit);
372
+ else
373
+ corsStrict.push(hit);
374
+ }
375
+ }
311
376
  for (const m of corsLoose)
312
377
  evidence.push({ type: 'snippet', value: m.snippet, file: m.file, line: m.line, claim: 'cors' });
313
378
  for (const m of corsStrict)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@produtype/core",
3
- "version": "0.79.0",
3
+ "version": "0.80.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": {