1- import { Router } from 'express' ;
1+ import { Router , Request , Response , NextFunction } from 'express' ;
22import proxy from 'express-http-proxy' ;
3+ import { PassThrough } from 'stream' ;
4+ import getRawBody from 'raw-body' ;
35import { executeChain } from '../chain' ;
46import { getProxyUrl } from '../../config' ;
57
@@ -33,7 +35,7 @@ const stripGitHubFromGitPath = (url: string): string | undefined => {
3335 * @return {boolean } If true, this is a valid and expected git request. Otherwise, false.
3436 */
3537const validGitRequest = ( url : string , headers : any ) : boolean => {
36- const { 'user-agent' : agent , accept } = headers ;
38+ const { 'user-agent' : agent = '' , accept = '' } = headers ;
3739 if ( [ '/info/refs?service=git-upload-pack' , '/info/refs?service=git-receive-pack' ] . includes ( url ) ) {
3840 // https://www.git-scm.com/docs/http-protocol#_discovering_references
3941 // We can only filter based on User-Agent since the Accept header is not
@@ -47,89 +49,95 @@ const validGitRequest = (url: string, headers: any): boolean => {
4749 return false ;
4850} ;
4951
52+ const isPackPost = ( req : Request ) =>
53+ req . method === 'POST' && / \/ .* \. g i t \/ ( g i t - u p l o a d - p a c k | g i t - r e c e i v e - p a c k ) $ / . test ( req . url ) ;
54+
55+ const teeAndValidate = async ( req : Request , res : Response , next : NextFunction ) => {
56+ if ( ! isPackPost ( req ) ) return next ( ) ;
57+
58+ const proxyStream = new PassThrough ( ) ;
59+ const pluginStream = new PassThrough ( ) ;
60+
61+ req . pipe ( proxyStream ) ;
62+ req . pipe ( pluginStream ) ;
63+
64+ try {
65+ const buf = await getRawBody ( pluginStream , { limit : '1gb' } ) ;
66+ ( req as any ) . body = buf ;
67+ const verdict = await executeChain ( req , res ) ;
68+ console . log ( 'action processed' ) ;
69+
70+ if ( verdict . error || verdict . blocked ) {
71+ const msg = verdict . error ? verdict . errorMessage ! : verdict . blockedMessage ! ;
72+ res
73+ . set ( {
74+ 'content-type' : 'application/x-git-receive-pack-result' ,
75+ 'transfer-encoding' : 'chunked' ,
76+ expires : 'Fri, 01 Jan 1980 00:00:00 GMT' ,
77+ pragma : 'no-cache' ,
78+ 'cache-control' : 'no-cache, max-age=0, must-revalidate' ,
79+ vary : 'Accept-Encoding' ,
80+ 'x-frame-options' : 'DENY' ,
81+ connection : 'close' ,
82+ } )
83+ . status ( 200 )
84+ . send ( handleMessage ( msg ) ) ;
85+ return ;
86+ }
87+
88+ ( req as any ) . pipe = ( dest : any , opts : any ) => proxyStream . pipe ( dest , opts ) ;
89+ next ( ) ;
90+ } catch ( e ) {
91+ console . error ( e ) ;
92+ proxyStream . destroy ( e as Error ) ;
93+ res . status ( 500 ) . end ( 'Proxy error' ) ;
94+ }
95+ } ;
96+
97+ router . use ( teeAndValidate ) ;
98+
5099router . use (
51100 '/' ,
52101 proxy ( getProxyUrl ( ) , {
102+ parseReqBody : false ,
53103 preserveHostHdr : false ,
54- filter : async function ( req , res ) {
55- try {
56- console . log ( 'request url: ' , req . url ) ;
57- console . log ( 'host: ' , req . headers . host ) ;
58- console . log ( 'user-agent: ' , req . headers [ 'user-agent' ] ) ;
59- const gitPath = stripGitHubFromGitPath ( req . url ) ;
60- if ( gitPath === undefined || ! validGitRequest ( gitPath , req . headers ) ) {
61- res . status ( 400 ) . send ( 'Invalid request received' ) ;
62- return false ;
63- }
64-
65- const action = await executeChain ( req , res ) ;
66- console . log ( 'action processed' ) ;
67-
68- if ( action . error || action . blocked ) {
69- res . set ( 'content-type' , 'application/x-git-receive-pack-result' ) ;
70- res . set ( 'transfer-encoding' , 'chunked' ) ;
71- res . set ( 'expires' , 'Fri, 01 Jan 1980 00:00:00 GMT' ) ;
72- res . set ( 'pragma' , 'no-cache' ) ;
73- res . set ( 'cache-control' , 'no-cache, max-age=0, must-revalidate' ) ;
74- res . set ( 'vary' , 'Accept-Encoding' ) ;
75- res . set ( 'x-frame-options' , 'DENY' ) ;
76- res . set ( 'connection' , 'close' ) ;
77-
78- let message = '' ;
79-
80- if ( action . error ) {
81- message = action . errorMessage ! ;
82- console . error ( message ) ;
83- }
84- if ( action . blocked ) {
85- message = action . blockedMessage ! ;
86- }
87-
88- const packetMessage = handleMessage ( message ) ;
89-
90- console . log ( req . headers ) ;
91-
92- res . status ( 200 ) . send ( packetMessage ) ;
93-
94- return false ;
95- }
96-
97- return true ;
98- } catch ( e ) {
99- console . error ( e ) ;
104+
105+ filter : async ( req , res ) => {
106+ console . log ( 'request url: ' , req . url ) ;
107+ console . log ( 'host: ' , req . headers . host ) ;
108+ console . log ( 'user-agent: ' , req . headers [ 'user-agent' ] ) ;
109+ const gitPath = stripGitHubFromGitPath ( req . url ) ;
110+ if ( gitPath === undefined || ! validGitRequest ( gitPath , req . headers ) ) {
111+ res . status ( 400 ) . send ( 'Invalid request received' ) ;
100112 return false ;
101113 }
114+ return true ;
102115 } ,
116+
103117 proxyReqPathResolver : ( req ) => {
104118 const url = getProxyUrl ( ) + req . originalUrl ;
105119 console . log ( 'Sending request to ' + url ) ;
106120 return url ;
107121 } ,
108- proxyReqOptDecorator : function ( proxyReqOpts ) {
109- return proxyReqOpts ;
110- } ,
111122
112- proxyReqBodyDecorator : function ( bodyContent , srcReq ) {
113- if ( srcReq . method === 'GET' ) {
114- return '' ;
115- }
116- return bodyContent ;
117- } ,
118-
119- proxyErrorHandler : function ( err , res , next ) {
123+ proxyErrorHandler : ( err , res , next ) => {
120124 console . log ( `ERROR=${ err } ` ) ;
121125 next ( err ) ;
122126 } ,
123127 } ) ,
124128) ;
125129
126130const handleMessage = ( message : string ) : string => {
127- const errorMessage = `\t${ message } ` ;
128- const len = 6 + new TextEncoder ( ) . encode ( errorMessage ) . length ;
129-
130- const prefix = len . toString ( 16 ) ;
131- const packetMessage = `${ prefix . padStart ( 4 , '0' ) } \x02${ errorMessage } \n0000` ;
132- return packetMessage ;
131+ const body = `\t${ message } ` ;
132+ const len = ( 6 + Buffer . byteLength ( body ) ) . toString ( 16 ) . padStart ( 4 , '0' ) ;
133+ return `${ len } \x02${ body } \n0000` ;
133134} ;
134135
135- export { router , handleMessage , validGitRequest , stripGitHubFromGitPath } ;
136+ export {
137+ router ,
138+ handleMessage ,
139+ validGitRequest ,
140+ teeAndValidate ,
141+ isPackPost ,
142+ stripGitHubFromGitPath ,
143+ } ;
0 commit comments