-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathto-rdjson.mjs
More file actions
125 lines (103 loc) · 3.37 KB
/
Copy pathto-rdjson.mjs
File metadata and controls
125 lines (103 loc) · 3.37 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
122
123
124
125
import fs from "node:fs";
import path from "node:path";
const schema = JSON.parse(
fs.readFileSync(new URL("./output-schema.json", import.meta.url), "utf8"),
);
const findingsSchema = schema.properties.findings;
const findingProperties = findingsSchema.items.properties;
const limits = {
summary: schema.properties.summary.maxLength,
findings: findingsSchema.maxItems,
title: findingProperties.title.maxLength,
body: findingProperties.body.maxLength,
path: findingProperties.path.maxLength,
};
const severities = new Set(findingProperties.severity.enum);
const [inputPath, rdjsonPath, summaryPath] = process.argv.slice(2);
if (!inputPath || !rdjsonPath || !summaryPath) {
throw new Error(
"Usage: node to-rdjson.mjs <review.json> <reviewdog.json> <summary.md>",
);
}
const reviewJson =
inputPath === "-"
? fs.readFileSync(process.stdin.fd, "utf8")
: fs.readFileSync(inputPath, "utf8");
const review = JSON.parse(reviewJson);
if (
review === null ||
typeof review !== "object" ||
!Array.isArray(review.findings)
) {
throw new Error("Review output does not match the expected schema.");
}
if (review.findings.length > limits.findings) {
throw new Error(`Review contains more than ${limits.findings} findings.`);
}
function validateString(value, label, maxLength) {
if (typeof value !== "string" || value.trim().length === 0) {
throw new Error(`${label} must be a non-empty string.`);
}
if (value.length > maxLength) {
throw new Error(`${label} exceeds ${maxLength} characters.`);
}
return value;
}
function validatePath(value, label) {
const filePath = validateString(value, `${label} path`, limits.path);
const normalizedPath = path.posix.normalize(filePath);
const isUnsafe =
normalizedPath !== filePath ||
normalizedPath === "." ||
normalizedPath.startsWith("../") ||
path.posix.isAbsolute(normalizedPath) ||
path.win32.isAbsolute(filePath) ||
filePath.includes("\\");
if (isUnsafe) {
throw new Error(`${label} has an unsafe path.`);
}
return normalizedPath;
}
const summary = validateString(
review.summary,
"Summary",
limits.summary,
).trim();
const diagnostics = review.findings.map((finding, index) => {
const label = `Finding ${index + 1}`;
if (finding === null || typeof finding !== "object") {
throw new Error(`${label} does not match the expected schema.`);
}
const title = validateString(finding.title, `${label} title`, limits.title);
const body = validateString(finding.body, `${label} body`, limits.body);
const findingPath = validatePath(finding.path, label);
if (!Number.isInteger(finding.startLine) || finding.startLine < 1) {
throw new Error(`${label} has an invalid startLine.`);
}
if (
!Number.isInteger(finding.endLine) ||
finding.endLine < finding.startLine
) {
throw new Error(`${label} has an invalid endLine.`);
}
if (!severities.has(finding.severity)) {
throw new Error(`${label} has an invalid severity.`);
}
return {
message: `**${title}**\n\n${body}`,
location: {
path: findingPath,
range: {
start: { line: finding.startLine },
end: { line: finding.endLine },
},
},
severity: finding.severity,
};
});
const rdjson = {
source: { name: "wasp-review" },
diagnostics,
};
fs.writeFileSync(rdjsonPath, `${JSON.stringify(rdjson, null, 2)}\n`);
fs.writeFileSync(summaryPath, `${summary}\n`);