peer-ai-standards 1.0.0-next.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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/core/ai-features.d.ts +65 -0
  4. package/dist/core/ai-features.js +113 -0
  5. package/dist/core/api-design.d.ts +41 -0
  6. package/dist/core/api-design.js +91 -0
  7. package/dist/core/architecture.d.ts +41 -0
  8. package/dist/core/architecture.js +93 -0
  9. package/dist/core/backend.d.ts +21 -0
  10. package/dist/core/backend.js +25 -0
  11. package/dist/core/code-quality.d.ts +91 -0
  12. package/dist/core/code-quality.js +179 -0
  13. package/dist/core/data.d.ts +45 -0
  14. package/dist/core/data.js +59 -0
  15. package/dist/core/delivery.d.ts +153 -0
  16. package/dist/core/delivery.js +149 -0
  17. package/dist/core/design-accessibility.d.ts +124 -0
  18. package/dist/core/design-accessibility.js +199 -0
  19. package/dist/core/frontend.d.ts +77 -0
  20. package/dist/core/frontend.js +109 -0
  21. package/dist/core/mobile.d.ts +46 -0
  22. package/dist/core/mobile.js +83 -0
  23. package/dist/core/money.d.ts +81 -0
  24. package/dist/core/money.js +135 -0
  25. package/dist/core/operations.d.ts +108 -0
  26. package/dist/core/operations.js +177 -0
  27. package/dist/core/performance.d.ts +53 -0
  28. package/dist/core/performance.js +85 -0
  29. package/dist/core/privacy-compliance.d.ts +68 -0
  30. package/dist/core/privacy-compliance.js +77 -0
  31. package/dist/core/reliability.d.ts +67 -0
  32. package/dist/core/reliability.js +105 -0
  33. package/dist/core/requirements.d.ts +41 -0
  34. package/dist/core/requirements.js +58 -0
  35. package/dist/core/safety-critical.d.ts +41 -0
  36. package/dist/core/safety-critical.js +71 -0
  37. package/dist/core/security.d.ts +281 -0
  38. package/dist/core/security.js +427 -0
  39. package/dist/core/system-design.d.ts +31 -0
  40. package/dist/core/system-design.js +69 -0
  41. package/dist/core/testing.d.ts +51 -0
  42. package/dist/core/testing.js +124 -0
  43. package/dist/domains.d.ts +6 -0
  44. package/dist/domains.js +53 -0
  45. package/dist/index.d.ts +30 -0
  46. package/dist/index.js +118 -0
  47. package/dist/profile.d.ts +262 -0
  48. package/dist/profile.js +269 -0
  49. package/dist/profiles/express.d.ts +2 -0
  50. package/dist/profiles/express.js +93 -0
  51. package/dist/profiles/fastapi.d.ts +2 -0
  52. package/dist/profiles/fastapi.js +104 -0
  53. package/dist/profiles/fastify.d.ts +2 -0
  54. package/dist/profiles/fastify.js +56 -0
  55. package/dist/profiles/github-actions.d.ts +2 -0
  56. package/dist/profiles/github-actions.js +151 -0
  57. package/dist/profiles/nestjs.d.ts +2 -0
  58. package/dist/profiles/nestjs.js +77 -0
  59. package/dist/profiles/next.d.ts +2 -0
  60. package/dist/profiles/next.js +73 -0
  61. package/dist/profiles/node.d.ts +2 -0
  62. package/dist/profiles/node.js +79 -0
  63. package/dist/profiles/python.d.ts +2 -0
  64. package/dist/profiles/python.js +232 -0
  65. package/dist/profiles/react-native.d.ts +2 -0
  66. package/dist/profiles/react-native.js +133 -0
  67. package/dist/profiles/react.d.ts +2 -0
  68. package/dist/profiles/react.js +180 -0
  69. package/dist/profiles/typescript.d.ts +2 -0
  70. package/dist/profiles/typescript.js +214 -0
  71. package/dist/rule.d.ts +72 -0
  72. package/dist/rule.js +39 -0
  73. package/package.json +39 -0
@@ -0,0 +1,427 @@
1
+ // Keeping people's data and the system itself safe from attack. Each rule cites the OWASP
2
+ // Application Security Verification Standard (ASVS) 5.0 requirement it comes from, checked
3
+ // against OWASP's own text. Rules with no source are engineering practice ASVS doesn't cover.
4
+ const ASVS = "OWASP ASVS 5.0";
5
+ const chapter = (file) => `https://github.com/OWASP/ASVS/blob/master/5.0/en/${file}`;
6
+ const V1 = chapter("0x10-V1-Encoding-and-Sanitization.md");
7
+ const V2 = chapter("0x11-V2-Validation-and-Business-Logic.md");
8
+ const V3 = chapter("0x12-V3-Web-Frontend-Security.md");
9
+ const V5 = chapter("0x14-V5-File-Handling.md");
10
+ const V6 = chapter("0x15-V6-Authentication.md");
11
+ const V7 = chapter("0x16-V7-Session-Management.md");
12
+ const V8 = chapter("0x17-V8-Authorization.md");
13
+ const V9 = chapter("0x18-V9-Self-contained-Tokens.md");
14
+ const V12 = chapter("0x21-V12-Secure-Communication.md");
15
+ const V13 = chapter("0x22-V13-Configuration.md");
16
+ const V14 = chapter("0x23-V14-Data-Protection.md");
17
+ const V16 = chapter("0x25-V16-Security-Logging-and-Error-Handling.md");
18
+ export const security = [
19
+ {
20
+ id: "SEC-01",
21
+ domain: "security",
22
+ title: "Permission is checked per record, not per role",
23
+ rule: "Being signed in, or holding a role, is not permission to use *this* record. Every request that names a record proves the caller may use that record. Review every endpoint that takes an id, not a sample.",
24
+ why: "Changing an id in a URL is the easiest attack there is, and it leaks one customer's data to another.",
25
+ ask: "Does every endpoint that takes an id check that the caller may use that record?",
26
+ stage: "mvp",
27
+ check: "ai-review",
28
+ severity: "high",
29
+ sources: [{ name: ASVS, ref: "8.2.2, level 1", url: V8 }],
30
+ },
31
+ {
32
+ id: "SEC-02",
33
+ domain: "security",
34
+ title: "Each action checks the caller's role allows it",
35
+ rule: "Every action checks that the caller's role allows it, and different roles have different powers: releasing money needs the specific role for it, not just any staff role.",
36
+ why: "When every staff role can do everything, one compromised support account can do what only finance should.",
37
+ ask: "Does every action in this change check for the specific role it needs?",
38
+ stage: "mvp",
39
+ check: "ai-review",
40
+ severity: "high",
41
+ sources: [{ name: ASVS, ref: "8.2.1, level 1", url: V8 }],
42
+ },
43
+ {
44
+ id: "SEC-03",
45
+ domain: "security",
46
+ title: "Permission is decided on the server, on every request",
47
+ rule: "Permission checks happen on the server on every request, never based on anything the client can change, and never carried over from an earlier request.",
48
+ why: "Anything the client decides, an attacker can change: hiding a button isn't a permission check.",
49
+ ask: "Is every permission in this change decided on the server, from data the client can't change?",
50
+ stage: "mvp",
51
+ check: "ai-review",
52
+ severity: "high",
53
+ sources: [{ name: ASVS, ref: "8.3.1, level 1", url: V8 }],
54
+ },
55
+ {
56
+ id: "SEC-04",
57
+ domain: "security",
58
+ title: "A session is for one app, and the app says which",
59
+ rule: "Where one backend serves several apps or tenants, each session is issued for exactly one of them and accepted only there. The app always says which one it is signing in to; the server never guesses.",
60
+ why: "Guessing prefers the most powerful option, so a person with two roles can end up in the wrong app with the wrong powers, and the failure surfaces much later as confusing permission errors.",
61
+ ask: "Is every session in this change tied to one app or tenant, and does the app say which?",
62
+ stage: "mvp",
63
+ check: "ai-review",
64
+ severity: "high",
65
+ when: ["several-audiences"],
66
+ sources: [
67
+ { name: ASVS, ref: "9.2.3, level 2", url: V9 },
68
+ { name: ASVS, ref: "8.4.1, level 2", url: V8 },
69
+ ],
70
+ },
71
+ {
72
+ id: "SEC-05",
73
+ domain: "security",
74
+ title: "Input is validated on the server",
75
+ rule: "Every request that changes something is validated on the server against what's allowed. Checks in the app or the browser are for convenience, never a control.",
76
+ why: "Anyone can send a request without using your app, so a check that only runs in the app doesn't run at all for an attacker.",
77
+ ask: "Is every input in this change validated on the server?",
78
+ stage: "prototype",
79
+ check: "ai-review",
80
+ severity: "high",
81
+ sources: [
82
+ { name: ASVS, ref: "2.2.2, level 1", url: V2 },
83
+ { name: ASVS, ref: "2.2.1, level 1", url: V2 },
84
+ ],
85
+ },
86
+ {
87
+ id: "SEC-06",
88
+ domain: "security",
89
+ title: "Data from outside is checked before it's trusted",
90
+ rule: "Data that crosses a boundary (an API response, stored data, an imported file, a message from another service) is checked against the shape it should have, never just assumed to have it.",
91
+ why: "Code that assumes the shape of outside data breaks, or worse carries on with wrong data, the first time that data is different.",
92
+ ask: "Is every piece of outside data in this change checked before it's used?",
93
+ stage: "mvp",
94
+ check: "ai-review",
95
+ severity: "medium",
96
+ sources: [{ name: ASVS, ref: "2.2.1, level 1", url: V2 }],
97
+ },
98
+ {
99
+ id: "SEC-07",
100
+ domain: "security",
101
+ title: "Database queries are parameterised",
102
+ rule: "Database queries never paste values into the query text. They use parameters or a query builder, so a value can never change what the query does.",
103
+ why: "A value pasted into a query can rewrite it: that's SQL injection, which can read or delete a whole database.",
104
+ ask: "Does any query in this change build its text from values?",
105
+ stage: "prototype",
106
+ check: "auto",
107
+ severity: "critical",
108
+ sources: [{ name: ASVS, ref: "1.2.4, level 1", url: V1 }],
109
+ },
110
+ {
111
+ id: "SEC-08",
112
+ domain: "security",
113
+ title: "Content from outside is never put into a page as HTML",
114
+ rule: "Content from people or outside services, including text an AI model produced, is shown as text. If it truly must be HTML, it goes through a well-known sanitiser first.",
115
+ why: "HTML inserted into a page can run a script in the viewer's browser, with their session.",
116
+ ask: "Does this change put any outside content into a page as HTML without a sanitiser?",
117
+ stage: "prototype",
118
+ check: "auto",
119
+ severity: "high",
120
+ sources: [
121
+ { name: ASVS, ref: "1.3.1, level 1", url: V1 },
122
+ { name: ASVS, ref: "1.2.1, level 1", url: V1 },
123
+ ],
124
+ },
125
+ {
126
+ id: "SEC-09",
127
+ domain: "security",
128
+ title: "No internal detail reaches the client",
129
+ rule: "Errors shown to the client are generic and actionable: no stack traces, file paths, queries or keys. The detail goes to the log, with an id the client can quote. Debug modes are off in production.",
130
+ why: "Internal detail is a map for an attacker: it shows what's running, where, and how it fails.",
131
+ ask: "Could any error in this change reveal internal detail to the client?",
132
+ stage: "mvp",
133
+ check: "ai-review",
134
+ severity: "medium",
135
+ sources: [
136
+ { name: ASVS, ref: "16.5.1, level 2", url: V16 },
137
+ { name: ASVS, ref: "13.4.2, level 2", url: V13 },
138
+ ],
139
+ },
140
+ {
141
+ id: "SEC-10",
142
+ domain: "security",
143
+ title: "No secret in code, ever",
144
+ rule: "Keys, passwords and tokens never live in code or in the repository. They come from a secrets manager or the environment.",
145
+ why: "A secret in a repository is readable by everyone who can ever read the repository, including from its history after the file is deleted.",
146
+ ask: "Does this change put a key, password or token in code or the repository?",
147
+ stage: "prototype",
148
+ check: "auto",
149
+ severity: "critical",
150
+ sources: [{ name: ASVS, ref: "13.3.1, level 2", url: V13 }],
151
+ },
152
+ {
153
+ id: "SEC-11",
154
+ domain: "security",
155
+ title: "Nothing secret is built into an app or a web page",
156
+ rule: "Anything shipped to a person's device (a web page's code, a mobile app) can be read by anyone, so no key or secret is ever built into it, even from an environment variable at build time. A call that needs a secret goes through your server.",
157
+ why: "A key built into an app is extracted within hours of release, and then it's everyone's key.",
158
+ ask: "Does this change build any key or secret into code that runs on a person's device?",
159
+ stage: "prototype",
160
+ check: "ai-review",
161
+ severity: "critical",
162
+ },
163
+ {
164
+ id: "SEC-12",
165
+ domain: "security",
166
+ title: "Sign-in is protected against guessing",
167
+ rule: "Sign-in, password reset, two-factor and one-time codes are rate limited, so passwords and codes can't be guessed at speed.",
168
+ why: "With unlimited attempts, a leaked list of passwords from another site opens accounts on yours.",
169
+ ask: "Is every sign-in, reset and code endpoint in this change rate limited?",
170
+ stage: "mvp",
171
+ check: "ai-review",
172
+ severity: "high",
173
+ sources: [{ name: ASVS, ref: "6.3.1, level 1", url: V6 }],
174
+ },
175
+ {
176
+ id: "SEC-13",
177
+ domain: "security",
178
+ title: "Rate limits are named policies in one place",
179
+ rule: "Rate limits are defined as named policies in one place, not numbers scattered through the code.",
180
+ why: 'Scattered limits drift apart, and nobody can answer "what are our limits?" when an attack is happening.',
181
+ ask: "Does this change add a rate limit anywhere other than the shared policies?",
182
+ stage: "production",
183
+ check: "ai-review",
184
+ severity: "low",
185
+ },
186
+ {
187
+ id: "SEC-14",
188
+ domain: "security",
189
+ title: "Sessions end",
190
+ rule: "Sessions expire after a period of inactivity and after a maximum lifetime. Signing out ends the session on the server, not just in the app, and disabling an account ends all its sessions.",
191
+ why: "A session that never ends turns one stolen token, or one shared computer, into permanent access.",
192
+ ask: "In this change, can a session outlive sign-out, account removal or its maximum lifetime?",
193
+ stage: "mvp",
194
+ check: "ai-review",
195
+ severity: "medium",
196
+ sources: [
197
+ { name: ASVS, ref: "7.4.1, level 1", url: V7 },
198
+ { name: ASVS, ref: "7.4.2, level 1", url: V7 },
199
+ { name: ASVS, ref: "7.3.2, level 2", url: V7 },
200
+ ],
201
+ },
202
+ {
203
+ id: "SEC-15",
204
+ domain: "security",
205
+ title: "Everything travels encrypted",
206
+ rule: "All traffic between apps and services uses TLS, never falls back to an unencrypted connection, and no platform setting allows unencrypted traffic.",
207
+ why: "Unencrypted traffic can be read and changed by anyone on the same network, such as public Wi-Fi.",
208
+ ask: "Does anything in this change send or allow traffic without TLS?",
209
+ stage: "mvp",
210
+ check: "ai-review",
211
+ severity: "high",
212
+ sources: [
213
+ { name: ASVS, ref: "12.2.1, level 1", url: V12 },
214
+ {
215
+ name: "OWASP MASVS 2.1.0",
216
+ ref: "MASVS-NETWORK-1",
217
+ url: "https://github.com/OWASP/masvs/blob/master/controls/MASVS-NETWORK-1.md",
218
+ },
219
+ ],
220
+ },
221
+ {
222
+ id: "SEC-16",
223
+ domain: "security",
224
+ title: "Which websites may call the API is a fixed list",
225
+ rule: "The websites allowed to call the API from a browser are a fixed list. The API never echoes back whatever origin asked, least of all when requests carry credentials.",
226
+ why: "An API that allows any origin with credentials lets any website act as a signed-in visitor.",
227
+ ask: "Is every allowed origin in this change on a fixed list?",
228
+ stage: "mvp",
229
+ check: "ai-review",
230
+ severity: "medium",
231
+ sources: [{ name: ASVS, ref: "3.4.2, level 1", url: V3 }],
232
+ },
233
+ {
234
+ id: "SEC-17",
235
+ domain: "security",
236
+ title: "Web responses set their security headers",
237
+ rule: "Web responses set Strict-Transport-Security, so browsers always use HTTPS, and a Content-Security-Policy, so the browser runs only trusted scripts.",
238
+ why: "These headers stop whole classes of attack in the browser, for the cost of a line of configuration.",
239
+ ask: "Do the web responses in this change set Strict-Transport-Security and a Content-Security-Policy?",
240
+ stage: "mvp",
241
+ check: "auto",
242
+ severity: "medium",
243
+ sources: [
244
+ { name: ASVS, ref: "3.4.1, level 1", url: V3 },
245
+ { name: ASVS, ref: "3.4.3, level 2", url: V3 },
246
+ ],
247
+ },
248
+ {
249
+ id: "SEC-18",
250
+ domain: "security",
251
+ title: "The server names stored files",
252
+ rule: "Stored files get names and paths the server makes. A name supplied by the client is never used as a path.",
253
+ why: "A client-supplied name like `../../config` can read or overwrite files it should never reach.",
254
+ ask: "Does this change use any client-supplied name as a storage path?",
255
+ stage: "mvp",
256
+ check: "ai-review",
257
+ severity: "high",
258
+ when: ["uploads"],
259
+ sources: [{ name: ASVS, ref: "5.3.2, level 1", url: V5 }],
260
+ },
261
+ {
262
+ id: "SEC-19",
263
+ domain: "security",
264
+ title: "An upload's content and size are checked",
265
+ rule: "An uploaded file's content is checked to be the type it claims, not just its extension, and its size is limited.",
266
+ why: "A renamed file slips past an extension check, and an unlimited upload is an easy way to take a service down.",
267
+ ask: "Does this change check an upload's content and size, not just its extension?",
268
+ stage: "mvp",
269
+ check: "ai-review",
270
+ severity: "medium",
271
+ when: ["uploads"],
272
+ sources: [
273
+ { name: ASVS, ref: "5.2.2, level 1", url: V5 },
274
+ { name: ASVS, ref: "5.2.1, level 1", url: V5 },
275
+ ],
276
+ },
277
+ {
278
+ id: "SEC-20",
279
+ domain: "security",
280
+ title: "Private files are shared through short-lived links",
281
+ rule: "Private files are served through signed links that expire soon, and every access is recorded.",
282
+ why: "A permanent link to a private file stays valid after it's forwarded, leaked or no longer allowed.",
283
+ ask: "Are the private files in this change served through expiring links, with access recorded?",
284
+ stage: "production",
285
+ check: "ai-review",
286
+ severity: "medium",
287
+ when: ["uploads"],
288
+ },
289
+ {
290
+ id: "SEC-21",
291
+ domain: "security",
292
+ title: "Every live event is authorised, not just the connection",
293
+ rule: "A live subscription checks permission for every event it sends, not once when it connects, and events go only to the people they're for.",
294
+ why: "Permissions change while a connection stays open, and a connection authorised once keeps receiving what it's no longer allowed to see.",
295
+ ask: "Does every event in this change check permission, and reach only the people it's for?",
296
+ stage: "mvp",
297
+ check: "ai-review",
298
+ severity: "high",
299
+ when: ["real-time"],
300
+ },
301
+ {
302
+ id: "SEC-22",
303
+ domain: "security",
304
+ title: "Nothing sensitive goes in a URL",
305
+ rule: "Tokens, keys and personal data never go in a URL or its query string. They travel in the request body or headers.",
306
+ why: "URLs are kept in server logs, browser history and the referrer sent to other sites, long after the request.",
307
+ ask: "Does this change put a token, key or personal data in a URL?",
308
+ stage: "prototype",
309
+ check: "ai-review",
310
+ severity: "medium",
311
+ sources: [{ name: ASVS, ref: "14.2.1, level 1", url: V14 }],
312
+ },
313
+ {
314
+ id: "SEC-23",
315
+ domain: "security",
316
+ title: "Only modern TLS, with strong ciphers",
317
+ rule: "Only current TLS versions are switched on (TLS 1.2 and 1.3, with 1.3 preferred), only recommended cipher suites are allowed, strongest first, and public services use publicly trusted certificates. On a hosting platform that manages TLS, its settings are checked rather than assumed.",
318
+ why: "Old TLS versions and weak ciphers have known breaks, so traffic that uses them can be read or changed even though it's encrypted.",
319
+ ask: "Does this change switch on an old TLS version, a weak cipher or a certificate that isn't publicly trusted?",
320
+ stage: "mvp",
321
+ check: "auto",
322
+ severity: "medium",
323
+ sources: [
324
+ { name: ASVS, ref: "12.1.1, level 1", url: V12 },
325
+ { name: ASVS, ref: "12.1.2, level 2", url: V12 },
326
+ { name: ASVS, ref: "12.2.2, level 1", url: V12 },
327
+ ],
328
+ },
329
+ {
330
+ id: "SEC-24",
331
+ domain: "security",
332
+ title: "Security events are logged",
333
+ rule: "Sign-ins, failed sign-ins, refused permission checks and attempts to get past a security control, such as input that fails validation or a hit on a rate limit, are logged with who, what and when, but never the password, token or personal data involved.",
334
+ why: "You can't stop or recover from an attack you can't see. These logs are how an attack gets noticed, and how anyone works out afterwards what it reached.",
335
+ ask: "Does this change add a sign-in, permission check or security control that refuses someone without logging it?",
336
+ stage: "mvp",
337
+ check: "ai-review",
338
+ severity: "medium",
339
+ sources: [
340
+ { name: ASVS, ref: "16.3.1, level 2", url: V16 },
341
+ { name: ASVS, ref: "16.3.2, level 2", url: V16 },
342
+ { name: ASVS, ref: "16.3.3, level 2", url: V16 },
343
+ ],
344
+ },
345
+ {
346
+ id: "SEC-25",
347
+ domain: "security",
348
+ title: "The threat model changes when the ways in change",
349
+ rule: "When a change adds a new way into the system, such as an endpoint, an upload, a webhook, a sign-in method or an outside service, the same change updates the threat model: what could go wrong through it, and what stops it.",
350
+ why: "A threat model written once describes the system as it was. Attacks come through the parts added since.",
351
+ ask: "Does this change add a way into the system that the threat model doesn't cover yet?",
352
+ stage: "mvp",
353
+ check: "ai-review",
354
+ severity: "medium",
355
+ },
356
+ {
357
+ id: "SEC-26",
358
+ domain: "security",
359
+ title: "Every secret can be replaced quickly",
360
+ rule: "A list names each of the project's secrets, where it lives and how to replace it. Each can be replaced without changing code, and each is replaced on a schedule and at once if it may have leaked.",
361
+ why: "A leaked key keeps working until it's replaced. Deleting it from wherever it leaked doesn't stop anyone who already copied it.",
362
+ ask: "Can every secret this change adds be replaced without changing code, and is it on the list?",
363
+ stage: "production",
364
+ check: "ai-review",
365
+ severity: "medium",
366
+ sources: [
367
+ { name: ASVS, ref: "13.1.4, level 3", url: V13 },
368
+ { name: ASVS, ref: "13.3.4, level 3", url: V13 },
369
+ ],
370
+ },
371
+ {
372
+ id: "SEC-27",
373
+ domain: "security",
374
+ title: "Secret scanning runs on every change",
375
+ rule: "A secret scanner checks every change before it merges, and the repository's history once, and fails the build when it finds a secret. It's confirmed to have actually run.",
376
+ why: 'Secrets slip in by accident: a key pasted in to test something, an .env file added by mistake. A scanner catches them before they spread, and a crashed scanner reports the same "nothing found" as a clean one. A missing scanner is a gap in the safety net, not a leak, so it ranks below a secret actually in the code (SEC-10).',
377
+ ask: "Does every change pass a secret scan that fails the build when it finds a secret?",
378
+ stage: "mvp",
379
+ check: "auto",
380
+ severity: "medium",
381
+ },
382
+ {
383
+ id: "SEC-28",
384
+ domain: "security",
385
+ title: "Each service has only the access it needs",
386
+ rule: "Each service, job and part of the product reaches the others with an account of its own, allowed only what it needs: a service that reads one table can't drop the database, and none shares an administrator's account.",
387
+ why: "When one part is broken into, its account decides how far the attacker gets. An account that can do everything turns one weak part into the whole system.",
388
+ ask: "Does this change give a service, job or part more access than it needs, or a shared or administrator account?",
389
+ stage: "mvp",
390
+ check: "ai-review",
391
+ severity: "high",
392
+ sources: [
393
+ { name: ASVS, ref: "13.2.1, level 2", url: V13 },
394
+ { name: ASVS, ref: "13.2.2, level 2", url: V13 },
395
+ ],
396
+ },
397
+ {
398
+ id: "SEC-29",
399
+ domain: "security",
400
+ title: "Nothing is public unless it's meant to be",
401
+ rule: "Storage, databases, queues, admin and monitoring pages, internal documentation and debug modes are reachable only by who needs them. Each thing open to the internet is open on purpose, and says so in the configuration.",
402
+ why: "A storage bucket or database left open to the internet is found by automated scanners within hours, and everything in it is exposed at once.",
403
+ ask: "Does this change make anything reachable from the internet that isn't meant to be, or switch on a debug mode in production?",
404
+ stage: "mvp",
405
+ check: "ai-review",
406
+ severity: "critical",
407
+ sources: [
408
+ { name: ASVS, ref: "13.4.5, level 2", url: V13 },
409
+ { name: ASVS, ref: "13.4.2, level 2", url: V13 },
410
+ ],
411
+ },
412
+ {
413
+ id: "SEC-30",
414
+ domain: "security",
415
+ title: "Nothing from outside runs as code or a command",
416
+ rule: "Text is never run as code, such as with eval or by building a function from a string, and a system command never goes through a shell with outside data in it: commands take their arguments as a list.",
417
+ why: "Text that runs as code or as a shell command can do anything the process can: read its secrets, change its data, or take over the machine.",
418
+ ask: "Does this change run text as code, or pass outside data to a shell?",
419
+ stage: "prototype",
420
+ check: "ai-review",
421
+ severity: "critical",
422
+ sources: [
423
+ { name: ASVS, ref: "1.3.2, level 1", url: V1 },
424
+ { name: ASVS, ref: "1.2.5, level 1", url: V1 },
425
+ ],
426
+ },
427
+ ];
@@ -0,0 +1,31 @@
1
+ export declare const systemDesign: ({
2
+ id: string;
3
+ domain: "system-design";
4
+ title: string;
5
+ rule: string;
6
+ why: string;
7
+ ask: string;
8
+ stage: "mvp";
9
+ check: "ai-review";
10
+ severity: "high";
11
+ } | {
12
+ id: string;
13
+ domain: "system-design";
14
+ title: string;
15
+ rule: string;
16
+ why: string;
17
+ ask: string;
18
+ stage: "mvp";
19
+ check: "ai-review";
20
+ severity: "medium";
21
+ } | {
22
+ id: string;
23
+ domain: "system-design";
24
+ title: string;
25
+ rule: string;
26
+ why: string;
27
+ ask: string;
28
+ stage: "production";
29
+ check: "ai-review";
30
+ severity: "medium";
31
+ })[];
@@ -0,0 +1,69 @@
1
+ // How the system behaves under load, concurrency and background work.
2
+ export const systemDesign = [
3
+ {
4
+ id: "SYS-01",
5
+ domain: "system-design",
6
+ title: "Background jobs are safe to run twice",
7
+ rule: "A background job gives the same result if it runs twice, because queues redeliver.",
8
+ why: "A job that assumes it runs exactly once corrupts data the first time a queue redelivers it.",
9
+ ask: "What happens if a job in this change runs twice?",
10
+ stage: "mvp",
11
+ check: "ai-review",
12
+ severity: "high",
13
+ },
14
+ {
15
+ id: "SYS-02",
16
+ domain: "system-design",
17
+ title: "Nothing slow blocks a request",
18
+ rule: "Slow work such as sending email, push notifications and processing documents goes to a queue. A request never waits for it.",
19
+ why: "A confirmation that waits on a mail server fails whenever the mail server is slow.",
20
+ ask: "Does any request in this change wait for slow work that could go to a queue?",
21
+ stage: "mvp",
22
+ check: "ai-review",
23
+ severity: "medium",
24
+ },
25
+ {
26
+ id: "SYS-03",
27
+ domain: "system-design",
28
+ title: "Two people will do the same thing at the same time",
29
+ rule: "If two people doing the same thing at once could corrupt data, the database prevents it, not the order the code happens to run in. Counting rows and adding one is not a way to make ids.",
30
+ why: "Two checkouts on the last slot, or two couriers accepting one job, happen every day under real load.",
31
+ ask: "What happens if two people run this change's code at the same moment?",
32
+ stage: "mvp",
33
+ check: "ai-review",
34
+ severity: "high",
35
+ },
36
+ {
37
+ id: "SYS-04",
38
+ domain: "system-design",
39
+ title: "What's read to be changed is locked first",
40
+ rule: "Reading a value, changing it and writing it back, such as a balance or a count of places left, locks the record while it happens, using one shared lock helper.",
41
+ why: "Without the lock, two changes read the same value and one of them is silently lost.",
42
+ ask: "Does any read, change and write in this change lock what it's changing?",
43
+ stage: "mvp",
44
+ check: "ai-review",
45
+ severity: "high",
46
+ },
47
+ {
48
+ id: "SYS-05",
49
+ domain: "system-design",
50
+ title: "A database guarantee beats a check in code",
51
+ rule: "Where a rule can be a database guarantee, such as a unique constraint, it is one, rather than a check in the code.",
52
+ why: "\"Check that it doesn't exist, then insert\" can be raced. A unique constraint can't.",
53
+ ask: "Does this change check a rule in code that the database could guarantee?",
54
+ stage: "mvp",
55
+ check: "ai-review",
56
+ severity: "medium",
57
+ },
58
+ {
59
+ id: "SYS-06",
60
+ domain: "system-design",
61
+ title: "Contested state is tested concurrently",
62
+ rule: "Anything with limited capacity, held money or payouts has a test that runs it concurrently.",
63
+ why: "A test that runs one step at a time can't find the bug that only happens when two run at once.",
64
+ ask: "Does this change to contested state come with a test that runs it concurrently?",
65
+ stage: "production",
66
+ check: "ai-review",
67
+ severity: "medium",
68
+ },
69
+ ];
@@ -0,0 +1,51 @@
1
+ export declare const testing: ({
2
+ id: string;
3
+ domain: "testing";
4
+ title: string;
5
+ rule: string;
6
+ why: string;
7
+ ask: string;
8
+ stage: "mvp";
9
+ check: "ai-review";
10
+ severity: "medium";
11
+ } | {
12
+ id: string;
13
+ domain: "testing";
14
+ title: string;
15
+ rule: string;
16
+ why: string;
17
+ ask: string;
18
+ stage: "mvp";
19
+ check: "auto";
20
+ severity: "medium";
21
+ } | {
22
+ id: string;
23
+ domain: "testing";
24
+ title: string;
25
+ rule: string;
26
+ why: string;
27
+ ask: string;
28
+ stage: "mvp";
29
+ check: "ai-review";
30
+ severity: "low";
31
+ } | {
32
+ id: string;
33
+ domain: "testing";
34
+ title: string;
35
+ rule: string;
36
+ why: string;
37
+ ask: string;
38
+ stage: "production";
39
+ check: "person";
40
+ severity: "medium";
41
+ } | {
42
+ id: string;
43
+ domain: "testing";
44
+ title: string;
45
+ rule: string;
46
+ why: string;
47
+ ask: string;
48
+ stage: "production";
49
+ check: "ai-review";
50
+ severity: "medium";
51
+ })[];