@@ -21,6 +21,19 @@ function isValidRepoId(repo_id: string): boolean {
2121 return repoPattern . test ( trimmed ) ;
2222}
2323
24+ function isValidCommitSha ( sha : string ) : boolean {
25+ // Git SHAs are 40-character hexadecimal strings
26+ const shaPattern = / ^ [ a - f 0 - 9 ] { 40 } $ / ;
27+ return shaPattern . test ( sha ) ;
28+ }
29+
30+ function buildGitHubApiUrl ( path : string ) : URL {
31+ // Always use the official GitHub API base URL to prevent SSRF
32+ const baseUrl = 'https://api.github.com' ;
33+ // URL constructor will throw if path is malformed
34+ return new URL ( path , baseUrl ) ;
35+ }
36+
2437export async function POST ( request : Request ) {
2538 try {
2639 const { repositories } : CIStatusRequest = await request . json ( ) ;
@@ -66,8 +79,10 @@ export async function POST(request: Request) {
6679
6780 try {
6881 // First, get the latest commit SHA on main branch
82+ // Use URL constructor to prevent SSRF
83+ const branchUrl = buildGitHubApiUrl ( `/repos/${ encodeURIComponent ( repoIdStr ) } /branches/main` ) ;
6984 const branchResponse = await fetch (
70- `https://api.github.com/repos/ ${ repoIdStr } /branches/main` ,
85+ branchUrl . toString ( ) ,
7186 {
7287 headers : {
7388 'Authorization' : `Bearer ${ token } ` ,
@@ -93,9 +108,22 @@ export async function POST(request: Request) {
93108 const branchData = await branchResponse . json ( ) ;
94109 const latestCommitSha = branchData . commit . sha ;
95110
111+ // Validate the commit SHA to prevent SSRF
112+ if ( ! isValidCommitSha ( latestCommitSha ) ) {
113+ return {
114+ repo_id,
115+ state : 'unknown' as const ,
116+ total_checks : 0 ,
117+ updated_at : new Date ( ) . toISOString ( ) ,
118+ details : 'Invalid commit SHA received from API' ,
119+ } ;
120+ }
121+
96122 // Now get check runs for this specific commit
123+ // Use URL constructor to prevent SSRF
124+ const checksUrl = buildGitHubApiUrl ( `/repos/${ encodeURIComponent ( repoIdStr ) } /commits/${ latestCommitSha } /check-runs` ) ;
97125 const checksResponse = await fetch (
98- `https://api.github.com/repos/ ${ repoIdStr } /commits/ ${ latestCommitSha } /check-runs` ,
126+ checksUrl . toString ( ) ,
99127 {
100128 headers : {
101129 'Authorization' : `Bearer ${ token } ` ,
0 commit comments