@produtype/core 0.81.0 → 0.83.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.
|
@@ -267,20 +267,38 @@ async function detectBackend(ctx) {
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
|
-
|
|
270
|
+
/**
|
|
271
|
+
* Django, which four filenames were enough to declare.
|
|
272
|
+
*
|
|
273
|
+
* Any two of `manage.py`, a file ending in `settings.py`, a file ending in `urls.py`
|
|
274
|
+
* and the dependency named Django, and three of those four are names other projects
|
|
275
|
+
* use. redash is a Flask application: it keeps a `manage.py`, and
|
|
276
|
+
* `redash/handlers/settings.py` is the HTTP handler for a user's settings page. Its
|
|
277
|
+
* report said "Backend: flask, django".
|
|
278
|
+
*
|
|
279
|
+
* The dependency is the fact — nobody runs Django without installing it — and the
|
|
280
|
+
* filenames only corroborate it.
|
|
281
|
+
*
|
|
282
|
+
* A fallback for the unreadable-manifest case was written and deleted: a settings
|
|
283
|
+
* file declaring `INSTALLED_APPS` would have stood on its own, and no repository
|
|
284
|
+
* measured reaches it, because every Django project declares Django. The mutation
|
|
285
|
+
* run said so — removing it failed nothing. A rule nothing measures is a rule to
|
|
286
|
+
* delete.
|
|
287
|
+
*/
|
|
271
288
|
const djangoSignals = [];
|
|
289
|
+
const djangoDep = (0, detectContext_1.hasRuntimePyDep)(ctx, 'django');
|
|
290
|
+
if (djangoDep)
|
|
291
|
+
djangoSignals.push({ type: 'dependency', value: 'django' });
|
|
272
292
|
if (ctx.files.all.some((f) => f.endsWith('manage.py') || f === 'manage.py')) {
|
|
273
293
|
djangoSignals.push({ type: 'file', value: 'manage.py' });
|
|
274
294
|
}
|
|
275
295
|
const settingsFile = ctx.files.all.find((f) => f.endsWith('settings.py'));
|
|
276
296
|
if (settingsFile)
|
|
277
297
|
djangoSignals.push({ type: 'file', value: settingsFile });
|
|
278
|
-
if ((0, detectContext_1.hasRuntimePyDep)(ctx, 'django'))
|
|
279
|
-
djangoSignals.push({ type: 'dependency', value: 'django' });
|
|
280
298
|
if (ctx.files.all.some((f) => f.endsWith('urls.py'))) {
|
|
281
299
|
djangoSignals.push({ type: 'file', value: ctx.files.all.find((f) => f.endsWith('urls.py')) });
|
|
282
300
|
}
|
|
283
|
-
if (djangoSignals.length >= 2) {
|
|
301
|
+
if (djangoDep && djangoSignals.length >= 2) {
|
|
284
302
|
frameworks.push('django');
|
|
285
303
|
evidence.push(...djangoSignals);
|
|
286
304
|
}
|
|
@@ -46,6 +46,21 @@ async function detectGdpr(ctx) {
|
|
|
46
46
|
*/
|
|
47
47
|
/anonymi[sz]e[_-]?(user|account)/i,
|
|
48
48
|
/user[_-]?anonymi[sz]/i,
|
|
49
|
+
/**
|
|
50
|
+
* The regulation's own name, next to the act.
|
|
51
|
+
*
|
|
52
|
+
* `deleteUser(id)` was measured and withdrawn because delete is every admin
|
|
53
|
+
* screen ever written. GDPR is not an ordinary word: an identifier that carries
|
|
54
|
+
* it beside a deletion is somebody naming the article they are answering. forem
|
|
55
|
+
* runs its erasure through `Users::DeleteWorker` and records a
|
|
56
|
+
* `GDPRDeleteRequest`, and was told at `high` that it has no erasure flow.
|
|
57
|
+
*
|
|
58
|
+
* The pattern was already in this file, one capability down — the retention
|
|
59
|
+
* needle reads `(gdpr|privacy).*(retention|purge|delete)`, so the line was being
|
|
60
|
+
* found and filed under the wrong question.
|
|
61
|
+
*/
|
|
62
|
+
/gdpr[_\- ]?(delete|deletion|eras|removal|forget)/i,
|
|
63
|
+
/(delete|deletion|eras|removal)[_\- ]?gdpr/i,
|
|
49
64
|
], 20);
|
|
50
65
|
/**
|
|
51
66
|
* `deleteUser(id)` is not article 17 — it is every admin screen ever written, and
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.detectSecurity = detectSecurity;
|
|
4
4
|
const detectContext_1 = require("./detectContext");
|
|
5
5
|
const readTextFileSafe_1 = require("../utils/readTextFileSafe");
|
|
6
|
+
const djangoSettings_1 = require("./djangoSettings");
|
|
6
7
|
const textSearch_1 = require("../utils/textSearch");
|
|
7
8
|
const absenceEvidence_1 = require("./absenceEvidence");
|
|
8
9
|
const valuesFromPackage_1 = require("./structural/valuesFromPackage");
|
|
@@ -111,35 +112,6 @@ function detectCorsConfig(text, file, boundNames) {
|
|
|
111
112
|
}
|
|
112
113
|
return { loose, strict };
|
|
113
114
|
}
|
|
114
|
-
/**
|
|
115
|
-
* A settings file that is Django's, rather than one that shares its name.
|
|
116
|
-
*
|
|
117
|
-
* `diet_hub/api/settings.py` is a FastAPI router that lets an administrator change
|
|
118
|
-
* application options from the browser. It was read as Django's configuration, and on
|
|
119
|
-
* the strength of the filename alone the project was credited with having security
|
|
120
|
-
* middleware it does not have — the one direction of error that matters here, because
|
|
121
|
-
* it hides a missing control rather than inventing a present one.
|
|
122
|
-
*
|
|
123
|
-
* Two conditions, because either alone is wrong. A repository can depend on Django and
|
|
124
|
-
* still own a dozen files called settings.py; a file can declare INSTALLED_APPS in a
|
|
125
|
-
* tutorial that the product does not run. And the first file named settings.py is not
|
|
126
|
-
* the right one: a project with `settings/base.py` and `settings/production.py` has
|
|
127
|
-
* several, so every candidate is examined and the first that is Django's is used.
|
|
128
|
-
*/
|
|
129
|
-
async function findDjangoSettings(ctx) {
|
|
130
|
-
if (!ctx.pythonDeps.includes('django'))
|
|
131
|
-
return null;
|
|
132
|
-
const candidates = ctx.files.all.filter((f) => /(^|\/)settings(_[a-z]+)?\.py$/i.test(f) || /(^|\/)settings\/[a-z_]+\.py$/i.test(f));
|
|
133
|
-
for (const file of candidates) {
|
|
134
|
-
const text = await (0, readTextFileSafe_1.readTextFileSafe)(ctx.root, file);
|
|
135
|
-
if (!text)
|
|
136
|
-
continue;
|
|
137
|
-
if (/^\s*INSTALLED_APPS\s*=/m.test(text) || /^\s*MIDDLEWARE\s*=/m.test(text) || /DJANGO_SETTINGS_MODULE/.test(text)) {
|
|
138
|
-
return { file, text };
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
115
|
const RATE_LIMIT_PACKAGES = [
|
|
144
116
|
'express-rate-limit',
|
|
145
117
|
'@upstash/ratelimit',
|
|
@@ -394,7 +366,7 @@ async function detectSecurity(ctx) {
|
|
|
394
366
|
* `CORS_ORIGIN_ALLOW_ALL`, which is what it was called before version 3.5 — is the
|
|
395
367
|
* one line that opens it.
|
|
396
368
|
*/
|
|
397
|
-
const django = await findDjangoSettings(ctx);
|
|
369
|
+
const django = await (0, djangoSettings_1.findDjangoSettings)(ctx);
|
|
398
370
|
if (django) {
|
|
399
371
|
const middlewareLine = django.text
|
|
400
372
|
.split(/\r?\n/)
|
|
@@ -453,7 +425,7 @@ async function detectSecurity(ctx) {
|
|
|
453
425
|
* this repository keeps as a fixture precisely because it is unprotected. What is
|
|
454
426
|
* left is the `SECURE_*` settings above: lines somebody chose to write.
|
|
455
427
|
*/
|
|
456
|
-
const djangoSettings = await findDjangoSettings(ctx);
|
|
428
|
+
const djangoSettings = await (0, djangoSettings_1.findDjangoSettings)(ctx);
|
|
457
429
|
let djangoDebugTrue = false;
|
|
458
430
|
let djangoSecureCookies = true;
|
|
459
431
|
if (djangoSettings) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DetectContext } from './detectContext';
|
|
2
|
+
/**
|
|
3
|
+
* A settings file that is Django's, rather than one that shares its name.
|
|
4
|
+
*
|
|
5
|
+
* `diet_hub/api/settings.py` is a FastAPI router that lets an administrator change
|
|
6
|
+
* application options from the browser. It was read as Django's configuration, and on
|
|
7
|
+
* the strength of the filename alone the project was credited with having security
|
|
8
|
+
* middleware it does not have — the one direction of error that matters here, because
|
|
9
|
+
* it hides a missing control rather than inventing a present one.
|
|
10
|
+
*
|
|
11
|
+
* Two conditions, because either alone is wrong. A repository can depend on Django and
|
|
12
|
+
* still own a dozen files called settings.py; a file can declare INSTALLED_APPS in a
|
|
13
|
+
* tutorial that the product does not run. And the first file named settings.py is not
|
|
14
|
+
* the right one: a project with `settings/base.py` and `settings/production.py` has
|
|
15
|
+
* several, so every candidate is examined and the first that is Django's is used.
|
|
16
|
+
*/
|
|
17
|
+
export declare function findDjangoSettings(ctx: DetectContext): Promise<{
|
|
18
|
+
file: string;
|
|
19
|
+
text: string;
|
|
20
|
+
} | null>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findDjangoSettings = findDjangoSettings;
|
|
4
|
+
const readTextFileSafe_1 = require("../utils/readTextFileSafe");
|
|
5
|
+
/**
|
|
6
|
+
* A settings file that is Django's, rather than one that shares its name.
|
|
7
|
+
*
|
|
8
|
+
* `diet_hub/api/settings.py` is a FastAPI router that lets an administrator change
|
|
9
|
+
* application options from the browser. It was read as Django's configuration, and on
|
|
10
|
+
* the strength of the filename alone the project was credited with having security
|
|
11
|
+
* middleware it does not have — the one direction of error that matters here, because
|
|
12
|
+
* it hides a missing control rather than inventing a present one.
|
|
13
|
+
*
|
|
14
|
+
* Two conditions, because either alone is wrong. A repository can depend on Django and
|
|
15
|
+
* still own a dozen files called settings.py; a file can declare INSTALLED_APPS in a
|
|
16
|
+
* tutorial that the product does not run. And the first file named settings.py is not
|
|
17
|
+
* the right one: a project with `settings/base.py` and `settings/production.py` has
|
|
18
|
+
* several, so every candidate is examined and the first that is Django's is used.
|
|
19
|
+
*/
|
|
20
|
+
async function findDjangoSettings(ctx) {
|
|
21
|
+
if (!ctx.pythonDeps.includes('django'))
|
|
22
|
+
return null;
|
|
23
|
+
const candidates = ctx.files.all.filter((f) => /(^|\/)settings(_[a-z]+)?\.py$/i.test(f) || /(^|\/)settings\/[a-z_]+\.py$/i.test(f));
|
|
24
|
+
for (const file of candidates) {
|
|
25
|
+
const text = await (0, readTextFileSafe_1.readTextFileSafe)(ctx.root, file);
|
|
26
|
+
if (!text)
|
|
27
|
+
continue;
|
|
28
|
+
if (/^\s*INSTALLED_APPS\s*=/m.test(text) || /^\s*MIDDLEWARE\s*=/m.test(text) || /DJANGO_SETTINGS_MODULE/.test(text)) {
|
|
29
|
+
return { file, text };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.83.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": {
|