@produtype/core 0.23.0 → 0.24.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.
@@ -1,3 +1,16 @@
1
1
  import type { Finding } from './types';
2
+ /**
3
+ * There is no fallback, and that is the point.
4
+ *
5
+ * There was one, and it said things like "a common attack is not defended against" and
6
+ * "a privacy obligation is unmet" — the finding's own title with the detail removed.
7
+ * A hundred and two actionable findings in the verification corpus carried one of those
8
+ * twelve sentences, under a heading that promises what actually happens.
9
+ *
10
+ * Saying nothing is better: the finding, its evidence and its recommendation are still
11
+ * there, and a reader is not handed a sentence that adds a line and no information. A
12
+ * test keeps the list honest by failing when a finding the fixtures produce has no
13
+ * specific sentence.
14
+ */
2
15
  export declare function businessImpactFor(finding: Finding): string | undefined;
3
16
  export declare function withBusinessImpact(findings: Finding[]): Finding[];
@@ -48,26 +48,56 @@ const IMPACT_BY_FINDING = [
48
48
  ['security.rate-limit', 'Credentials can be guessed in bulk and expensive endpoints can be used to run up your bill.'],
49
49
  ['uploads.public-exposure', 'Uploaded files are reachable by anyone who knows or guesses the path.'],
50
50
  ['billing.webhook', 'Payment notifications are not verified, so billing state can be changed by a forged request.'],
51
+ /**
52
+ * The profiles that are not a web application.
53
+ *
54
+ * This table was written for server products and never grew when `client-app`,
55
+ * `mobile-app` and `library` arrived, so a hundred and twenty-six actionable findings
56
+ * across the verification corpus said nothing at all here — every one of them in a
57
+ * product that is not a web application, which is the case this tool exists to judge
58
+ * on its own terms.
59
+ */
60
+ ['expectation.app.state-durability', 'Work the person has done lives only in storage the platform may clear, so a cleared cache, a private window or a reinstall loses it silently.'],
61
+ ['expectation.app.asset-delivery', 'The first screen waits on the network every time, and on a bad connection the product looks broken rather than slow.'],
62
+ ['expectation.client.error-reporting', 'A crash on somebody else device is invisible: the screen goes white, the person leaves, and nothing reaches anybody who could fix it.'],
63
+ ['expectation.mobile.offline', 'A network that comes and goes is the normal state of a phone, so the product spends part of every day showing a spinner in a lift or on a train.'],
64
+ ['expectation.mobile.credential-storage', 'A token in preferences or a plain file is readable on a rooted device and often travels into a backup, which is where it leaks from.'],
65
+ ['expectation.mobile.permissions', 'A permission asked with no reason attached is refused more often, and on iOS a missing purpose string is a review rejection rather than a warning.'],
66
+ ['expectation.mobile.forced-update', 'A version installed months ago keeps talking to the server whatever the release notes say, so a broken client cannot be retired.'],
67
+ ['expectation.mobile.privacy-declaration', 'What the store listing promises about data lives in a dashboard rather than beside the code, so it stops matching what the app collects and nobody notices.'],
68
+ ['expectation.packaging.license', 'Without a licence nobody may legally use the package, and a company that checks will refuse it at the dependency review.'],
69
+ ['expectation.packaging.entrypoints', 'Whoever installs the package cannot import it: what is published has no stated way in.'],
70
+ ['expectation.packaging.metadata', 'The registry cannot say what this is or who maintains it, so the package is hard to find and harder to trust.'],
71
+ ['expectation.docs.readme', 'Somebody who finds the package has nothing telling them what it does, so they move on.'],
72
+ ['expectation.quality.tests', 'Nothing proves the package works, so every release is a guess and every regression is found by a user.'],
73
+ ['expectation.quality.ci', 'The tests run when somebody remembers, which over a few months means they stop running.'],
74
+ ['expectation.b2c.notifications', 'There is no way to reach somebody after they close the tab: no confirmation, no password reset, no notice that something happened to their account.'],
75
+ ['expectation.b2c.onboarding', 'A new account lands on an empty product with nothing to do first, which is where most of them stop.'],
76
+ ['expectation.auth.api-keys', 'Machine access has to borrow a human session, so a script that stops working is indistinguishable from somebody signing out — and a leaked key cannot be revoked on its own.'],
77
+ ['auth.core', 'Anyone who finds a URL can use the product and read whatever it exposes. There is no notion of "your" data.'],
78
+ ['authz.resource-level', 'A normal user can read or change another user records by changing an identifier in the URL.'],
79
+ ['docker.presence', 'Environments drift between local, CI and production, and "works on my machine" becomes unfalsifiable.'],
80
+ ['env.example', 'Somebody setting the project up has to read the source to find out which variables it needs, and a missing one fails at runtime rather than at startup.'],
81
+ ['security.django-debug', 'With DEBUG on, an error page shows the stack, the settings and often the database contents to whoever triggered it.'],
82
+ ['security.django-secure-cookies', 'Session and CSRF cookies travel in the clear, so anybody on the same network can take a signed-in session.'],
51
83
  ['tenancy', 'Customer data is not separated, so one customer can reach another data.'],
52
84
  ['gdpr', 'A privacy obligation is unmet, and the obligation applies whether or not the feature exists.'],
53
85
  ['observability', 'Production behaviour is not visible, so problems are found late and diagnosed slowly.'],
54
86
  ['deployment', 'Releases are not reproducible, which makes recovery depend on individual memory.'],
55
87
  ];
56
- /** Fallback when no specific mapping matches, so every actionable finding says something. */
57
- const IMPACT_BY_CATEGORY = {
58
- auth: 'Access to the product is not properly controlled.',
59
- authz: 'Users can act beyond what their role should allow.',
60
- tenancy: 'Customer data is not reliably separated.',
61
- gdpr: 'A privacy obligation is unmet.',
62
- security: 'A common attack is not defended against.',
63
- uploads: 'Uploaded files are not properly controlled.',
64
- billing: 'Revenue handling is incomplete or unverified.',
65
- audit: 'Sensitive actions cannot be attributed after the fact.',
66
- observability: 'Production behaviour is not visible.',
67
- jobs: 'Long-running work is not handled reliably.',
68
- deployment: 'Releases are not reproducible.',
69
- env: 'Configuration or secrets are handled unsafely.',
70
- };
88
+ /**
89
+ * There is no fallback, and that is the point.
90
+ *
91
+ * There was one, and it said things like "a common attack is not defended against" and
92
+ * "a privacy obligation is unmet" — the finding's own title with the detail removed.
93
+ * A hundred and two actionable findings in the verification corpus carried one of those
94
+ * twelve sentences, under a heading that promises what actually happens.
95
+ *
96
+ * Saying nothing is better: the finding, its evidence and its recommendation are still
97
+ * there, and a reader is not handed a sentence that adds a line and no information. A
98
+ * test keeps the list honest by failing when a finding the fixtures produce has no
99
+ * specific sentence.
100
+ */
71
101
  function businessImpactFor(finding) {
72
102
  if (finding.status === 'passed')
73
103
  return undefined;
@@ -75,7 +105,7 @@ function businessImpactFor(finding) {
75
105
  if (finding.id.startsWith(prefix))
76
106
  return impact;
77
107
  }
78
- return IMPACT_BY_CATEGORY[finding.category];
108
+ return undefined;
79
109
  }
80
110
  /** Attaches the business impact to every actionable finding. */
81
111
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@produtype/core",
3
- "version": "0.23.0",
3
+ "version": "0.24.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": {