redline-review 1.0.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/README.md +239 -0
- package/adapters/antigravity/redline-review.md +32 -0
- package/adapters/claude/redline-review.md +41 -0
- package/adapters/codex/redline-review.md +38 -0
- package/adapters/copilot/redline-review.md +38 -0
- package/adapters/opencode/redline-review.md +34 -0
- package/bin/redline-review +4 -0
- package/dist/build-context.d.ts +6 -0
- package/dist/build-context.d.ts.map +1 -0
- package/dist/build-context.js +47 -0
- package/dist/build-context.js.map +1 -0
- package/dist/detect-domains.d.ts +2 -0
- package/dist/detect-domains.d.ts.map +1 -0
- package/dist/detect-domains.js +66 -0
- package/dist/detect-domains.js.map +1 -0
- package/dist/redline-review.d.ts +3 -0
- package/dist/redline-review.d.ts.map +1 -0
- package/dist/redline-review.js +171 -0
- package/dist/redline-review.js.map +1 -0
- package/package.json +54 -0
- package/prompts/base-reviewer.md +28 -0
- package/prompts/lightweight-reviewer.md +20 -0
- package/prompts/strict-reviewer.md +24 -0
- package/rules/architecture.yaml +55 -0
- package/rules/auth.yaml +57 -0
- package/rules/concurrency.yaml +62 -0
- package/rules/correctness.yaml +108 -0
- package/rules/frontend.yaml +46 -0
- package/rules/maintainability.yaml +42 -0
- package/rules/observability.yaml +59 -0
- package/rules/performance-algorithmic.yaml +49 -0
- package/rules/performance-db.yaml +44 -0
- package/rules/performance-system.yaml +37 -0
- package/rules/risk-patterns.yaml +58 -0
- package/rules/simplicity.yaml +85 -0
- package/schemas/rule.schema.json +52 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const fs_1 = __importDefault(require("fs"));
|
|
41
|
+
const path_1 = __importDefault(require("path"));
|
|
42
|
+
const yaml = __importStar(require("js-yaml"));
|
|
43
|
+
const child_process_1 = require("child_process");
|
|
44
|
+
const build_context_1 = require("./build-context");
|
|
45
|
+
const detect_domains_1 = require("./detect-domains");
|
|
46
|
+
function parseArgs() {
|
|
47
|
+
const args = process.argv.slice(2);
|
|
48
|
+
const get = (flag) => {
|
|
49
|
+
const idx = args.indexOf(flag);
|
|
50
|
+
return idx !== -1 ? args[idx + 1] : undefined;
|
|
51
|
+
};
|
|
52
|
+
const stack = get('--stack')?.split(',').filter(Boolean);
|
|
53
|
+
const reviewType = get('--type')?.split(',').filter(Boolean);
|
|
54
|
+
const promptVariant = get('--prompt') ?? 'base';
|
|
55
|
+
const base = get('--base');
|
|
56
|
+
return { stack, reviewType, promptVariant, base };
|
|
57
|
+
}
|
|
58
|
+
function detectBaseBranch() {
|
|
59
|
+
try {
|
|
60
|
+
return (0, child_process_1.execSync)('git rev-parse --abbrev-ref @{upstream}', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
61
|
+
}
|
|
62
|
+
catch { /* no upstream configured */ }
|
|
63
|
+
try {
|
|
64
|
+
const ref = (0, child_process_1.execSync)('git symbolic-ref refs/remotes/origin/HEAD', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
65
|
+
return ref.replace('refs/remotes/', '');
|
|
66
|
+
}
|
|
67
|
+
catch { /* no remote HEAD ref */ }
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function getGitDiff(baseOverride) {
|
|
71
|
+
const baseCandidates = baseOverride
|
|
72
|
+
? [baseOverride]
|
|
73
|
+
: [detectBaseBranch(), 'main', 'master', 'develop', 'trunk'].filter(Boolean);
|
|
74
|
+
for (const base of baseCandidates) {
|
|
75
|
+
try {
|
|
76
|
+
const result = (0, child_process_1.execSync)(`git diff ${base}...HEAD`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
77
|
+
if (result.length > 0) {
|
|
78
|
+
process.stderr.write(`Base branch: ${base}\n`);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const cmd of ['git diff HEAD~1', 'git diff --cached']) {
|
|
87
|
+
try {
|
|
88
|
+
const result = (0, child_process_1.execSync)(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
89
|
+
if (result.length > 0) {
|
|
90
|
+
process.stderr.write(`Warning: could not find a base branch; falling back to \`${cmd}\`\n`);
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
process.stderr.write('No diff found. Make sure you are in a git repository with changes.\n');
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
function loadRules(rulesDir, selectedFiles) {
|
|
102
|
+
const files = fs_1.default.readdirSync(rulesDir)
|
|
103
|
+
.filter(f => f.endsWith('.yaml'))
|
|
104
|
+
.filter(f => !selectedFiles || selectedFiles.includes(f));
|
|
105
|
+
return files.map(file => {
|
|
106
|
+
const content = fs_1.default.readFileSync(path_1.default.join(rulesDir, file), 'utf8');
|
|
107
|
+
return yaml.load(content);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function formatRulesForPrompt(ruleFiles) {
|
|
111
|
+
return ruleFiles.map(rf => {
|
|
112
|
+
const header = `### ${rf.category.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())}`;
|
|
113
|
+
const context = rf.severity_context ? `\n*${rf.severity_context.trim()}*\n` : '';
|
|
114
|
+
const rules = rf.rules.map(r => {
|
|
115
|
+
const examples = r.examples && r.examples.length > 0
|
|
116
|
+
? `\n Example:\n${r.examples[0].split('\n').map(l => ` ${l}`).join('\n')}`
|
|
117
|
+
: '';
|
|
118
|
+
return `- [${r.severity.toUpperCase()}] ${r.detect}${examples}`;
|
|
119
|
+
}).join('\n');
|
|
120
|
+
return `${header}${context}\n${rules}`;
|
|
121
|
+
}).join('\n\n');
|
|
122
|
+
}
|
|
123
|
+
function buildPrompt(rulesText, diff, promptTemplate) {
|
|
124
|
+
return promptTemplate
|
|
125
|
+
.replace('{{selectedRules}}', rulesText)
|
|
126
|
+
.replace('{{gitDiff}}', diff);
|
|
127
|
+
}
|
|
128
|
+
function resolvePromptPath(variant, promptsDir) {
|
|
129
|
+
const map = {
|
|
130
|
+
base: 'base-reviewer.md',
|
|
131
|
+
strict: 'strict-reviewer.md',
|
|
132
|
+
lightweight: 'lightweight-reviewer.md',
|
|
133
|
+
};
|
|
134
|
+
const file = map[variant] ?? 'base-reviewer.md';
|
|
135
|
+
return path_1.default.join(promptsDir, file);
|
|
136
|
+
}
|
|
137
|
+
function main() {
|
|
138
|
+
process.stdout.on('error', (err) => {
|
|
139
|
+
if (err.code === 'EPIPE')
|
|
140
|
+
process.exit(0);
|
|
141
|
+
});
|
|
142
|
+
const { stack, reviewType, promptVariant, base } = parseArgs();
|
|
143
|
+
const rulesDir = path_1.default.join(__dirname, '..', 'rules');
|
|
144
|
+
const promptsDir = path_1.default.join(__dirname, '..', 'prompts');
|
|
145
|
+
const diff = getGitDiff(base);
|
|
146
|
+
let selectedFiles;
|
|
147
|
+
let contextSource;
|
|
148
|
+
if (reviewType || stack) {
|
|
149
|
+
selectedFiles = (0, build_context_1.buildReviewContext)({ stack, reviewType });
|
|
150
|
+
if (selectedFiles.length === 0) {
|
|
151
|
+
process.stderr.write('Warning: no matching rules for the given --stack/--type. Falling back to all rules.\n');
|
|
152
|
+
selectedFiles = undefined;
|
|
153
|
+
}
|
|
154
|
+
contextSource = 'explicit';
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
selectedFiles = (0, detect_domains_1.detectRelevantDomains)(diff);
|
|
158
|
+
contextSource = 'auto-detected';
|
|
159
|
+
}
|
|
160
|
+
const ruleFiles = loadRules(rulesDir, selectedFiles?.length ? selectedFiles : undefined);
|
|
161
|
+
const promptPath = resolvePromptPath(promptVariant, promptsDir);
|
|
162
|
+
const promptTemplate = fs_1.default.readFileSync(promptPath, 'utf8');
|
|
163
|
+
const rulesText = formatRulesForPrompt(ruleFiles);
|
|
164
|
+
const finalPrompt = buildPrompt(rulesText, diff, promptTemplate);
|
|
165
|
+
if (selectedFiles?.length) {
|
|
166
|
+
process.stderr.write(`Domains (${contextSource}): ${selectedFiles.join(', ')}\n`);
|
|
167
|
+
}
|
|
168
|
+
process.stdout.write(finalPrompt + '\n');
|
|
169
|
+
}
|
|
170
|
+
main();
|
|
171
|
+
//# sourceMappingURL=redline-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redline-review.js","sourceRoot":"","sources":["../src/redline-review.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "redline-review",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Adaptive AI code review skill for any frontier agent — context-aware, rule-driven, diff-native",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"code-review",
|
|
7
|
+
"ai",
|
|
8
|
+
"claude",
|
|
9
|
+
"opencode",
|
|
10
|
+
"copilot",
|
|
11
|
+
"codex",
|
|
12
|
+
"llm",
|
|
13
|
+
"developer-tools",
|
|
14
|
+
"git",
|
|
15
|
+
"prompt",
|
|
16
|
+
"review",
|
|
17
|
+
"static-analysis"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/nabil1440/redline-review.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/nabil1440/redline-review#readme",
|
|
25
|
+
"bin": {
|
|
26
|
+
"redline-review": "bin/redline-review"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"bin",
|
|
30
|
+
"dist",
|
|
31
|
+
"rules",
|
|
32
|
+
"prompts",
|
|
33
|
+
"schemas",
|
|
34
|
+
"adapters",
|
|
35
|
+
"README.md"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"dev": "ts-node src/redline-review.ts",
|
|
40
|
+
"prepare": "npm run build"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"js-yaml": "^4.1.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/js-yaml": "^4.0.9",
|
|
47
|
+
"@types/node": "^22.0.0",
|
|
48
|
+
"ts-node": "^10.9.2",
|
|
49
|
+
"typescript": "^5.7.0"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
You are an expert code reviewer with deep expertise in software architecture, security, performance, and correctness.
|
|
2
|
+
|
|
3
|
+
## Review Focus Areas
|
|
4
|
+
|
|
5
|
+
{{selectedRules}}
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
Review the diff below and identify issues based on the focus areas above.
|
|
10
|
+
|
|
11
|
+
For each issue found:
|
|
12
|
+
- **Location**: file name and line number (if determinable from the diff)
|
|
13
|
+
- **Rule**: which rule category and rule ID applies
|
|
14
|
+
- **Severity**: CRITICAL / HIGH / MEDIUM / LOW
|
|
15
|
+
- **Issue**: a concise explanation of the problem
|
|
16
|
+
- **Suggestion**: a concrete recommendation to fix it
|
|
17
|
+
|
|
18
|
+
Group issues by severity descending: CRITICAL → HIGH → MEDIUM → LOW.
|
|
19
|
+
|
|
20
|
+
If no issues are found in a category, skip it. If the diff is clean, say so.
|
|
21
|
+
|
|
22
|
+
Keep explanations tight — one or two sentences per issue. No filler.
|
|
23
|
+
|
|
24
|
+
## Diff
|
|
25
|
+
|
|
26
|
+
```diff
|
|
27
|
+
{{gitDiff}}
|
|
28
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
You are an expert code reviewer doing a quick scan.
|
|
2
|
+
|
|
3
|
+
## Review Focus Areas
|
|
4
|
+
|
|
5
|
+
{{selectedRules}}
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
Review the diff below. Return the **top 3 most important issues only**, regardless of category.
|
|
10
|
+
|
|
11
|
+
Format each as a single tight paragraph:
|
|
12
|
+
`[SEVERITY] file:line — issue description. Suggested fix.`
|
|
13
|
+
|
|
14
|
+
If the diff looks clean, say so in one sentence.
|
|
15
|
+
|
|
16
|
+
## Diff
|
|
17
|
+
|
|
18
|
+
```diff
|
|
19
|
+
{{gitDiff}}
|
|
20
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
You are an expert code reviewer performing a strict security and correctness pass.
|
|
2
|
+
|
|
3
|
+
## Review Focus Areas
|
|
4
|
+
|
|
5
|
+
{{selectedRules}}
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
Review the diff below. Report **only CRITICAL and HIGH severity issues** — skip MEDIUM and LOW entirely.
|
|
10
|
+
|
|
11
|
+
For each issue:
|
|
12
|
+
- **Location**: file name and line number
|
|
13
|
+
- **Rule**: category and rule ID
|
|
14
|
+
- **Severity**: CRITICAL or HIGH
|
|
15
|
+
- **Issue**: one sentence describing the problem
|
|
16
|
+
- **Fix**: a concrete code-level suggestion
|
|
17
|
+
|
|
18
|
+
Be direct. No filler. If there are no CRITICAL or HIGH issues, say: "No critical or high severity issues found."
|
|
19
|
+
|
|
20
|
+
## Diff
|
|
21
|
+
|
|
22
|
+
```diff
|
|
23
|
+
{{gitDiff}}
|
|
24
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
category: architecture_design
|
|
2
|
+
severity_context: >
|
|
3
|
+
Prioritize architectural patterns that increase coupling, centralize instability,
|
|
4
|
+
or create long-term maintenance bottlenecks. Optimize for maintainability,
|
|
5
|
+
scalability, and low coupling.
|
|
6
|
+
|
|
7
|
+
rules:
|
|
8
|
+
- id: god_class
|
|
9
|
+
severity: high
|
|
10
|
+
detect: God classes, services, or components with too many responsibilities
|
|
11
|
+
examples:
|
|
12
|
+
- |
|
|
13
|
+
UserManager handles auth, billing, notifications, and reporting
|
|
14
|
+
|
|
15
|
+
- id: responsibility_leakage
|
|
16
|
+
severity: high
|
|
17
|
+
detect: responsibility leakage between layers or modules
|
|
18
|
+
examples:
|
|
19
|
+
- |
|
|
20
|
+
Controller directly queries the database
|
|
21
|
+
|
|
22
|
+
- id: separation_of_concerns
|
|
23
|
+
severity: medium
|
|
24
|
+
detect: violations of separation of concerns
|
|
25
|
+
examples:
|
|
26
|
+
- |
|
|
27
|
+
Business logic inside a route handler
|
|
28
|
+
|
|
29
|
+
- id: low_cohesion
|
|
30
|
+
severity: medium
|
|
31
|
+
detect: modules or classes with low cohesion
|
|
32
|
+
examples:
|
|
33
|
+
- |
|
|
34
|
+
Utility class mixing string helpers, date parsing, and HTTP calls
|
|
35
|
+
|
|
36
|
+
- id: unnecessary_abstractions
|
|
37
|
+
severity: medium
|
|
38
|
+
detect: unnecessary or premature abstractions that add complexity without value
|
|
39
|
+
examples:
|
|
40
|
+
- |
|
|
41
|
+
AbstractBaseServiceFactoryInterface
|
|
42
|
+
|
|
43
|
+
- id: framework_convention_deviation
|
|
44
|
+
severity: medium
|
|
45
|
+
detect: deviations from established framework conventions without justification
|
|
46
|
+
examples:
|
|
47
|
+
- |
|
|
48
|
+
Custom routing that bypasses framework middleware
|
|
49
|
+
|
|
50
|
+
- id: unclear_architectural_boundaries
|
|
51
|
+
severity: high
|
|
52
|
+
detect: unclear or violated architectural boundaries between layers or domains
|
|
53
|
+
examples:
|
|
54
|
+
- |
|
|
55
|
+
Domain model importing from infrastructure layer
|
package/rules/auth.yaml
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
category: authentication_authorization
|
|
2
|
+
severity_context: >
|
|
3
|
+
Prioritize vulnerabilities that can enable unauthorized access, privilege escalation,
|
|
4
|
+
or exposure of sensitive data. Review all sensitive actions assuming hostile access
|
|
5
|
+
patterns and privilege escalation attempts.
|
|
6
|
+
|
|
7
|
+
rules:
|
|
8
|
+
- id: missing_authentication_checks
|
|
9
|
+
severity: critical
|
|
10
|
+
detect: missing authentication checks on sensitive operations
|
|
11
|
+
examples:
|
|
12
|
+
- |
|
|
13
|
+
updateUserProfile(userId)
|
|
14
|
+
|
|
15
|
+
- id: missing_authorization_checks
|
|
16
|
+
severity: critical
|
|
17
|
+
detect: missing authorization checks on sensitive operations
|
|
18
|
+
examples:
|
|
19
|
+
- |
|
|
20
|
+
adminDeletePost(postId)
|
|
21
|
+
|
|
22
|
+
- id: role_permission_bypass
|
|
23
|
+
severity: critical
|
|
24
|
+
detect: role or permission bypass opportunities through insufficient guard logic
|
|
25
|
+
examples:
|
|
26
|
+
- |
|
|
27
|
+
if isLoggedIn:
|
|
28
|
+
allowDelete()
|
|
29
|
+
|
|
30
|
+
- id: idor_patterns
|
|
31
|
+
severity: critical
|
|
32
|
+
detect: insecure direct object reference patterns where ownership is not validated
|
|
33
|
+
examples:
|
|
34
|
+
- |
|
|
35
|
+
GET /invoice/123
|
|
36
|
+
|
|
37
|
+
- id: hardcoded_roles_permissions
|
|
38
|
+
severity: high
|
|
39
|
+
detect: hardcoded roles or permission strings
|
|
40
|
+
examples:
|
|
41
|
+
- |
|
|
42
|
+
if role == 'admin'
|
|
43
|
+
|
|
44
|
+
- id: inconsistent_permission_enforcement
|
|
45
|
+
severity: high
|
|
46
|
+
detect: inconsistent permission enforcement across API routes and background jobs
|
|
47
|
+
examples:
|
|
48
|
+
- |
|
|
49
|
+
API route protected
|
|
50
|
+
background job unprotected
|
|
51
|
+
|
|
52
|
+
- id: missing_ownership_validation
|
|
53
|
+
severity: critical
|
|
54
|
+
detect: sensitive operations missing ownership or tenancy validation
|
|
55
|
+
examples:
|
|
56
|
+
- |
|
|
57
|
+
updateOrder(orderId)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
category: concurrency_consistency
|
|
2
|
+
severity_context: >
|
|
3
|
+
Prioritize concurrency and consistency issues that can create corrupted state,
|
|
4
|
+
duplicate execution, financial inconsistencies, or distributed coordination failures.
|
|
5
|
+
Review concurrent and distributed flows assuming retries, duplication, delays,
|
|
6
|
+
and parallel execution.
|
|
7
|
+
|
|
8
|
+
rules:
|
|
9
|
+
- id: race_condition
|
|
10
|
+
severity: critical
|
|
11
|
+
detect: race condition opportunities from unguarded read-modify-write patterns
|
|
12
|
+
examples:
|
|
13
|
+
- |
|
|
14
|
+
read balance
|
|
15
|
+
update balance
|
|
16
|
+
save balance
|
|
17
|
+
|
|
18
|
+
- id: missing_transaction_boundaries
|
|
19
|
+
severity: critical
|
|
20
|
+
detect: multi-step operations missing transaction boundaries
|
|
21
|
+
examples:
|
|
22
|
+
- |
|
|
23
|
+
createOrder()
|
|
24
|
+
chargeCard()
|
|
25
|
+
updateInventory()
|
|
26
|
+
|
|
27
|
+
- id: non_idempotent_retry
|
|
28
|
+
severity: critical
|
|
29
|
+
detect: non-idempotent operations in retry flows
|
|
30
|
+
examples:
|
|
31
|
+
- |
|
|
32
|
+
retry payment()
|
|
33
|
+
|
|
34
|
+
- id: missing_distributed_lock
|
|
35
|
+
severity: high
|
|
36
|
+
detect: scenarios requiring distributed locks that have none
|
|
37
|
+
examples:
|
|
38
|
+
- |
|
|
39
|
+
Multiple workers processing the same job without coordination
|
|
40
|
+
|
|
41
|
+
- id: shared_mutable_state
|
|
42
|
+
severity: high
|
|
43
|
+
detect: shared mutable state accessed without synchronization
|
|
44
|
+
examples:
|
|
45
|
+
- |
|
|
46
|
+
globalCache.value += 1
|
|
47
|
+
|
|
48
|
+
- id: eventual_consistency_hazard
|
|
49
|
+
severity: high
|
|
50
|
+
detect: code that assumes read-after-write consistency on eventually consistent stores
|
|
51
|
+
examples:
|
|
52
|
+
- |
|
|
53
|
+
write database
|
|
54
|
+
immediately read replica
|
|
55
|
+
|
|
56
|
+
- id: unsafe_async_ordering
|
|
57
|
+
severity: high
|
|
58
|
+
detect: unsafe assumptions about async operation ordering
|
|
59
|
+
examples:
|
|
60
|
+
- |
|
|
61
|
+
sendEmail()
|
|
62
|
+
assume user saved
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
category: correctness_safety
|
|
2
|
+
severity_context: >
|
|
3
|
+
Prioritize correctness issues that can cause silent failures, corrupted state,
|
|
4
|
+
unpredictable behavior, or production instability.
|
|
5
|
+
|
|
6
|
+
rules:
|
|
7
|
+
- id: swallowed_exceptions
|
|
8
|
+
severity: high
|
|
9
|
+
detect: swallowed or silently caught exceptions
|
|
10
|
+
examples:
|
|
11
|
+
- |
|
|
12
|
+
try:
|
|
13
|
+
riskyOperation()
|
|
14
|
+
catch:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
- id: hidden_side_effects
|
|
18
|
+
severity: high
|
|
19
|
+
detect: functions with hidden side effects not implied by their name or signature
|
|
20
|
+
examples:
|
|
21
|
+
- |
|
|
22
|
+
calculateTotal()
|
|
23
|
+
-> updates database
|
|
24
|
+
|
|
25
|
+
- id: ambiguous_failure_states
|
|
26
|
+
severity: critical
|
|
27
|
+
detect: ambiguous or silent failure states returned as null or empty
|
|
28
|
+
examples:
|
|
29
|
+
- |
|
|
30
|
+
return null
|
|
31
|
+
|
|
32
|
+
- id: unhandled_custom_exceptions
|
|
33
|
+
severity: high
|
|
34
|
+
detect: custom exceptions raised but never caught or documented
|
|
35
|
+
examples:
|
|
36
|
+
- |
|
|
37
|
+
raise PaymentError
|
|
38
|
+
|
|
39
|
+
- id: unreachable_branches
|
|
40
|
+
severity: medium
|
|
41
|
+
detect: unreachable code after return, throw, or break statements
|
|
42
|
+
examples:
|
|
43
|
+
- |
|
|
44
|
+
return result
|
|
45
|
+
log('done')
|
|
46
|
+
|
|
47
|
+
- id: dead_code
|
|
48
|
+
severity: medium
|
|
49
|
+
detect: dead code, unused variables, or unused helper functions
|
|
50
|
+
examples:
|
|
51
|
+
- |
|
|
52
|
+
unusedHelper()
|
|
53
|
+
unusedVariable
|
|
54
|
+
|
|
55
|
+
- id: stale_abstractions
|
|
56
|
+
severity: medium
|
|
57
|
+
detect: stale or legacy abstractions still in active code paths
|
|
58
|
+
examples:
|
|
59
|
+
- |
|
|
60
|
+
LegacyUserAdapter
|
|
61
|
+
OldPaymentService
|
|
62
|
+
|
|
63
|
+
- id: vague_error_messages
|
|
64
|
+
severity: medium
|
|
65
|
+
detect: vague or non-actionable error messages
|
|
66
|
+
examples:
|
|
67
|
+
- |
|
|
68
|
+
"Something went wrong"
|
|
69
|
+
"Operation failed"
|
|
70
|
+
|
|
71
|
+
- id: missing_operational_context_in_errors
|
|
72
|
+
severity: medium
|
|
73
|
+
detect: error messages missing operational context needed for diagnosis
|
|
74
|
+
examples:
|
|
75
|
+
- |
|
|
76
|
+
"Failed to process request"
|
|
77
|
+
|
|
78
|
+
- id: inconsistent_error_quality
|
|
79
|
+
severity: low
|
|
80
|
+
detect: inconsistent error message quality across the codebase
|
|
81
|
+
examples:
|
|
82
|
+
- |
|
|
83
|
+
"Invalid input"
|
|
84
|
+
vs
|
|
85
|
+
"Email format is invalid"
|
|
86
|
+
|
|
87
|
+
- id: leaked_implementation_details_in_errors
|
|
88
|
+
severity: high
|
|
89
|
+
detect: leaked internal implementation details exposed in error messages
|
|
90
|
+
examples:
|
|
91
|
+
- |
|
|
92
|
+
SQLSTATE[23505]
|
|
93
|
+
NullReferenceException at line 842
|
|
94
|
+
|
|
95
|
+
- id: user_hostile_error_wording
|
|
96
|
+
severity: medium
|
|
97
|
+
detect: user-hostile or unhelpful error wording
|
|
98
|
+
examples:
|
|
99
|
+
- |
|
|
100
|
+
"Bad request"
|
|
101
|
+
"Invalid"
|
|
102
|
+
|
|
103
|
+
- id: missing_remediation_guidance
|
|
104
|
+
severity: medium
|
|
105
|
+
detect: recoverable errors missing remediation guidance for the user
|
|
106
|
+
examples:
|
|
107
|
+
- |
|
|
108
|
+
"Upload failed"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
category: frontend_heuristics
|
|
2
|
+
severity_context: >
|
|
3
|
+
Frontend complexity compounds fast. Keep state and rendering flows isolated.
|
|
4
|
+
|
|
5
|
+
rules:
|
|
6
|
+
- id: god_component
|
|
7
|
+
severity: high
|
|
8
|
+
detect: God frontend components handling too many concerns in one place
|
|
9
|
+
examples:
|
|
10
|
+
- |
|
|
11
|
+
UserDashboard manages auth state, data fetching, form handling, and routing
|
|
12
|
+
|
|
13
|
+
- id: duplicated_state
|
|
14
|
+
severity: medium
|
|
15
|
+
detect: duplicated state across multiple components that should be shared or derived
|
|
16
|
+
examples:
|
|
17
|
+
- |
|
|
18
|
+
const [user, setUser] = useState() in three sibling components
|
|
19
|
+
|
|
20
|
+
- id: unnecessary_derived_state
|
|
21
|
+
severity: medium
|
|
22
|
+
detect: unnecessary derived state stored in useState instead of computed inline
|
|
23
|
+
examples:
|
|
24
|
+
- |
|
|
25
|
+
const [fullName, setFullName] = useState(firstName + ' ' + lastName)
|
|
26
|
+
|
|
27
|
+
- id: excessive_prop_drilling
|
|
28
|
+
severity: medium
|
|
29
|
+
detect: excessive prop drilling through multiple component layers
|
|
30
|
+
examples:
|
|
31
|
+
- |
|
|
32
|
+
<A user={user}><B user={user}><C user={user} /></B></A>
|
|
33
|
+
|
|
34
|
+
- id: unnecessary_third_party_tooling
|
|
35
|
+
severity: medium
|
|
36
|
+
detect: unnecessary third-party library usage where a native or framework solution exists
|
|
37
|
+
examples:
|
|
38
|
+
- |
|
|
39
|
+
Using axios for a request when Inertia client handles it natively
|
|
40
|
+
|
|
41
|
+
- id: axios_instead_of_inertia
|
|
42
|
+
severity: medium
|
|
43
|
+
detect: axios usage where Inertia client is more appropriate
|
|
44
|
+
examples:
|
|
45
|
+
- |
|
|
46
|
+
axios.post('/users') instead of router.post('/users')
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
category: maintainability
|
|
2
|
+
severity_context: >
|
|
3
|
+
Code should be easy to modify safely under pressure. Focus on patterns that create
|
|
4
|
+
friction for future changes.
|
|
5
|
+
|
|
6
|
+
rules:
|
|
7
|
+
- id: poor_naming
|
|
8
|
+
severity: medium
|
|
9
|
+
detect: poor naming that obscures intent at the variable, function, or class level
|
|
10
|
+
examples:
|
|
11
|
+
- |
|
|
12
|
+
let x = getD()
|
|
13
|
+
function proc(v) {}
|
|
14
|
+
|
|
15
|
+
- id: large_functions
|
|
16
|
+
severity: medium
|
|
17
|
+
detect: large functions or methods that should be decomposed
|
|
18
|
+
examples:
|
|
19
|
+
- |
|
|
20
|
+
A 200-line function handling validation, DB writes, and notifications
|
|
21
|
+
|
|
22
|
+
- id: implicit_mutations
|
|
23
|
+
severity: medium
|
|
24
|
+
detect: implicit mutations of shared state or parameters
|
|
25
|
+
examples:
|
|
26
|
+
- |
|
|
27
|
+
function process(order) { order.status = 'done' }
|
|
28
|
+
|
|
29
|
+
- id: inconsistent_conventions
|
|
30
|
+
severity: low
|
|
31
|
+
detect: inconsistent naming, formatting, or structural conventions
|
|
32
|
+
examples:
|
|
33
|
+
- |
|
|
34
|
+
getUserById, fetchOrder, loadProduct in the same codebase
|
|
35
|
+
|
|
36
|
+
- id: future_change_friction
|
|
37
|
+
severity: medium
|
|
38
|
+
detect: patterns that create friction for future changes
|
|
39
|
+
examples:
|
|
40
|
+
- |
|
|
41
|
+
Magic numbers without named constants
|
|
42
|
+
Hardcoded URLs or config values inline
|