|
from flask import Flask, send_file, request, render_template |
|
import io |
|
import zipfile |
|
import os |
|
|
|
app = Flask(__name__) |
|
|
|
import subprocess |
|
import re |
|
from urllib.parse import urlparse, urlunparse |
|
import shutil |
|
|
|
def sanitize_git_url(url): |
|
"""Sanitizes a Git repository URL.""" |
|
|
|
|
|
try: |
|
parsed_url = urlparse(url) |
|
if not all([parsed_url.scheme, parsed_url.netloc]): |
|
print(f"Error: Invalid URL structure: {url}") |
|
return None |
|
if parsed_url.scheme not in ("https", "http", "git", "ssh"): |
|
print(f"Error: Invalid URL scheme: {parsed_url.scheme}") |
|
return None |
|
except Exception: |
|
print(f"Error: Could not parse url: {url}") |
|
return None |
|
|
|
|
|
|
|
if re.search(r'[;&|]', url): |
|
print(f"Error: Suspicious character in url {url}") |
|
return None |
|
|
|
|
|
|
|
normalized_url = url.rstrip("/").rstrip(".git") |
|
|
|
return normalized_url |
|
|
|
|
|
def git_clone(repo_url): |
|
"""Clones a Git repository, using the repo name as the destination folder.""" |
|
|
|
sanitized_url = sanitize_git_url(repo_url) |
|
if not sanitized_url: |
|
return |
|
|
|
try: |
|
|
|
repo_name = extract_repo_name(sanitized_url) |
|
if not repo_name: |
|
print(f"Error: Unable to extract repository name from URL: {repo_url}") |
|
return |
|
|
|
destination_dir = repo_name |
|
if os.path.isdir(destination_dir): |
|
return repo_name |
|
|
|
subprocess.run( |
|
["git", "clone", sanitized_url, destination_dir], |
|
check=True, |
|
capture_output=True, |
|
text=True |
|
) |
|
print(f"Successfully cloned {sanitized_url} to {destination_dir}") |
|
return repo_name |
|
|
|
except subprocess.CalledProcessError as e: |
|
print(f"Error cloning {sanitized_url}:") |
|
print(f" Return code: {e.returncode}") |
|
print(f" Stdout:\n{e.stdout}") |
|
print(f" Stderr:\n{e.stderr}") |
|
|
|
def extract_repo_name(repo_url): |
|
"""Extracts the repository name from a Git URL.""" |
|
|
|
|
|
|
|
|
|
match = re.search(r"([^/:]+)\/?$", repo_url.rstrip(".git/")) |
|
if match: |
|
return match.group(1) |
|
else: |
|
return None |
|
|
|
def zip_folder_to_buffer(folder_path): |
|
""" |
|
Zips a folder and returns the zipped content in a BytesIO buffer. |
|
|
|
Args: |
|
folder_path: The path to the folder you want to zip. |
|
|
|
Returns: |
|
A io.BytesIO buffer containing the zipped folder data, or None if |
|
there is an error. |
|
""" |
|
try: |
|
buffer = io.BytesIO() |
|
with zipfile.ZipFile(buffer, 'w', zipfile.ZIP_DEFLATED) as zf: |
|
for root, _, files in os.walk(folder_path): |
|
for file in files: |
|
file_path = os.path.join(root, file) |
|
arcname = os.path.relpath(file_path, folder_path) |
|
zf.write(file_path, arcname=arcname) |
|
buffer.seek(0) |
|
return buffer |
|
except Exception as e: |
|
print(f"Error creating zip file: {e}") |
|
return None |
|
|
|
|
|
@app.route('/', methods=['GET', 'POST']) |
|
def index(): |
|
if request.method == 'POST': |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repo_url = request.form['giturl'] |
|
|
|
folder_to_zip = git_clone(repo_url) |
|
|
|
|
|
|
|
zipped_buffer = zip_folder_to_buffer(folder_to_zip) |
|
if zipped_buffer: |
|
|
|
response = send_file( |
|
zipped_buffer, |
|
mimetype="application/zip", |
|
as_attachment=True, |
|
download_name=f"{folder_to_zip}.zip" |
|
) |
|
|
|
|
|
|
|
|
|
|
|
return response |
|
else: |
|
return "Failed to create zip file", 500 |
|
|
|
return render_template('form.html') |
|
|
|
if __name__ == '__main__': |
|
|
|
|
|
app.run(debug=True, port=5500) |
|
|
|
|