Add public Cloud Run repository audit service - #6
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 02980cc727
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <p>${f.claim}</p> | ||
| <div class="meta"> | ||
| Evidence: ${f.evidence}<br> |
There was a problem hiding this comment.
Render audit fields as text, not HTML
When auditing an attacker-controlled repository, fields such as claim and evidence contain raw repository strings; for example, a pyproject.toml script name can contain an <img onerror=...> payload. Interpolating those values into innerHTML executes the payload in the service origin; the same unsafe pattern also handles the remote, hmmm, and error fields. Escape every value or construct the result with textContent/DOM nodes.
Useful? React with 👍 / 👎.
| check=True, | ||
| timeout=120, | ||
| ) | ||
| result = audit_path(target) |
There was a problem hiding this comment.
Refuse out-of-tree symlinks before auditing
When a public repository commits README.md as a symlink to /dev/zero, Git preserves that symlink during clone and audit_path() eventually follows it in _read_text() using an unbounded Path.read_text(). Because only the clone subprocess has a timeout and the audit runs inside the HTTP process, one request can hang or OOM the Cloud Run instance; validate that inspected files are regular files contained beneath target, or isolate and resource-limit the audit.
Useful? React with 👍 / 👎.
| with tempfile.TemporaryDirectory() as tmp: | ||
| target = os.path.join(tmp, "repo") | ||
| subprocess.run( | ||
| ["git", "clone", "--depth=1", repo_url, target], |
There was a problem hiding this comment.
Bound clone resource consumption
On this unauthenticated public endpoint, every request can start a concurrent clone into instance-local storage without a byte, disk, or concurrency limit. git clone -h defines --depth only as “create a shallow clone of that depth,” so a repository with a very large current tree—or a burst of ordinary requests—can still exhaust disk, threads, or autoscaling budget before the 120-second timeout; add admission controls and enforce hard resource limits around cloning.
Useful? React with 👍 / 👎.
| ThreadingHTTPServer( | ||
| ("0.0.0.0", int(os.environ.get("PORT", "8080"))), | ||
| Handler, | ||
| ).serve_forever() |
There was a problem hiding this comment.
Remove the prohibited hosted service
This starts a network-facing remote-repository service even though the required matching HANDOFF.md explicitly lists “Do not host a SaaS” as a v0.2 non-goal and leaves remote URL inspection outside v0.2. Shipping this server expands the public product surface contrary to the repository's binding work order; retain the documented local-path CLI rather than adding the hosted endpoint.
AGENTS.md reference: AGENTS.md:L3-L5
Useful? React with 👍 / 👎.
| FROM python:3.11-slim | ||
| RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* | ||
| WORKDIR /app | ||
| COPY . . |
There was a problem hiding this comment.
Exclude local secrets from the container context
When a developer builds this image from a checkout containing the repository's ignored .env credential file, COPY . . includes that file because .gitignore does not filter Docker build contexts and no .dockerignore exists. The credentials then remain in the final image and its layers, exposing them to deployments and anyone with image access; add a .dockerignore that excludes .env*, .git, and other local artifacts while explicitly retaining only safe examples if needed.
Useful? React with 👍 / 👎.
| }) | ||
| }); | ||
|
|
||
| const r = await response.json(); |
There was a problem hiding this comment.
Report transport and non-JSON failures
When fetch() rejects or Cloud Run returns a proxy-generated non-JSON 5xx response, response.json() throws before the response.ok branch runs. With no surrounding error handling, the page leaves “Running audit...” displayed indefinitely and produces only an unhandled promise rejection; catch both transport and response-parsing failures and replace the progress message with an actionable error.
Useful? React with 👍 / 👎.
| except Exception as exc: | ||
| self.reply(500, {"error": str(exc)}) |
There was a problem hiding this comment.
Classify expected audit failures before returning 500
When a POST contains malformed JSON, omits repo_url, supplies a non-string value, or names an inaccessible repository, parsing, lookup, validation, or cloning raises into this catch-all and the service reports HTTP 500. These are routine client or upstream failures, so treating them as internal faults encourages inappropriate retries and pollutes operational error rates; validate request bodies separately with 400/413 responses and map clone rejection or timeout to a stable 4xx/5xx response before reserving 500 for unexpected faults.
Useful? React with 👍 / 👎.
Adds the working Cloud Run audit service, container definition, and minimal readable web UI proven against public GitHub repositories.