Skip to content

Commit 8ccc2f7

Browse files
committed
chore: fix report file path validation
1 parent 46ece5d commit 8ccc2f7

1 file changed

Lines changed: 24 additions & 1 deletion

File tree

generate-reports.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,25 @@ const config = require('./api-extractor.json');
3333

3434
const tempConfigFile = 'api-extractor.tmp';
3535

36+
// Regex to validate that the entrypoint name consists of safe alphanumeric, underscore,
37+
// and dash characters, optionally separated by single slashes.
38+
const ENTRY_POINT_REGEX = /^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$/;
39+
40+
// Regex to validate that the typing file path is a local relative declaration file path
41+
// inside the "./lib" directory.
42+
const TYPING_FILE_PATH_REGEX = /^\.\/lib\/[a-zA-Z0-9_\-\/]+\.d\.ts$/;
43+
3644
async function generateReports() {
3745
const entryPoints = require('./entrypoints.json');
3846
for (const entryPoint in entryPoints) {
47+
// Validate entryPoint to prevent path traversal
48+
if (!ENTRY_POINT_REGEX.test(entryPoint)) {
49+
throw new Error(`Invalid entryPoint format: ${entryPoint}`);
50+
}
3951
const filePath = entryPoints[entryPoint].typings;
52+
if (filePath.includes('..') || !TYPING_FILE_PATH_REGEX.test(filePath)) {
53+
throw new Error(`Invalid typing file path: ${filePath}`);
54+
}
4055
await generateReportForEntryPoint(entryPoint, filePath);
4156
}
4257
}
@@ -46,6 +61,9 @@ async function generateReportForEntryPoint(entryPoint, filePath) {
4661
console.log('========================================================\n');
4762

4863
const safeName = entryPoint.replace('/', '.');
64+
if (safeName.includes('..') || safeName.includes('/') || safeName.includes('\\')) {
65+
throw new Error(`Invalid safeName calculated: ${safeName}`);
66+
}
4967
console.log('Updating configuration for entry point...');
5068
config.apiReport.reportFileName = `${safeName}.api.md`;
5169
config.mainEntryPointFilePath = filePath;
@@ -75,7 +93,12 @@ async function generateReportForEntryPoint(entryPoint, filePath) {
7593
console.error(`API Extractor completed successfully`);
7694

7795
// Strip @excludeFromDocs APIs from the generated docModel so they aren't documented in reference docs.
78-
const apiJsonPath = path.resolve('temp', `${safeName}.api.json`);
96+
const tempDir = path.resolve('temp');
97+
const apiJsonPath = path.resolve(tempDir, `${safeName}.api.json`);
98+
const relativePath = path.relative(tempDir, apiJsonPath);
99+
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
100+
throw new Error(`Path traversal detected: ${apiJsonPath}`);
101+
}
79102
await stripHiddenDocsFromApiJson(apiJsonPath);
80103
} finally {
81104
await fs.unlink(tempConfigFile);

0 commit comments

Comments
 (0)