@produtype/core 0.61.0 → 0.63.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.
|
@@ -6,6 +6,7 @@ const textSearch_1 = require("../utils/textSearch");
|
|
|
6
6
|
const roleChecks_1 = require("./structural/roleChecks");
|
|
7
7
|
const absenceEvidence_1 = require("./absenceEvidence");
|
|
8
8
|
const fileNames_1 = require("./fileNames");
|
|
9
|
+
const valuesFromPackage_1 = require("./structural/valuesFromPackage");
|
|
9
10
|
/**
|
|
10
11
|
* In a product that talks to a model, `role` usually means who is speaking.
|
|
11
12
|
*
|
|
@@ -71,8 +72,38 @@ async function detectAuth(ctx) {
|
|
|
71
72
|
'passport',
|
|
72
73
|
]);
|
|
73
74
|
const managedAuthPyDeps = (0, detectContext_1.hasAnyPyDep)(ctx, ['django-allauth', 'authlib', 'python-jose', 'fastapi-users', 'flask-login']);
|
|
75
|
+
/**
|
|
76
|
+
* The packages that do the authenticating, as distinct from the words people use
|
|
77
|
+
* around them.
|
|
78
|
+
*
|
|
79
|
+
* An Italian business application hashing with argon2 and holding sessions with
|
|
80
|
+
* iron-session came back `auth.core: missing` — a complete, working sign-in
|
|
81
|
+
* reported as absent, and eight findings wrong behind it, because its routes are
|
|
82
|
+
* `/accedi` and `/registrati` and neither package was on the list.
|
|
83
|
+
*
|
|
84
|
+
* Nothing about that application is unusual. It is what this product's own thesis
|
|
85
|
+
* says out loud: the analyzer was reading a language rather than a program.
|
|
86
|
+
*/
|
|
87
|
+
const AUTH_PACKAGES = [
|
|
88
|
+
'jsonwebtoken',
|
|
89
|
+
'jose',
|
|
90
|
+
'bcrypt',
|
|
91
|
+
'bcryptjs',
|
|
92
|
+
'bcrypt-ts',
|
|
93
|
+
'argon2',
|
|
94
|
+
'@node-rs/argon2',
|
|
95
|
+
'scrypt-kdf',
|
|
96
|
+
'@phc/format',
|
|
97
|
+
'express-session',
|
|
98
|
+
'cookie-session',
|
|
99
|
+
'iron-session',
|
|
100
|
+
'cookie-parser',
|
|
101
|
+
'oslo',
|
|
102
|
+
'@oslojs/crypto',
|
|
103
|
+
'@auth/core',
|
|
104
|
+
];
|
|
74
105
|
const authDeps = [
|
|
75
|
-
...(0, detectContext_1.hasAnyDep)(ctx,
|
|
106
|
+
...(0, detectContext_1.hasAnyDep)(ctx, AUTH_PACKAGES),
|
|
76
107
|
...managedAuthDeps,
|
|
77
108
|
...managedAuthPyDeps,
|
|
78
109
|
];
|
|
@@ -314,6 +345,21 @@ async function detectAuth(ctx) {
|
|
|
314
345
|
/teamId/i,
|
|
315
346
|
/companyId/i,
|
|
316
347
|
], 20);
|
|
348
|
+
/**
|
|
349
|
+
* Where a value from one of those packages is actually used.
|
|
350
|
+
*
|
|
351
|
+
* The dependency says the project installed something that authenticates; this says
|
|
352
|
+
* where it does it, on a line the reader can open. It is also what makes the route
|
|
353
|
+
* names irrelevant: `argon2.verify(...)` inside `/accedi` is the same evidence as
|
|
354
|
+
* inside `/login`, and the binding is what finds it either way.
|
|
355
|
+
*/
|
|
356
|
+
const boundAuthUses = await (0, valuesFromPackage_1.readPackageValueUses)(ctx.root, sourceFiles, AUTH_PACKAGES);
|
|
357
|
+
const authUseEvidence = (boundAuthUses ?? []).slice(0, 6).map((use) => ({
|
|
358
|
+
type: 'file',
|
|
359
|
+
value: 'an authentication package is used here',
|
|
360
|
+
file: use.file,
|
|
361
|
+
line: use.line,
|
|
362
|
+
}));
|
|
317
363
|
const hasAuth = authDeps.length > 0 || routeSignals.length > 0;
|
|
318
364
|
const hasAuthz = permissionSignals.length > 0 || roleSignals.length > 0;
|
|
319
365
|
const b2bHint = b2bSignals.length > 0;
|
|
@@ -323,7 +369,7 @@ async function detectAuth(ctx) {
|
|
|
323
369
|
key: 'auth.core',
|
|
324
370
|
present: hasAuth,
|
|
325
371
|
complete: hasAuth && hasAuthz,
|
|
326
|
-
evidence: (0, absenceEvidence_1.evidenceOrSearch)([...depEvidence(authDeps), ...snippetEvidence(routeSignals)], 'a way for somebody to sign in', ['next-auth', 'passport', 'lucia', '@clerk/', '@supabase/auth', 'django.contrib.auth', 'devise', 'jsonwebtoken', 'a /login or /signin route', 'signIn(', 'authenticate(']),
|
|
372
|
+
evidence: (0, absenceEvidence_1.evidenceOrSearch)([...depEvidence(authDeps), ...authUseEvidence, ...snippetEvidence(routeSignals)], 'a way for somebody to sign in', ['next-auth', 'passport', 'lucia', '@clerk/', '@supabase/auth', 'django.contrib.auth', 'devise', 'jsonwebtoken', 'a /login or /signin route', 'signIn(', 'authenticate(']),
|
|
327
373
|
details: {
|
|
328
374
|
hasAuth,
|
|
329
375
|
hasAuthz,
|
|
@@ -4,9 +4,35 @@ exports.detectObservability = detectObservability;
|
|
|
4
4
|
const detectContext_1 = require("./detectContext");
|
|
5
5
|
const textSearch_1 = require("../utils/textSearch");
|
|
6
6
|
const absenceEvidence_1 = require("./absenceEvidence");
|
|
7
|
+
const valuesFromPackage_1 = require("./structural/valuesFromPackage");
|
|
8
|
+
/**
|
|
9
|
+
* Logging packages, which the ecosystem names and the author does not.
|
|
10
|
+
*
|
|
11
|
+
* The list was four long and the search beside it read `logger.` — the author's own
|
|
12
|
+
* variable. A repository using Roarr, bound to `shout`, was reported as having no
|
|
13
|
+
* logging at all: wrong package, wrong variable, two ways to miss the same thing.
|
|
14
|
+
*
|
|
15
|
+
* A list of package names is still a list, but it is a list of names nobody in the
|
|
16
|
+
* repository being analysed chose. That is the difference the whole exercise is about.
|
|
17
|
+
*/
|
|
18
|
+
const LOGGING_PACKAGES = [
|
|
19
|
+
'winston',
|
|
20
|
+
'pino',
|
|
21
|
+
'morgan',
|
|
22
|
+
'bunyan',
|
|
23
|
+
'roarr',
|
|
24
|
+
'loglevel',
|
|
25
|
+
'signale',
|
|
26
|
+
'consola',
|
|
27
|
+
'tslog',
|
|
28
|
+
'log4js',
|
|
29
|
+
'@logtail/node',
|
|
30
|
+
'debug',
|
|
31
|
+
'npmlog',
|
|
32
|
+
];
|
|
7
33
|
async function detectObservability(ctx) {
|
|
8
34
|
const evidence = [];
|
|
9
|
-
const logDeps = (0, detectContext_1.hasAnyDep)(ctx,
|
|
35
|
+
const logDeps = (0, detectContext_1.hasAnyDep)(ctx, LOGGING_PACKAGES);
|
|
10
36
|
const sentryDeps = (0, detectContext_1.hasAnyDep)(ctx, ['@sentry/node', 'sentry-sdk']);
|
|
11
37
|
for (const d of logDeps)
|
|
12
38
|
evidence.push({ type: 'dependency', value: d, claim: 'logging' });
|
|
@@ -70,6 +96,26 @@ async function detectObservability(ctx) {
|
|
|
70
96
|
const plainLogging = await (0, textSearch_1.searchInFiles)(ctx.root, ctx.files.source, [/\berror_log\s*\(/, /\bsyslog\s*\(/, /console\.(error|warn)\s*\(/, /\bprintStackTrace\s*\(/], 10);
|
|
71
97
|
for (const m of plainLogging)
|
|
72
98
|
evidence.push({ type: 'snippet', value: m.snippet, file: m.file, line: m.line, claim: 'logging' });
|
|
99
|
+
/**
|
|
100
|
+
* Where a value from a logging package is used, whatever it was called.
|
|
101
|
+
*
|
|
102
|
+
* `const shout = Roarr.child(...)` then `shout.info(...)` is structured logging, and
|
|
103
|
+
* the text search cannot see it because `shout` is a name its author invented. The
|
|
104
|
+
* import is the anchor and the binding is the chain.
|
|
105
|
+
*
|
|
106
|
+
* Evidence, not verdict, and the distinction is measured rather than assumed. This
|
|
107
|
+
* walk can only fire where one of the packages above is imported, and a package that
|
|
108
|
+
* is imported is a package the manifest declares — workspace manifests included, as
|
|
109
|
+
* a monorepo fixture confirmed. So it never changes the answer, and the clause that
|
|
110
|
+
* pretended it might was removed rather than left to look load-bearing.
|
|
111
|
+
*
|
|
112
|
+
* What it does change is what the reader is shown: "pino is in your package.json"
|
|
113
|
+
* becomes "pino is used at server.js:8". The claim was always about the second.
|
|
114
|
+
*/
|
|
115
|
+
const boundLoggerUses = await (0, valuesFromPackage_1.readPackageValueUses)(ctx.root, ctx.files.source, LOGGING_PACKAGES);
|
|
116
|
+
for (const use of (boundLoggerUses ?? []).slice(0, 10)) {
|
|
117
|
+
evidence.push({ type: 'file', value: `a logger from ${logDeps[0] ?? 'a logging package'} is used here`, file: use.file, line: use.line, claim: 'logging' });
|
|
118
|
+
}
|
|
73
119
|
const hasStructuredLogging = logDeps.length > 0 || structuredHits.length > 0;
|
|
74
120
|
const hasAnyLogging = hasStructuredLogging || plainLogging.length > 0;
|
|
75
121
|
if (!hasHealth) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.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": {
|