Skip to content

Instantly share code, notes, and snippets.

@stewartpark
Last active March 26, 2024 00:59
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save stewartpark/1b079dc0481c6213def9 to your computer and use it in GitHub Desktop.
Save stewartpark/1b079dc0481c6213def9 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# This is an example Git server. http://blog.nerds.kr/post/106956608369/implementing-a-git-http-server-in-python
# Stewart Park <stewartpark92@gmail.com>
from flask import Flask, make_response, request, abort
from StringIO import StringIO
from dulwich.pack import PackStreamReader
import subprocess, os.path
from flask.ext.httpauth import HTTPBasicAuth
app = Flask(__name__)
auth = HTTPBasicAuth()
users = {
"john": "hello",
"susan": "bye"
}
@auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
@app.route('/<string:project_name>/info/refs')
@auth.login_required
def info_refs(project_name):
service = request.args.get('service')
if service[:4] != 'git-':
abort(500)
p = subprocess.Popen([service, '--stateless-rpc', '--advertise-refs', os.path.join('.', project_name)], stdout=subprocess.PIPE)
packet = '# service=%s\n' % service
length = len(packet) + 4
_hex = '0123456789abcdef'
prefix = ''
prefix += _hex[length >> 12 & 0xf]
prefix += _hex[length >> 8 & 0xf]
prefix += _hex[length >> 4 & 0xf]
prefix += _hex[length & 0xf]
data = prefix + packet + '0000'
data += p.stdout.read()
res = make_response(data)
res.headers['Expires'] = 'Fri, 01 Jan 1980 00:00:00 GMT'
res.headers['Pragma'] = 'no-cache'
res.headers['Cache-Control'] = 'no-cache, max-age=0, must-revalidate'
res.headers['Content-Type'] = 'application/x-%s-advertisement' % service
p.wait()
return res
@app.route('/<string:project_name>/git-receive-pack', methods=('POST',))
@auth.login_required
def git_receive_pack(project_name):
p = subprocess.Popen(['git-receive-pack', '--stateless-rpc', os.path.join('.', project_name)], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
data_in = request.data
pack_file = data_in[data_in.index('PACK'):]
objects = PackStreamReader(StringIO(pack_file).read)
for obj in objects.read_objects():
if obj.obj_type_num == 1: # Commit
print obj
p.stdin.write(data_in)
data_out = p.stdout.read()
res = make_response(data_out)
res.headers['Expires'] = 'Fri, 01 Jan 1980 00:00:00 GMT'
res.headers['Pragma'] = 'no-cache'
res.headers['Cache-Control'] = 'no-cache, max-age=0, must-revalidate'
res.headers['Content-Type'] = 'application/x-git-receive-pack-result'
p.wait()
return res
@app.route('/<string:project_name>/git-upload-pack', methods=('POST',))
@auth.login_required
def git_upload_pack(project_name):
p = subprocess.Popen(['git-upload-pack', '--stateless-rpc', os.path.join('.', project_name)], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.write(request.data)
data = p.stdout.read()
res = make_response(data)
res.headers['Expires'] = 'Fri, 01 Jan 1980 00:00:00 GMT'
res.headers['Pragma'] = 'no-cache'
res.headers['Cache-Control'] = 'no-cache, max-age=0, must-revalidate'
res.headers['Content-Type'] = 'application/x-git-upload-pack-result'
p.wait()
return res
if __name__ == '__main__':
app.run()
@mfm24
Copy link

mfm24 commented Oct 21, 2016

Nice, this is useful!

I think

    _hex = '0123456789abcdef'
    prefix = ''
    prefix += _hex[length >> 12 & 0xf]
    prefix += _hex[length >> 8  & 0xf]
    prefix += _hex[length >> 4 & 0xf]
    prefix += _hex[length & 0xf]

could be replaced with

    prefix = "{:04x}".format(length & 0xFFFF);

?

@rabbagliettiandrea
Copy link

Thank you so much :)

@stevepeak
Copy link

Thanks for this awesome post! I implemented it with Tornado here.

@yhojann-cl
Copy link

yhojann-cl commented Nov 28, 2020

Your code is vulnerable to recomte code execution:

    if service[:4] != 'git-': 
        abort(500)
    p = subprocess.Popen([service,

By example: GET /foo/info/refs?service=git-;rm%20-rf%20./*;exit;help HTTP/1.1

@fakerybakery
Copy link

Thank you so much! What is the license for this code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment