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.
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/core/ai-features.d.ts +65 -0
- package/dist/core/ai-features.js +113 -0
- package/dist/core/api-design.d.ts +41 -0
- package/dist/core/api-design.js +91 -0
- package/dist/core/architecture.d.ts +41 -0
- package/dist/core/architecture.js +93 -0
- package/dist/core/backend.d.ts +21 -0
- package/dist/core/backend.js +25 -0
- package/dist/core/code-quality.d.ts +91 -0
- package/dist/core/code-quality.js +179 -0
- package/dist/core/data.d.ts +45 -0
- package/dist/core/data.js +59 -0
- package/dist/core/delivery.d.ts +153 -0
- package/dist/core/delivery.js +149 -0
- package/dist/core/design-accessibility.d.ts +124 -0
- package/dist/core/design-accessibility.js +199 -0
- package/dist/core/frontend.d.ts +77 -0
- package/dist/core/frontend.js +109 -0
- package/dist/core/mobile.d.ts +46 -0
- package/dist/core/mobile.js +83 -0
- package/dist/core/money.d.ts +81 -0
- package/dist/core/money.js +135 -0
- package/dist/core/operations.d.ts +108 -0
- package/dist/core/operations.js +177 -0
- package/dist/core/performance.d.ts +53 -0
- package/dist/core/performance.js +85 -0
- package/dist/core/privacy-compliance.d.ts +68 -0
- package/dist/core/privacy-compliance.js +77 -0
- package/dist/core/reliability.d.ts +67 -0
- package/dist/core/reliability.js +105 -0
- package/dist/core/requirements.d.ts +41 -0
- package/dist/core/requirements.js +58 -0
- package/dist/core/safety-critical.d.ts +41 -0
- package/dist/core/safety-critical.js +71 -0
- package/dist/core/security.d.ts +281 -0
- package/dist/core/security.js +427 -0
- package/dist/core/system-design.d.ts +31 -0
- package/dist/core/system-design.js +69 -0
- package/dist/core/testing.d.ts +51 -0
- package/dist/core/testing.js +124 -0
- package/dist/domains.d.ts +6 -0
- package/dist/domains.js +53 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +118 -0
- package/dist/profile.d.ts +262 -0
- package/dist/profile.js +269 -0
- package/dist/profiles/express.d.ts +2 -0
- package/dist/profiles/express.js +93 -0
- package/dist/profiles/fastapi.d.ts +2 -0
- package/dist/profiles/fastapi.js +104 -0
- package/dist/profiles/fastify.d.ts +2 -0
- package/dist/profiles/fastify.js +56 -0
- package/dist/profiles/github-actions.d.ts +2 -0
- package/dist/profiles/github-actions.js +151 -0
- package/dist/profiles/nestjs.d.ts +2 -0
- package/dist/profiles/nestjs.js +77 -0
- package/dist/profiles/next.d.ts +2 -0
- package/dist/profiles/next.js +73 -0
- package/dist/profiles/node.d.ts +2 -0
- package/dist/profiles/node.js +79 -0
- package/dist/profiles/python.d.ts +2 -0
- package/dist/profiles/python.js +232 -0
- package/dist/profiles/react-native.d.ts +2 -0
- package/dist/profiles/react-native.js +133 -0
- package/dist/profiles/react.d.ts +2 -0
- package/dist/profiles/react.js +180 -0
- package/dist/profiles/typescript.d.ts +2 -0
- package/dist/profiles/typescript.js +214 -0
- package/dist/rule.d.ts +72 -0
- package/dist/rule.js +39 -0
- package/package.json +39 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export declare const money: ({
|
|
2
|
+
id: string;
|
|
3
|
+
domain: "money";
|
|
4
|
+
title: string;
|
|
5
|
+
rule: string;
|
|
6
|
+
why: string;
|
|
7
|
+
ask: string;
|
|
8
|
+
stage: "prototype";
|
|
9
|
+
check: "auto";
|
|
10
|
+
severity: "critical";
|
|
11
|
+
} | {
|
|
12
|
+
id: string;
|
|
13
|
+
domain: "money";
|
|
14
|
+
title: string;
|
|
15
|
+
rule: string;
|
|
16
|
+
why: string;
|
|
17
|
+
ask: string;
|
|
18
|
+
stage: "mvp";
|
|
19
|
+
check: "ai-review";
|
|
20
|
+
severity: "high";
|
|
21
|
+
} | {
|
|
22
|
+
id: string;
|
|
23
|
+
domain: "money";
|
|
24
|
+
title: string;
|
|
25
|
+
rule: string;
|
|
26
|
+
why: string;
|
|
27
|
+
ask: string;
|
|
28
|
+
stage: "prototype";
|
|
29
|
+
check: "ai-review";
|
|
30
|
+
severity: "high";
|
|
31
|
+
} | {
|
|
32
|
+
id: string;
|
|
33
|
+
domain: "money";
|
|
34
|
+
title: string;
|
|
35
|
+
rule: string;
|
|
36
|
+
why: string;
|
|
37
|
+
ask: string;
|
|
38
|
+
stage: "prototype";
|
|
39
|
+
check: "auto";
|
|
40
|
+
severity: "medium";
|
|
41
|
+
} | {
|
|
42
|
+
id: string;
|
|
43
|
+
domain: "money";
|
|
44
|
+
title: string;
|
|
45
|
+
rule: string;
|
|
46
|
+
why: string;
|
|
47
|
+
ask: string;
|
|
48
|
+
stage: "mvp";
|
|
49
|
+
check: "auto";
|
|
50
|
+
severity: "high";
|
|
51
|
+
} | {
|
|
52
|
+
id: string;
|
|
53
|
+
domain: "money";
|
|
54
|
+
title: string;
|
|
55
|
+
rule: string;
|
|
56
|
+
why: string;
|
|
57
|
+
ask: string;
|
|
58
|
+
stage: "mvp";
|
|
59
|
+
check: "ai-review";
|
|
60
|
+
severity: "critical";
|
|
61
|
+
} | {
|
|
62
|
+
id: string;
|
|
63
|
+
domain: "money";
|
|
64
|
+
title: string;
|
|
65
|
+
rule: string;
|
|
66
|
+
why: string;
|
|
67
|
+
ask: string;
|
|
68
|
+
stage: "production";
|
|
69
|
+
check: "ai-review";
|
|
70
|
+
severity: "medium";
|
|
71
|
+
} | {
|
|
72
|
+
id: string;
|
|
73
|
+
domain: "money";
|
|
74
|
+
title: string;
|
|
75
|
+
rule: string;
|
|
76
|
+
why: string;
|
|
77
|
+
ask: string;
|
|
78
|
+
stage: "prototype";
|
|
79
|
+
check: "ai-review";
|
|
80
|
+
severity: "critical";
|
|
81
|
+
})[];
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// For products with the money trait. The project's add-on names its currency's smallest unit.
|
|
2
|
+
export const money = [
|
|
3
|
+
{
|
|
4
|
+
id: "MONEY-01",
|
|
5
|
+
domain: "money",
|
|
6
|
+
title: "Money is a whole number in the currency's smallest unit",
|
|
7
|
+
rule: "Money is stored and calculated as a whole number in the currency's smallest unit (pence, kobo, cents), and named for it, such as `amountMinor` or `amount_minor`. A decimal number touching money is a critical problem on sight, even for display.",
|
|
8
|
+
why: "Decimal fractions can't hold most money amounts exactly. The errors are tiny, silent, and they add up.",
|
|
9
|
+
ask: "Is any money held or calculated as a decimal number?",
|
|
10
|
+
stage: "prototype",
|
|
11
|
+
check: "auto",
|
|
12
|
+
severity: "critical",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "MONEY-02",
|
|
16
|
+
domain: "money",
|
|
17
|
+
title: "Each amount has one authoritative value",
|
|
18
|
+
rule: "Each amount has one authoritative field. Any other copy is a mirror on its way out, and deciding anything from it is a defect, even when it gives the right answer.",
|
|
19
|
+
why: "Two copies of an amount drift apart, and a decision made from the wrong one is wrong in a way nobody notices.",
|
|
20
|
+
ask: "Does any decision in this change read a copy of an amount rather than its authoritative value?",
|
|
21
|
+
stage: "mvp",
|
|
22
|
+
check: "ai-review",
|
|
23
|
+
severity: "high",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "MONEY-03",
|
|
27
|
+
domain: "money",
|
|
28
|
+
title: "Money maths has one implementation",
|
|
29
|
+
rule: "Rounding, splitting, conversion and commission each have exactly one implementation, used everywhere.",
|
|
30
|
+
why: "A second implementation will disagree with the first, a penny at a time.",
|
|
31
|
+
ask: "Does this change calculate money anywhere other than the one money module?",
|
|
32
|
+
stage: "prototype",
|
|
33
|
+
check: "ai-review",
|
|
34
|
+
severity: "high",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "MONEY-04",
|
|
38
|
+
domain: "money",
|
|
39
|
+
title: "Money is formatted in one place",
|
|
40
|
+
rule: "Every amount shown to a person goes through one formatting function.",
|
|
41
|
+
why: "Two formatters show the same amount two ways, and people stop trusting the numbers.",
|
|
42
|
+
ask: "Does this change format money anywhere other than the one formatter?",
|
|
43
|
+
stage: "prototype",
|
|
44
|
+
check: "auto",
|
|
45
|
+
severity: "medium",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "MONEY-05",
|
|
49
|
+
domain: "money",
|
|
50
|
+
title: "No money maths in the user interface",
|
|
51
|
+
rule: "Totals come from the server or from tested money code, never from arithmetic in a screen.",
|
|
52
|
+
why: "Maths in a screen is untested, and a screen that shows a different total from the receipt is a support call at best.",
|
|
53
|
+
ask: "Does any screen or component in this change do arithmetic on money?",
|
|
54
|
+
stage: "mvp",
|
|
55
|
+
check: "ai-review",
|
|
56
|
+
severity: "high",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "MONEY-06",
|
|
60
|
+
domain: "money",
|
|
61
|
+
title: "A money movement is never negative; its direction is a type",
|
|
62
|
+
rule: "A money movement's amount is always positive, and its direction is a named type: income, expense, transfer, payment, refund. A balance can be negative.",
|
|
63
|
+
why: "A minus sign is easy to lose or to double, and a lost sign sends money the wrong way.",
|
|
64
|
+
ask: "Does any money movement in this change carry its direction in its sign?",
|
|
65
|
+
stage: "mvp",
|
|
66
|
+
check: "auto",
|
|
67
|
+
severity: "high",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "MONEY-07",
|
|
71
|
+
domain: "money",
|
|
72
|
+
title: "The books balance",
|
|
73
|
+
rule: "Every ledger entry has its counterpart, and any change to how entries are posted comes with a test that proves the books still balance.",
|
|
74
|
+
why: "An unbalanced ledger means money has appeared or vanished, and finding where takes far longer than preventing it.",
|
|
75
|
+
ask: "If this change affects ledger entries, does a test prove the books still balance?",
|
|
76
|
+
stage: "mvp",
|
|
77
|
+
check: "ai-review",
|
|
78
|
+
severity: "critical",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "MONEY-08",
|
|
82
|
+
domain: "money",
|
|
83
|
+
title: "Anything that moves money is safe to repeat",
|
|
84
|
+
rule: "A request, job or webhook that moves money can run twice without moving it twice. It's keyed on something the caller supplies, never on timing, and every API that creates or moves money accepts an idempotency key.",
|
|
85
|
+
why: "Phones retry on patchy connections, people double-tap, and queues redeliver. Repeats are normal, not rare.",
|
|
86
|
+
ask: "What happens if this money operation runs twice?",
|
|
87
|
+
stage: "mvp",
|
|
88
|
+
check: "ai-review",
|
|
89
|
+
severity: "critical",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "MONEY-09",
|
|
93
|
+
domain: "money",
|
|
94
|
+
title: "A payment provider's webhook is verified before it does anything",
|
|
95
|
+
rule: "A webhook's signature is verified before it has any effect, and an event already seen is ignored.",
|
|
96
|
+
why: "Anyone can send a request that looks like a payment notification, and a replayed one can credit money twice.",
|
|
97
|
+
ask: "Is this webhook's signature verified before anything happens, and are replays ignored?",
|
|
98
|
+
stage: "mvp",
|
|
99
|
+
check: "ai-review",
|
|
100
|
+
severity: "critical",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "MONEY-10",
|
|
104
|
+
domain: "money",
|
|
105
|
+
title: "Every money path is tested for its amounts",
|
|
106
|
+
rule: "Every path that calculates or moves money has a test that checks the amounts, not just that the request succeeded.",
|
|
107
|
+
why: "A money bug that returns a success is the most expensive kind: it looks fine until the books are reconciled.",
|
|
108
|
+
ask: "Does every money path in this change have a test that checks the amounts?",
|
|
109
|
+
stage: "mvp",
|
|
110
|
+
check: "auto",
|
|
111
|
+
severity: "high",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "MONEY-11",
|
|
115
|
+
domain: "money",
|
|
116
|
+
title: "Money moving is visible",
|
|
117
|
+
rule: "Money movements emit a metric, so a person can see whether money is moving normally right now without querying the database by hand.",
|
|
118
|
+
why: "When payments quietly stop, every minute of not knowing costs real money and real trust.",
|
|
119
|
+
ask: "Would a sudden change in money movements show up on a dashboard or an alert?",
|
|
120
|
+
stage: "production",
|
|
121
|
+
check: "ai-review",
|
|
122
|
+
severity: "medium",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: "MONEY-12",
|
|
126
|
+
domain: "money",
|
|
127
|
+
title: "Full card numbers and security codes never touch your servers",
|
|
128
|
+
rule: "Card details go straight from the person to the payment provider, and your servers receive only the provider's token and, at most, the card's last four digits. A full card number or security code is never received, logged or stored.",
|
|
129
|
+
why: "Storing card data brings the whole system under PCI DSS, which forbids keeping the security code at all, and a leak of card numbers is one of the most damaging breaches a business can have.",
|
|
130
|
+
ask: "Could a full card number or security code reach your servers, logs or database through this change?",
|
|
131
|
+
stage: "prototype",
|
|
132
|
+
check: "ai-review",
|
|
133
|
+
severity: "critical",
|
|
134
|
+
},
|
|
135
|
+
];
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export declare const operations: ({
|
|
2
|
+
id: string;
|
|
3
|
+
domain: "operations";
|
|
4
|
+
title: string;
|
|
5
|
+
rule: string;
|
|
6
|
+
why: string;
|
|
7
|
+
ask: string;
|
|
8
|
+
stage: "mvp";
|
|
9
|
+
check: "ai-review";
|
|
10
|
+
severity: "high";
|
|
11
|
+
sources?: never;
|
|
12
|
+
} | {
|
|
13
|
+
id: string;
|
|
14
|
+
domain: "operations";
|
|
15
|
+
title: string;
|
|
16
|
+
rule: string;
|
|
17
|
+
why: string;
|
|
18
|
+
ask: string;
|
|
19
|
+
stage: "prototype";
|
|
20
|
+
check: "ai-review";
|
|
21
|
+
severity: "high";
|
|
22
|
+
sources?: never;
|
|
23
|
+
} | {
|
|
24
|
+
id: string;
|
|
25
|
+
domain: "operations";
|
|
26
|
+
title: string;
|
|
27
|
+
rule: string;
|
|
28
|
+
why: string;
|
|
29
|
+
ask: string;
|
|
30
|
+
stage: "production";
|
|
31
|
+
check: "person";
|
|
32
|
+
severity: "high";
|
|
33
|
+
sources?: never;
|
|
34
|
+
} | {
|
|
35
|
+
id: string;
|
|
36
|
+
domain: "operations";
|
|
37
|
+
title: string;
|
|
38
|
+
rule: string;
|
|
39
|
+
why: string;
|
|
40
|
+
ask: string;
|
|
41
|
+
stage: "production";
|
|
42
|
+
check: "ai-review";
|
|
43
|
+
severity: "medium";
|
|
44
|
+
sources?: never;
|
|
45
|
+
} | {
|
|
46
|
+
id: string;
|
|
47
|
+
domain: "operations";
|
|
48
|
+
title: string;
|
|
49
|
+
rule: string;
|
|
50
|
+
why: string;
|
|
51
|
+
ask: string;
|
|
52
|
+
stage: "production";
|
|
53
|
+
check: "person";
|
|
54
|
+
severity: "medium";
|
|
55
|
+
sources?: never;
|
|
56
|
+
} | {
|
|
57
|
+
id: string;
|
|
58
|
+
domain: "operations";
|
|
59
|
+
title: string;
|
|
60
|
+
rule: string;
|
|
61
|
+
why: string;
|
|
62
|
+
ask: string;
|
|
63
|
+
stage: "mvp";
|
|
64
|
+
check: "auto";
|
|
65
|
+
severity: "low";
|
|
66
|
+
sources?: never;
|
|
67
|
+
} | {
|
|
68
|
+
id: string;
|
|
69
|
+
domain: "operations";
|
|
70
|
+
title: string;
|
|
71
|
+
rule: string;
|
|
72
|
+
why: string;
|
|
73
|
+
ask: string;
|
|
74
|
+
stage: "mvp";
|
|
75
|
+
check: "ai-review";
|
|
76
|
+
severity: "medium";
|
|
77
|
+
sources?: never;
|
|
78
|
+
} | {
|
|
79
|
+
id: string;
|
|
80
|
+
domain: "operations";
|
|
81
|
+
title: string;
|
|
82
|
+
rule: string;
|
|
83
|
+
why: string;
|
|
84
|
+
ask: string;
|
|
85
|
+
stage: "mvp";
|
|
86
|
+
check: "ai-review";
|
|
87
|
+
severity: "medium";
|
|
88
|
+
sources: {
|
|
89
|
+
name: string;
|
|
90
|
+
ref: string;
|
|
91
|
+
url: string;
|
|
92
|
+
}[];
|
|
93
|
+
} | {
|
|
94
|
+
id: string;
|
|
95
|
+
domain: "operations";
|
|
96
|
+
title: string;
|
|
97
|
+
rule: string;
|
|
98
|
+
why: string;
|
|
99
|
+
ask: string;
|
|
100
|
+
stage: "production";
|
|
101
|
+
check: "ai-review";
|
|
102
|
+
severity: "medium";
|
|
103
|
+
sources: {
|
|
104
|
+
name: string;
|
|
105
|
+
ref: string;
|
|
106
|
+
url: string;
|
|
107
|
+
}[];
|
|
108
|
+
})[];
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Environments, access to production, backups, logs, metrics, alerts and incidents. Keeping
|
|
2
|
+
// environments apart and controlling access to production are what audits such as PCI DSS and
|
|
3
|
+
// ISO 27001 check; Peer AI grades them by stage so a prototype isn't held to production's bar.
|
|
4
|
+
const ASVS = "OWASP ASVS 5.0";
|
|
5
|
+
const V16 = "https://github.com/OWASP/ASVS/blob/master/5.0/en/0x25-V16-Security-Logging-and-Error-Handling.md";
|
|
6
|
+
export const operations = [
|
|
7
|
+
{
|
|
8
|
+
id: "OPS-01",
|
|
9
|
+
domain: "operations",
|
|
10
|
+
title: "Development and production are kept apart",
|
|
11
|
+
rule: "Development and test never share production's servers, databases or secrets: each environment has its own, so a mistake while building can't touch real customers.",
|
|
12
|
+
why: "A test run against the production database, or a development machine holding production's secrets, is how real data gets deleted or leaked. Audits such as PCI DSS and ISO 27001 check for this separation.",
|
|
13
|
+
ask: "Does anything in this change share a server, database or secret between development and production?",
|
|
14
|
+
stage: "mvp",
|
|
15
|
+
check: "ai-review",
|
|
16
|
+
severity: "high",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "OPS-02",
|
|
20
|
+
domain: "operations",
|
|
21
|
+
title: "Real people's data never goes into development or test",
|
|
22
|
+
rule: "Development and test use made-up data, or copies of production where personal data has been anonymised or replaced. A plain copy of production is never used for testing.",
|
|
23
|
+
why: "A copy of production puts real people's data somewhere less protected, where more people can see it. Data protection laws treat that as a breach waiting to happen, and PCI DSS forbids real card numbers in test outright.",
|
|
24
|
+
ask: "Does this change put real people's data into development or test?",
|
|
25
|
+
stage: "prototype",
|
|
26
|
+
check: "ai-review",
|
|
27
|
+
severity: "high",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "OPS-03",
|
|
31
|
+
domain: "operations",
|
|
32
|
+
title: "Nobody has everyday access to production",
|
|
33
|
+
rule: "Developers have no standing access to production. When someone truly needs it, such as during an outage, it's approved, time-limited and recorded.",
|
|
34
|
+
why: "Standing access means one stolen laptop or one mistyped command reaches production. Access that's approved and recorded can be explained afterwards.",
|
|
35
|
+
ask: "Who has access to production today, and is every use approved, time-limited and recorded?",
|
|
36
|
+
stage: "production",
|
|
37
|
+
check: "person",
|
|
38
|
+
severity: "high",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "OPS-04",
|
|
42
|
+
domain: "operations",
|
|
43
|
+
title: "A staging environment mirrors production",
|
|
44
|
+
rule: "A staging environment is set up like production, with anonymised data, and every change passes through it before it reaches production.",
|
|
45
|
+
why: '"It worked on my machine" is what staging exists to catch, before real customers find it instead.',
|
|
46
|
+
ask: "Does this change pass through a staging environment that mirrors production?",
|
|
47
|
+
stage: "production",
|
|
48
|
+
check: "ai-review",
|
|
49
|
+
severity: "medium",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "OPS-05",
|
|
53
|
+
domain: "operations",
|
|
54
|
+
title: "Backups are automatic",
|
|
55
|
+
rule: "Data is backed up automatically, at least daily, to somewhere separate from where it lives.",
|
|
56
|
+
why: "The backup you meant to set up is the one you need on the day the database is lost.",
|
|
57
|
+
ask: "Is the data in this change backed up automatically, somewhere separate?",
|
|
58
|
+
stage: "mvp",
|
|
59
|
+
check: "ai-review",
|
|
60
|
+
severity: "high",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "OPS-06",
|
|
64
|
+
domain: "operations",
|
|
65
|
+
title: "Restores are tested, and redundancy matches the uptime target",
|
|
66
|
+
rule: "Restoring from backup is tested on a schedule, and the system has enough redundancy to meet the uptime it promises.",
|
|
67
|
+
why: "A backup that has never been restored is a hope, not a backup.",
|
|
68
|
+
ask: "When was a restore from backup last tested, and does the redundancy match the uptime target?",
|
|
69
|
+
stage: "production",
|
|
70
|
+
check: "person",
|
|
71
|
+
severity: "medium",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "OPS-07",
|
|
75
|
+
domain: "operations",
|
|
76
|
+
title: "Logs are structured",
|
|
77
|
+
rule: "Logs are written as structured fields, not sentences built by pasting values together.",
|
|
78
|
+
why: "Structured logs can be searched and counted during an incident. Pasted-together text can't, and it's where secrets slip in.",
|
|
79
|
+
ask: "Does this change write any log line by pasting values into text?",
|
|
80
|
+
stage: "mvp",
|
|
81
|
+
check: "auto",
|
|
82
|
+
severity: "low",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "OPS-08",
|
|
86
|
+
domain: "operations",
|
|
87
|
+
title: "Every request can be traced end to end",
|
|
88
|
+
rule: "Each request carries one id, which appears in its log lines, its trace and the error the client sees.",
|
|
89
|
+
why: "When a customer reports an error, the id takes you straight to what happened instead of hours of searching.",
|
|
90
|
+
ask: "Can every request in this change be traced by one id from the client's error to the logs?",
|
|
91
|
+
stage: "production",
|
|
92
|
+
check: "ai-review",
|
|
93
|
+
severity: "medium",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "OPS-09",
|
|
97
|
+
domain: "operations",
|
|
98
|
+
title: "An alert fires only on something a person would act on",
|
|
99
|
+
rule: "Every alert is about something a person needs to act on, and says what to do.",
|
|
100
|
+
why: "Alerts that don't need action teach people to ignore alerts, including the one that matters.",
|
|
101
|
+
ask: "Does every alert in this change need a person to act?",
|
|
102
|
+
stage: "production",
|
|
103
|
+
check: "ai-review",
|
|
104
|
+
severity: "medium",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: "OPS-10",
|
|
108
|
+
domain: "operations",
|
|
109
|
+
title: "Health checks are cheap and honest",
|
|
110
|
+
rule: "Health and readiness checks are cheap to run and tell the truth: ready means able to serve requests, not just that the process started.",
|
|
111
|
+
why: "A readiness check that always says yes sends traffic to a server that can't handle it.",
|
|
112
|
+
ask: "Do the health checks in this change say whether the service can really serve requests?",
|
|
113
|
+
stage: "production",
|
|
114
|
+
check: "ai-review",
|
|
115
|
+
severity: "medium",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "OPS-11",
|
|
119
|
+
domain: "operations",
|
|
120
|
+
title: "There's a plan for a security incident",
|
|
121
|
+
rule: "A short written plan says what happens in a security incident: who leads, how an attacker's access is cut off, which secrets are replaced, how the system gets back to a known good state, and who tells affected people and regulators within the law's deadline. Afterwards, a review records what happened and what will stop it happening again.",
|
|
122
|
+
why: "Under pressure, people make worse decisions and lose hours working out who does what. Data protection laws such as the GDPR give as little as 72 hours to tell the regulator.",
|
|
123
|
+
ask: "Is there a written incident plan, and does this change add something it should cover, such as a new secret or a new store of personal data?",
|
|
124
|
+
stage: "mvp",
|
|
125
|
+
check: "ai-review",
|
|
126
|
+
severity: "medium",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "OPS-12",
|
|
130
|
+
domain: "operations",
|
|
131
|
+
title: "Security logs can't be changed by an attacker",
|
|
132
|
+
rule: "Security logs are sent to a separate system as they're written, where the app can add to them but can't change or delete them, and only the people who need them can read them.",
|
|
133
|
+
why: "Someone who breaks in often tries to erase their tracks next. Logs kept only on the server they broke into can't be trusted afterwards.",
|
|
134
|
+
ask: "Does this change keep security logs anywhere the app, or someone who breaks into it, could change or delete them?",
|
|
135
|
+
stage: "mvp",
|
|
136
|
+
check: "ai-review",
|
|
137
|
+
severity: "medium",
|
|
138
|
+
sources: [
|
|
139
|
+
{ name: ASVS, ref: "16.4.2, level 2", url: V16 },
|
|
140
|
+
{ name: ASVS, ref: "16.4.3, level 2", url: V16 },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: "OPS-13",
|
|
145
|
+
domain: "operations",
|
|
146
|
+
title: "Signs of attack raise an alert",
|
|
147
|
+
rule: "Security logs are watched for signs of attack, such as a burst of failed sign-ins, many refused permission checks for one account, repeated hits on a rate limit, or an admin action at an unusual time. Each raises an alert that reaches a person and says what to do.",
|
|
148
|
+
why: "Without anyone watching, an attack is found weeks later, often by someone outside. One caught in minutes does far less harm.",
|
|
149
|
+
ask: "Does this change add a security event that should raise an alert, and does it?",
|
|
150
|
+
stage: "production",
|
|
151
|
+
check: "ai-review",
|
|
152
|
+
severity: "medium",
|
|
153
|
+
sources: [{ name: ASVS, ref: "16.4.3, level 2", url: V16 }],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: "OPS-14",
|
|
157
|
+
domain: "operations",
|
|
158
|
+
title: "Every release can be undone",
|
|
159
|
+
rule: "Every release has a tested way back to the version before it, and its database changes still work with that version, so undoing the release doesn't need an emergency fix.",
|
|
160
|
+
why: "A release that can't be undone turns every bad deploy into an outage that lasts until someone writes a fix under pressure.",
|
|
161
|
+
ask: "If this release goes wrong, can it be undone, and will the previous version still run on the changed database?",
|
|
162
|
+
stage: "mvp",
|
|
163
|
+
check: "ai-review",
|
|
164
|
+
severity: "high",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "OPS-15",
|
|
168
|
+
domain: "operations",
|
|
169
|
+
title: "The product's service targets are written down and watched",
|
|
170
|
+
rule: "The product's targets for its main journeys, such as how often they work and how fast, are written down, measured in production, and raise an alert before they're missed.",
|
|
171
|
+
why: "Without a target, nobody can tell a slow week from an outage, or decide when reliability work should come before features.",
|
|
172
|
+
ask: "Does this change affect a main journey's target, and is that target still measured and alerted on?",
|
|
173
|
+
stage: "production",
|
|
174
|
+
check: "ai-review",
|
|
175
|
+
severity: "medium",
|
|
176
|
+
},
|
|
177
|
+
];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export declare const performance: ({
|
|
2
|
+
id: string;
|
|
3
|
+
domain: "performance";
|
|
4
|
+
title: string;
|
|
5
|
+
rule: string;
|
|
6
|
+
why: string;
|
|
7
|
+
ask: string;
|
|
8
|
+
stage: "mvp";
|
|
9
|
+
check: "ai-review";
|
|
10
|
+
severity: "medium";
|
|
11
|
+
sources?: never;
|
|
12
|
+
} | {
|
|
13
|
+
id: string;
|
|
14
|
+
domain: "performance";
|
|
15
|
+
title: string;
|
|
16
|
+
rule: string;
|
|
17
|
+
why: string;
|
|
18
|
+
ask: string;
|
|
19
|
+
stage: "production";
|
|
20
|
+
check: "ai-review";
|
|
21
|
+
severity: "medium";
|
|
22
|
+
sources?: never;
|
|
23
|
+
} | {
|
|
24
|
+
id: string;
|
|
25
|
+
domain: "performance";
|
|
26
|
+
title: string;
|
|
27
|
+
rule: string;
|
|
28
|
+
why: string;
|
|
29
|
+
ask: string;
|
|
30
|
+
stage: "production";
|
|
31
|
+
check: "ai-review";
|
|
32
|
+
severity: "high";
|
|
33
|
+
sources: {
|
|
34
|
+
name: string;
|
|
35
|
+
ref: string;
|
|
36
|
+
url: string;
|
|
37
|
+
}[];
|
|
38
|
+
} | {
|
|
39
|
+
id: string;
|
|
40
|
+
domain: "performance";
|
|
41
|
+
title: string;
|
|
42
|
+
rule: string;
|
|
43
|
+
why: string;
|
|
44
|
+
ask: string;
|
|
45
|
+
stage: "production";
|
|
46
|
+
check: "ai-review";
|
|
47
|
+
severity: "medium";
|
|
48
|
+
sources: {
|
|
49
|
+
name: string;
|
|
50
|
+
ref: string;
|
|
51
|
+
url: string;
|
|
52
|
+
}[];
|
|
53
|
+
})[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Knowing what things cost, and caching only what's safe to cache.
|
|
2
|
+
const ASVS = "OWASP ASVS 5.0";
|
|
3
|
+
const V2 = "https://github.com/OWASP/ASVS/blob/master/5.0/en/0x11-V2-Validation-and-Business-Logic.md";
|
|
4
|
+
const V14 = "https://github.com/OWASP/ASVS/blob/master/5.0/en/0x23-V14-Data-Protection.md";
|
|
5
|
+
export const performance = [
|
|
6
|
+
{
|
|
7
|
+
id: "PERF-01",
|
|
8
|
+
domain: "performance",
|
|
9
|
+
title: "No list runs one query per row",
|
|
10
|
+
rule: "A list loads its related data in a fixed number of queries, never one extra query for each row.",
|
|
11
|
+
why: "One query per row passes every test on sample data and falls over on real data.",
|
|
12
|
+
ask: "Does any list in this change run a query for each of its rows?",
|
|
13
|
+
stage: "mvp",
|
|
14
|
+
check: "ai-review",
|
|
15
|
+
severity: "medium",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "PERF-02",
|
|
19
|
+
domain: "performance",
|
|
20
|
+
title: "A query's cost is known before it ships",
|
|
21
|
+
rule: "Before a query ships, someone knows what it costs: filters and sorts use indexes, joins are few, and maths done per request on large data is watched.",
|
|
22
|
+
why: "A slow query is invisible with little data and becomes an outage as the data grows.",
|
|
23
|
+
ask: "Is every new query in this change backed by the indexes it needs?",
|
|
24
|
+
stage: "production",
|
|
25
|
+
check: "ai-review",
|
|
26
|
+
severity: "medium",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "PERF-03",
|
|
30
|
+
domain: "performance",
|
|
31
|
+
title: "Cache only what's safe to be wrong about",
|
|
32
|
+
rule: "Cache only what's expensive to work out and cheap to get wrong. Never cache a decision about money, safety or permissions, and never cache one person's data where another could be served it.",
|
|
33
|
+
why: "A stale or shared cache entry can show one person another's data, or approve what's no longer allowed.",
|
|
34
|
+
ask: "Does anything in this change cache a sensitive decision, or one person's data where another could see it?",
|
|
35
|
+
stage: "production",
|
|
36
|
+
check: "ai-review",
|
|
37
|
+
severity: "high",
|
|
38
|
+
sources: [{ name: ASVS, ref: "14.2.2, level 2", url: V14 }],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "PERF-04",
|
|
42
|
+
domain: "performance",
|
|
43
|
+
title: "Expensive endpoints are rate limited too",
|
|
44
|
+
rule: "Endpoints that are cheap to call but expensive to serve, such as searches and exports, are rate limited, not just sign-in.",
|
|
45
|
+
why: "Cheap to ask for and expensive to answer is the shape of a denial-of-service attack.",
|
|
46
|
+
ask: "Is every expensive endpoint in this change rate limited?",
|
|
47
|
+
stage: "production",
|
|
48
|
+
check: "ai-review",
|
|
49
|
+
severity: "medium",
|
|
50
|
+
sources: [{ name: ASVS, ref: "2.4.1, level 2", url: V2 }],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "PERF-05",
|
|
54
|
+
domain: "performance",
|
|
55
|
+
title: "Long lists draw only what's on screen",
|
|
56
|
+
rule: "A list that can grow long draws only the items on screen, loading and releasing the rest as the person scrolls.",
|
|
57
|
+
why: "Drawing thousands of items at once freezes a phone and drains its battery.",
|
|
58
|
+
ask: "Does any list in this change draw every item at once when it could grow long?",
|
|
59
|
+
stage: "mvp",
|
|
60
|
+
check: "ai-review",
|
|
61
|
+
severity: "medium",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "PERF-06",
|
|
65
|
+
domain: "performance",
|
|
66
|
+
title: "What's started is stopped",
|
|
67
|
+
rule: "Timers, subscriptions, listeners and object URLs are released when the screen or component that created them goes away.",
|
|
68
|
+
why: "Anything left running keeps using memory, battery and network, and often keeps acting on a screen nobody can see.",
|
|
69
|
+
ask: "Does everything this change starts get stopped or released when it's no longer needed?",
|
|
70
|
+
stage: "mvp",
|
|
71
|
+
check: "ai-review",
|
|
72
|
+
severity: "medium",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "PERF-07",
|
|
76
|
+
domain: "performance",
|
|
77
|
+
title: "Images are resized before they're stored or sent",
|
|
78
|
+
rule: "Photos and large images are resized to what the product actually shows before they're stored or uploaded.",
|
|
79
|
+
why: "A full-size camera photo is often several megabytes, and a few hundred of them fill a device's storage or a person's data plan.",
|
|
80
|
+
ask: "Does this change store or send images larger than the product shows?",
|
|
81
|
+
stage: "mvp",
|
|
82
|
+
check: "ai-review",
|
|
83
|
+
severity: "medium",
|
|
84
|
+
},
|
|
85
|
+
];
|