From 85487e5e10f445218b5ca3a8114309178199f00f Mon Sep 17 00:00:00 2001 From: Erin Spencer Date: Sun, 13 Sep 2026 15:04:49 -0700 Subject: [PATCH 1/3] Add Cloud Run container --- Dockerfile | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..30317f4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.11-slim +RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* +WORKDIR /app +COPY . . +RUN pip install --no-cache-dir . +CMD ["python","service.py"] From b6db11738676880b7fcc459953cfb181d8b4e0aa Mon Sep 17 00:00:00 2001 From: Erin Spencer Date: Sun, 13 Sep 2026 15:04:59 -0700 Subject: [PATCH 2/3] Add public repository audit HTTP service --- service.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 service.py diff --git a/service.py b/service.py new file mode 100644 index 0000000..a84d4e2 --- /dev/null +++ b/service.py @@ -0,0 +1,61 @@ +import json +import os +import re +import subprocess +import tempfile +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path + +from pubskill_lib.audit import audit_path + +REPO = re.compile(r"^https://github\.com/[^/]+/[^/]+(?:\.git)?$") + + +class Handler(BaseHTTPRequestHandler): + def reply(self, status, body): + data = json.dumps(body).encode() + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def do_GET(self): + data = Path("index.html").read_bytes() + self.send_response(200) + self.send_header("Content-Type", "text/html; charset=utf-8") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def do_POST(self): + if self.path != "/audit": + return self.reply(404, {"error": "not found"}) + + try: + length = int(self.headers.get("Content-Length", 0)) + body = json.loads(self.rfile.read(min(length, 8192))) + repo_url = body["repo_url"] + + if not REPO.fullmatch(repo_url): + return self.reply(400, {"error": "public github.com repository required"}) + + with tempfile.TemporaryDirectory() as tmp: + target = os.path.join(tmp, "repo") + subprocess.run( + ["git", "clone", "--depth=1", repo_url, target], + check=True, + timeout=120, + ) + result = audit_path(target) + + self.reply(200, result) + + except Exception as exc: + self.reply(500, {"error": str(exc)}) + + +ThreadingHTTPServer( + ("0.0.0.0", int(os.environ.get("PORT", "8080"))), + Handler, +).serve_forever() From 02980cc727148cd4312b5da930eb16fda8cbef91 Mon Sep 17 00:00:00 2001 From: Erin Spencer Date: Sun, 13 Sep 2026 15:05:06 -0700 Subject: [PATCH 3/3] Add repository audit web page --- index.html | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..4de90f4 --- /dev/null +++ b/index.html @@ -0,0 +1,73 @@ + + + + + + pubskill audit + + + +

Repository Audit

+ + + + +
+ + + +