@@ -445,6 +445,9 @@ async function categorizeFile(
445445) : Promise < [ string | null , FileInfo ] > {
446446 const pkg = / ^ t y p e s \/ ( .* ?) \/ .* $ / . exec ( path ) ?. [ 1 ] ;
447447 if ( ! pkg ) return [ null , { path, kind : "infrastructure" } ] ;
448+ // Treat unrecognized directory names as infrastructure rather than trusting the string in
449+ // downstream comment/label/URL construction.
450+ if ( ! isValidPackageDirectoryName ( pkg ) ) return [ null , { path, kind : "infrastructure" } ] ;
448451
449452 if ( isDeclarationPath ( path ) ) return [ pkg , { path, kind : "definition" } ] ;
450453 if ( / \. (?: [ c m ] ? t s | t s x ) $ / . test ( path ) ) return [ pkg , { path, kind : "test" } ] ;
@@ -754,8 +757,23 @@ export async function getOwnersOfPackage(
754757 } catch ( e ) {
755758 if ( e instanceof Error ) return new Error ( `error parsing owners: ${ e . message } ` ) ;
756759 }
757- return noNullish ( parsed ! . contributors . map ( ( c ) => c . githubUsername ) ) ;
760+ return noNullish ( parsed ! . contributors . map ( ( c ) => c . githubUsername ) ) . filter ( isValidGithubUsername ) ;
758761 }
759762
760- return noNullish ( packageJsonObj . owners ?. map ( ( c : any ) => c ?. githubUsername ) ) ;
763+ return noNullish ( packageJsonObj . owners ?. map ( ( c : any ) => c ?. githubUsername ) ) . filter ( isValidGithubUsername ) ;
764+ }
765+
766+ // GitHub usernames: alphanumeric or single hyphens (plus underscores for Enterprise Managed
767+ // Users). Validating here prevents an untrusted PR-head package.json from injecting Markdown
768+ // (links, headers, newlines, etc.) into bot comment bodies under @typescript-bot's identity.
769+ function isValidGithubUsername ( name : unknown ) : name is string {
770+ return typeof name === "string" && / ^ [ A - Z a - z 0 - 9 _ - ] + $ / . test ( name ) ;
771+ }
772+
773+ // DefinitelyTyped package directory names: lowercase letters, digits, dots, hyphens, and the
774+ // `__` scope-mangle separator (e.g. `react-native`, `gapi.client.youtube-v3`, `google__maps`).
775+ // Validating here prevents an attacker-crafted path like `types/foo](evil)/index.d.ts` from
776+ // injecting Markdown when the directory name is interpolated into bot comment bodies.
777+ function isValidPackageDirectoryName ( name : string ) : boolean {
778+ return / ^ [ a - z 0 - 9 ] [ a - z 0 - 9 . _ - ] * $ / . test ( name ) ;
761779}
0 commit comments