@@ -33,10 +33,25 @@ const config = require('./api-extractor.json');
3333
3434const 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 - z A - Z 0 - 9 _ - ] + (?: \/ [ a - z A - Z 0 - 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 = / ^ \. \/ l i b \/ [ a - z A - Z 0 - 9 _ \- \/ ] + \. d \. t s $ / ;
43+
3644async 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