@produtype/core 1.21.0 → 1.21.2
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.
|
@@ -69,7 +69,21 @@ async function detectObservability(ctx) {
|
|
|
69
69
|
evidence.push({ type: 'dependency', value: d, claim: 'logging' });
|
|
70
70
|
for (const d of sentryDeps)
|
|
71
71
|
evidence.push({ type: 'dependency', value: d, claim: 'logging' });
|
|
72
|
-
|
|
72
|
+
/**
|
|
73
|
+
* An import path is not a route.
|
|
74
|
+
*
|
|
75
|
+
* `import Health from 'typings/Health';` is a TypeScript type in Radarr's frontend,
|
|
76
|
+
* and `/Health'` matches the route pattern exactly as `/health` in a URL does. It
|
|
77
|
+
* was the evidence behind Radarr's passing health check — a false pass, which is
|
|
78
|
+
* the direction that raises a score rather than lowering one.
|
|
79
|
+
*
|
|
80
|
+
* Imports are good evidence elsewhere and are left alone there; this is the search
|
|
81
|
+
* for a *route*, and a module specifier is never one. Filtered inside the search so
|
|
82
|
+
* that a frontend full of them cannot spend the budget before a real route is
|
|
83
|
+
* reached.
|
|
84
|
+
*/
|
|
85
|
+
const IMPORT_LINE = /^\s*(?:import\b|from\s+['"]|const\s+\{?[\w\s,}]*\}?\s*=\s*require\()/;
|
|
86
|
+
const hits = await (0, textSearch_1.searchInFiles)(ctx.root, ctx.files.source, [HEALTH_ROUTE, /x-request-id/i, /correlation-id/i, /error\s*handler/i, /RotatingFileHandler/i], 25, (match) => !IMPORT_LINE.test(match.snippet));
|
|
73
87
|
/**
|
|
74
88
|
* One search, three answers, so the claim is decided per hit rather than per search.
|
|
75
89
|
*
|
package/dist/utils/textSearch.js
CHANGED
|
@@ -98,14 +98,37 @@ const NAMES_SOMETHING = /^["']?(?:name|key|label|id|title|field|env|variable)["'
|
|
|
98
98
|
* has other arguments and is untouched.
|
|
99
99
|
*/
|
|
100
100
|
const HTTP_HEADER_NAME = /^[A-Z][A-Za-z0-9]*(?:-[A-Z][A-Za-z0-9]*)+$/;
|
|
101
|
+
/**
|
|
102
|
+
* What is left when a constant's value is taken out of its own name.
|
|
103
|
+
*
|
|
104
|
+
* Stirling-PDF stores a user's second factor under a key, and declares that key as
|
|
105
|
+
* `public static final String MFA_SECRET_KEY = "mfaSecret";`. Normalised, the name is
|
|
106
|
+
* the value plus `key` — it says what the string is *for*. That line was the one
|
|
107
|
+
* `critical` in the report of a careful product: the loudest severity there is, about
|
|
108
|
+
* a map key.
|
|
109
|
+
*
|
|
110
|
+
* A real secret is never its own name. `JWT_SECRET = "jwtSecret"` is somebody naming
|
|
111
|
+
* a slot; the value that matters is random and lives in the environment.
|
|
112
|
+
*/
|
|
113
|
+
const NAME_PLUS_WHAT_IT_IS = /^(?:key|name|field|prop|property|attr|attribute|header|param|id|label|const|setting|option)$/;
|
|
101
114
|
function spellsItsOwnName(trimmed) {
|
|
102
115
|
const withoutComment = trimmed.replace(/\s*(\/\/|#).*$/, '').replace(/[,;]\s*$/, '');
|
|
103
|
-
|
|
116
|
+
/**
|
|
117
|
+
* The type sits between the modifiers and the name in Java, C# and Go, and it is
|
|
118
|
+
* spelled however that language spells it — `String`, not `string`. Listing the
|
|
119
|
+
* declarators alone missed `public static final String MFA_SECRET_KEY = "mfaSecret"`
|
|
120
|
+
* entirely, so one optional word is allowed there instead of a list that would
|
|
121
|
+
* always be short of somebody's type.
|
|
122
|
+
*/
|
|
123
|
+
const assignment = /^(?:(?:public|private|protected|internal|export|const|let|var|final|static|readonly)\s+)*(?:[A-Za-z_][\w.<>[\]]*\s+)?([A-Za-z_][\w.]*)\s*(?::\s*[\w<>[\].]+)?\s*=\s*(['"`])([^'"`]+)\2$/.exec(withoutComment);
|
|
104
124
|
if (!assignment)
|
|
105
125
|
return false;
|
|
106
126
|
const plain = (value) => value.toLowerCase().replace(/[-_.\s]/g, '');
|
|
107
127
|
const declared = /^\s*(?:public|private|protected|internal|export|const|let|var|final|static|readonly)\b/.test(withoutComment);
|
|
108
|
-
|
|
128
|
+
const name = plain(assignment[1]);
|
|
129
|
+
const literal = plain(assignment[3]);
|
|
130
|
+
return name === literal
|
|
131
|
+
|| (declared && NAME_PLUS_WHAT_IT_IS.test(name.replace(literal, '')) && name.includes(literal))
|
|
109
132
|
|| (declared && HTTP_HEADER_NAME.test(assignment[3]));
|
|
110
133
|
}
|
|
111
134
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.2",
|
|
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": {
|