-
Notifications
You must be signed in to change notification settings - Fork 355
Expand file tree
/
Copy pathprevent-invalid-changesets.js
More file actions
108 lines (89 loc) · 3.23 KB
/
Copy pathprevent-invalid-changesets.js
File metadata and controls
108 lines (89 loc) · 3.23 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
const fs = require("fs");
module.exports = async ({ core, exec }) => {
try {
await exec.exec("git", [
"fetch",
"https://github.com/bigcommerce/catalyst.git",
"integrations/makeswift",
]);
const { stdout } = await exec.getExecOutput("git", [
"diff",
"--name-only",
`origin/integrations/makeswift...HEAD`,
]);
const allFilenames = stdout.split("\n").filter((line) => line.trim());
const changesetFilenames = allFilenames.filter(
(file) => file.startsWith(".changeset/") && file.endsWith(".md")
);
if (changesetFilenames.length === 0) {
core.info("No changeset files found to validate");
return;
}
core.info(`Found ${changesetFilenames.length} changeset files to validate`);
for (const filename of changesetFilenames) {
core.info(`Checking ${filename}...`);
// .changeset/*.md filenames should only contain alphanumeric characters, hyphens, and underscores
if (!/^\.changeset\/[a-zA-Z0-9_-]+\.md$/.test(filename)) {
core.setFailed(`Invalid filename pattern: ${filename}`);
return;
}
// extra defense against path traversal attacks
if (
filename.includes("..") ||
(filename.includes("/") && !filename.startsWith(".changeset/"))
) {
core.setFailed(`Suspicious file path: ${filename}`);
return;
}
if (!fs.existsSync(filename)) {
core.setFailed(`File not found: ${filename}`);
return;
}
// check file size (limit to 100KB)
const stats = fs.statSync(filename);
if (stats.size > 102400) {
core.error(`File too large`, { file: filename });
core.setFailed(`File ${filename} is too large`);
return;
}
if (stats.isSymbolicLink()) {
core.error(`Symlinks are not allowed`, { file: filename });
core.setFailed(`File ${filename} is a symlink`);
return;
}
const content = fs.readFileSync(filename, "utf8");
// starts with "---", captures everything until the next "---"
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
if (!frontmatterMatch) {
core.error(`Failed to extract frontmatter or file has no frontmatter`, {
file: filename,
});
core.setFailed(`File ${filename} has invalid or missing frontmatter`);
return;
}
const frontmatter = frontmatterMatch[1];
// extract all packages starting with "@bigcommerce/
const packageMatches = frontmatter.match(/"@bigcommerce\/[^"]+"/g);
if (packageMatches) {
const invalidPackages = packageMatches.filter(
(pkg) => pkg !== '"@bigcommerce/catalyst-makeswift"'
);
if (invalidPackages.length > 0) {
core.error(
`Invalid package found in changeset file. Only @bigcommerce/catalyst-makeswift is allowed.`,
{ file: filename }
);
core.setFailed(
`File ${filename} contains invalid packages: ${invalidPackages.join(
", "
)}`
);
return;
}
}
}
core.info("All changeset files validated successfully");
} catch (error) {
core.setFailed(`Validation failed: ${error.message}`);
}
};