-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtypes.ts
More file actions
121 lines (104 loc) · 3.38 KB
/
Copy pathtypes.ts
File metadata and controls
121 lines (104 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/** The wire contract with `/ptaas/ajax/lory-code-review.php`. */
export const SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'] as const;
export type Severity = (typeof SEVERITIES)[number];
export const CONFIDENCES = ['high', 'medium', 'low'] as const;
export type Confidence = (typeof CONFIDENCES)[number];
/** One security defect, anchored to a line in the reviewed file. */
export interface Finding {
/** 1-based, absolute in the file (the server echoes back the window offset). */
line: number;
/** Last line of a multi-line defect. Absent when the defect is one line. */
end_line?: number;
severity: Severity;
title: string;
detail: string;
confidence: Confidence;
/** `CWE-89` form. Absent unless the mapping is certain. */
cwe?: string;
/** Replacement source for `line`..`end_line`. Code only. */
fix?: string;
/** Short rationale for the fix. */
fix_note?: string;
}
export interface ReviewMeta {
start_line: number;
end_line: number;
model: string;
/** Which rate-limit tier served the request: `anonymous` or `token`. */
tier?: string;
}
export interface ReviewResult {
findings: Finding[];
summary: string;
meta?: ReviewMeta;
}
export interface ReviewRequest {
code: string;
language?: string;
filename?: string;
/** Line number of the first line of `code` within the whole file. */
start_line?: number;
stream?: boolean;
}
/** Rank for comparisons. Lower is worse. */
export const SEVERITY_RANK: Record<Severity, number> = {
critical: 0,
high: 1,
medium: 2,
low: 3,
info: 4,
};
export const CONFIDENCE_RANK: Record<Confidence, number> = {
high: 0,
medium: 1,
low: 2,
};
export function isSeverity(value: unknown): value is Severity {
return typeof value === 'string' && (SEVERITIES as readonly string[]).includes(value);
}
export function isConfidence(value: unknown): value is Confidence {
return typeof value === 'string' && (CONFIDENCES as readonly string[]).includes(value);
}
/**
* Coerce an untrusted object into a Finding, or reject it.
*
* The server already validates, but the extension must not trust the network:
* a proxy, a stale server, or a misconfigured `lory.baseUrl` can all put an
* unexpected shape on the wire, and a bad `line` would land a security marker
* on innocent code.
*/
export function parseFinding(raw: unknown): Finding | null {
if (typeof raw !== 'object' || raw === null) {
return null;
}
const o = raw as Record<string, unknown>;
const line = Number(o['line']);
if (!Number.isInteger(line) || line < 1) {
return null;
}
const title = typeof o['title'] === 'string' ? o['title'].trim() : '';
if (title === '') {
return null;
}
const finding: Finding = {
line,
severity: isSeverity(o['severity']) ? o['severity'] : 'medium',
title,
detail: typeof o['detail'] === 'string' ? o['detail'].trim() : '',
confidence: isConfidence(o['confidence']) ? o['confidence'] : 'medium',
};
const endLine = Number(o['end_line']);
if (Number.isInteger(endLine) && endLine > line) {
finding.end_line = endLine;
}
if (typeof o['cwe'] === 'string' && /^CWE-\d{1,4}$/i.test(o['cwe'])) {
finding.cwe = o['cwe'].toUpperCase();
}
if (typeof o['fix'] === 'string' && o['fix'] !== '') {
finding.fix = o['fix'];
}
if (typeof o['fix_note'] === 'string' && o['fix_note'].trim() !== '') {
finding.fix_note = o['fix_note'].trim();
}
return finding;
}