@produtype/core 0.26.0 → 0.26.1

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.
@@ -90,7 +90,20 @@ async function detectAuth(ctx) {
90
90
  /createServerClient/,
91
91
  /\[\.\.\.nextauth\]/i,
92
92
  ], 30);
93
- const twoFaSignals = await (0, textSearch_1.searchInFiles)(ctx.root, sourceFiles, [/two[_-]?factor/i, /otp/i, /totp/i], 20);
93
+ /**
94
+ * `otp` as a word, not as three letters inside another one.
95
+ *
96
+ * `/otp/i` matched `VarError::NotPresent` and `PandasUseOfDotPivotOrUpdate` — N-**otp**-resent
97
+ * and D-**otp**-ivot — so a Rust linter was credited with two-factor authentication.
98
+ * Two of the three signals behind that reading were substring collisions of this kind.
99
+ *
100
+ * Word boundaries in the languages people actually write: delimited by a non-letter
101
+ * (`otp_secret`, `verify(otp)`), or the capital that starts a camelCase word
102
+ * (`verifyOtp`, `otpCode`). `pyotp` no longer matches here and does not need to: it is
103
+ * a dependency, and dependencies are read from the manifest above.
104
+ */
105
+ const OTP_AS_A_WORD = /(?:^|[^a-z])t?otp(?:[^a-z]|$)|[a-z_](?:Otp|OTP|Totp|TOTP)/;
106
+ const twoFaSignals = await (0, textSearch_1.searchInFiles)(ctx.root, sourceFiles, [/two[_-]?factor/i, OTP_AS_A_WORD], 20);
94
107
  const apiKeySignals = await (0, textSearch_1.searchInFiles)(ctx.root, sourceFiles, [
95
108
  /**
96
109
  * A key this project checks, not a key it holds.
@@ -206,8 +219,17 @@ async function detectAuth(ctx) {
206
219
  const organizationSignals = strongOrganization.length > 0
207
220
  ? [...strongOrganization, ...weakOrganization]
208
221
  : [];
209
- const strongMembership = await (0, textSearch_1.searchInFiles)(ctx.root, sourceFiles, [/memberId/i, ...STRONG_TENANCY], 25);
210
- const weakMembership = await (0, textSearch_1.searchInFiles)(ctx.root, sourceFiles, WEAK_TENANCY, 25);
222
+ /**
223
+ * `memberId` is a weak word, by this detector's own rule.
224
+ *
225
+ * It was strong enough to stand alone, and `ScopedMemberId` — a symbol table in a
226
+ * compiler — was read as a tenant membership. "Member" means a struct field in most
227
+ * languages and a person in a few; only a tenant word says which. The comment above
228
+ * already states the principle: a weak word never stands on its own, however many
229
+ * files it appears in.
230
+ */
231
+ const strongMembership = await (0, textSearch_1.searchInFiles)(ctx.root, sourceFiles, STRONG_TENANCY, 25);
232
+ const weakMembership = await (0, textSearch_1.searchInFiles)(ctx.root, sourceFiles, [/memberId/i, ...WEAK_TENANCY], 25);
211
233
  const membershipSignals = strongMembership.length > 0 ? [...strongMembership, ...weakMembership] : [];
212
234
  const b2bSignals = await (0, textSearch_1.searchInFiles)(ctx.root, sourceFiles, [
213
235
  /stripe/i,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@produtype/core",
3
- "version": "0.26.0",
3
+ "version": "0.26.1",
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": {