@@ -6,23 +6,31 @@ import { executeChain } from '../chain';
66import { processUrlPath , validGitRequest , getAllProxiedHosts } from './helper' ;
77import { ProxyOptions } from 'express-http-proxy' ;
88
9+ enum ActionType {
10+ // eslint-disable-next-line no-unused-vars
11+ ALLOWED = 'Allowed' ,
12+ // eslint-disable-next-line no-unused-vars
13+ ERROR = 'Error' ,
14+ // eslint-disable-next-line no-unused-vars
15+ BLOCKED = 'Blocked' ,
16+ }
17+
918const logAction = (
1019 url : string ,
1120 host : string | null | undefined ,
1221 userAgent : string | null | undefined ,
13- errMsg : string | null | undefined ,
14- blockMsg ?: string | null | undefined ,
22+ type : ActionType ,
23+ message ?: string ,
1524) => {
16- let msg = `Action processed: ${ ! ( errMsg || blockMsg ) ? 'Allowed' : 'Blocked' }
25+ let msg = `Action processed: ${ type }
1726 Request URL: ${ url }
1827 Host: ${ host }
1928 User-Agent: ${ userAgent } ` ;
20- if ( errMsg ) {
21- msg += `\n Error: ${ errMsg } ` ;
22- }
23- if ( blockMsg ) {
24- msg += `\n Blocked: ${ blockMsg } ` ;
29+
30+ if ( message && type !== ActionType . ALLOWED ) {
31+ msg += `\n ${ type } : ${ message } ` ;
2532 }
33+
2634 console . log ( msg ) ;
2735} ;
2836
@@ -34,76 +42,69 @@ const proxyFilter: ProxyOptions['filter'] = async (req, res) => {
3442 urlComponents . gitPath === undefined ||
3543 ! validGitRequest ( urlComponents . gitPath , req . headers )
3644 ) {
37- logAction (
38- req . url ,
39- req . headers . host ,
40- req . headers [ 'user-agent' ] ,
41- 'Invalid request received' ,
42- null ,
43- ) ;
44- // return status 200 to ensure that the error message is rendered by the git client
45- res . status ( 200 ) . send ( handleMessage ( 'Invalid request received' ) ) ;
45+ const message = 'Invalid request received' ;
46+ logAction ( req . url , req . headers . host , req . headers [ 'user-agent' ] , ActionType . ERROR , message ) ;
47+ res . status ( 200 ) . send ( handleMessage ( message ) ) ;
4648 return false ;
4749 }
4850
4951 const action = await executeChain ( req , res ) ;
5052
5153 if ( action . error || action . blocked ) {
52- res . set ( 'content-type' , 'application/x-git-receive-pack-result' ) ;
53- res . set ( 'expires' , 'Fri, 01 Jan 1980 00:00:00 GMT' ) ;
54- res . set ( 'pragma' , 'no-cache' ) ;
55- res . set ( 'cache-control' , 'no-cache, max-age=0, must-revalidate' ) ;
56- res . set ( 'vary' , 'Accept-Encoding' ) ;
57- res . set ( 'x-frame-options' , 'DENY' ) ;
58- res . set ( 'connection' , 'close' ) ;
59-
60- const packetMessage = handleMessage ( action . errorMessage ?? action . blockedMessage ?? '' ) ;
61-
62- logAction (
63- req . url ,
64- req . headers . host ,
65- req . headers [ 'user-agent' ] ,
66- action . errorMessage ,
67- action . blockedMessage ,
68- ) ;
69- // return status 200 to ensure that the error message is rendered by the git client
70- res . status ( 200 ) . send ( packetMessage ) ;
54+ const message = action . errorMessage ?? action . blockedMessage ?? 'Unknown error' ;
55+ const type = action . error ? ActionType . ERROR : ActionType . BLOCKED ;
56+
57+ logAction ( req . url , req . headers . host , req . headers [ 'user-agent' ] , type , message ) ;
58+ sendErrorResponse ( req , res , message ) ;
7159 return false ;
7260 }
7361
74- logAction (
75- req . url ,
76- req . headers . host ,
77- req . headers [ 'user-agent' ] ,
78- action . errorMessage ,
79- action . blockedMessage ,
80- ) ;
62+ logAction ( req . url , req . headers . host , req . headers [ 'user-agent' ] , ActionType . ALLOWED ) ;
8163
8264 // this is the only case where we do not respond directly, instead we return true to proxy the request
8365 return true ;
8466 } catch ( e ) {
85- const packetMessage = handleMessage ( `Error occurred in proxy filter function ${ e } ` ) ;
86-
87- logAction (
88- req . url ,
89- req . headers . host ,
90- req . headers [ 'user-agent' ] ,
91- 'Error occurred in proxy filter function: ' + ( ( e as Error ) . message ?? e ) ,
92- null ,
93- ) ;
67+ const message = `Error occurred in proxy filter function ${ ( e as Error ) . message ?? e } ` ;
9468
95- // return status 200 to ensure that the error message is rendered by the git client
96- res . status ( 200 ) . send ( packetMessage ) ;
69+ logAction ( req . url , req . headers . host , req . headers [ 'user-agent' ] , ActionType . ERROR , message ) ;
70+ sendErrorResponse ( req , res , message ) ;
9771 return false ;
9872 }
9973} ;
10074
75+ const sendErrorResponse = ( req : Request , res : Response , message : string ) : void => {
76+ // GET requests to /info/refs (used to check refs for many git operations) must use Git protocol error packet format
77+ if ( req . method === 'GET' && req . url . includes ( '/info/refs' ) ) {
78+ res . set ( 'content-type' , 'application/x-git-upload-pack-advertisement' ) ;
79+ res . status ( 200 ) . send ( handleRefsErrorMessage ( message ) ) ;
80+ return ;
81+ }
82+
83+ // Standard git receive-pack response
84+ res . set ( 'content-type' , 'application/x-git-receive-pack-result' ) ;
85+ res . set ( 'expires' , 'Fri, 01 Jan 1980 00:00:00 GMT' ) ;
86+ res . set ( 'pragma' , 'no-cache' ) ;
87+ res . set ( 'cache-control' , 'no-cache, max-age=0, must-revalidate' ) ;
88+ res . set ( 'vary' , 'Accept-Encoding' ) ;
89+ res . set ( 'x-frame-options' , 'DENY' ) ;
90+ res . set ( 'connection' , 'close' ) ;
91+
92+ res . status ( 200 ) . send ( handleMessage ( message ) ) ;
93+ } ;
94+
10195const handleMessage = ( message : string ) : string => {
10296 const body = `\t${ message } ` ;
10397 const len = ( 6 + Buffer . byteLength ( body ) ) . toString ( 16 ) . padStart ( 4 , '0' ) ;
10498 return `${ len } \x02${ body } \n0000` ;
10599} ;
106100
101+ const handleRefsErrorMessage = ( message : string ) : string => {
102+ // Git protocol for GET /info/refs error packets: PKT-LINE("ERR" SP explanation-text)
103+ const errorBody = `ERR ${ message } ` ;
104+ const len = ( 4 + Buffer . byteLength ( errorBody ) ) . toString ( 16 ) . padStart ( 4 , '0' ) ;
105+ return `${ len } ${ errorBody } \n0000` ;
106+ } ;
107+
107108const getRequestPathResolver : ( prefix : string ) => ProxyOptions [ 'proxyReqPathResolver' ] = (
108109 prefix ,
109110) => {
@@ -155,15 +156,10 @@ const teeAndValidate = async (req: Request, res: Response, next: NextFunction) =
155156 ( req as any ) . body = buf ;
156157 const verdict = await executeChain ( req , res ) ;
157158 if ( verdict . error || verdict . blocked ) {
158- const msg = verdict . errorMessage ?? verdict . blockedMessage ?? '' ;
159+ const message = verdict . errorMessage ?? verdict . blockedMessage ?? 'Unknown error' ;
160+ const type = verdict . error ? ActionType . ERROR : ActionType . BLOCKED ;
159161
160- logAction (
161- req . url ,
162- req . headers ?. host ,
163- req . headers ?. [ 'user-agent' ] ,
164- verdict . errorMessage ,
165- verdict . blockedMessage ,
166- ) ;
162+ logAction ( req . url , req . headers ?. host , req . headers ?. [ 'user-agent' ] , type , message ) ;
167163
168164 res
169165 . set ( {
@@ -176,7 +172,7 @@ const teeAndValidate = async (req: Request, res: Response, next: NextFunction) =
176172 connection : 'close' ,
177173 } )
178174 . status ( 200 ) // return status 200 to ensure that the error message is rendered by the git client
179- . send ( handleMessage ( msg ) ) ;
175+ . send ( handleMessage ( message ) ) ;
180176 return ;
181177 }
182178
@@ -216,7 +212,8 @@ const getRouter = async () => {
216212 proxyReqOptDecorator : proxyReqOptDecorator ,
217213 proxyReqBodyDecorator : proxyReqBodyDecorator ,
218214 proxyErrorHandler : proxyErrorHandler ,
219- } ) ,
215+ stream : true ,
216+ } as any ) ,
220217 ) ;
221218 } ) ;
222219
@@ -229,7 +226,8 @@ const getRouter = async () => {
229226 proxyReqOptDecorator : proxyReqOptDecorator ,
230227 proxyReqBodyDecorator : proxyReqBodyDecorator ,
231228 proxyErrorHandler : proxyErrorHandler ,
232- } ) ;
229+ stream : true ,
230+ } as any ) ;
233231
234232 console . log ( 'proxy keys registered: ' , JSON . stringify ( proxyKeys ) ) ;
235233
@@ -259,4 +257,12 @@ const getRouter = async () => {
259257 return router ;
260258} ;
261259
262- export { proxyFilter , getRouter , handleMessage , isPackPost , teeAndValidate , validGitRequest } ;
260+ export {
261+ proxyFilter ,
262+ getRouter ,
263+ handleMessage ,
264+ handleRefsErrorMessage ,
265+ isPackPost ,
266+ teeAndValidate ,
267+ validGitRequest ,
268+ } ;
0 commit comments