Skip to content

Add public Cloud Run repository audit service - #6

Merged
erinepshovel-code merged 3 commits into
mainfrom
cloud-run-service
Sep 13, 2026
Merged

Add public Cloud Run repository audit service#6
erinepshovel-code merged 3 commits into
mainfrom
cloud-run-service

Conversation

@erinepshovel-code

Copy link
Copy Markdown
Contributor

Adds the working Cloud Run audit service, container definition, and minimal readable web UI proven against public GitHub repositories.

@erinepshovel-code
erinepshovel-code merged commit 4e8e532 into main Sep 13, 2026
6 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-13T22:14:30.121615Z 02980cc PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread index.html
Comment on lines +54 to +56
<p>${f.claim}</p>
<div class="meta">
Evidence: ${f.evidence}<br>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread service.py
check=True,
timeout=120,
)
result = audit_path(target)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread service.py
with tempfile.TemporaryDirectory() as tmp:
target = os.path.join(tmp, "repo")
subprocess.run(
["git", "clone", "--depth=1", repo_url, target],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread service.py
Comment on lines +58 to +61
ThreadingHTTPServer(
("0.0.0.0", int(os.environ.get("PORT", "8080"))),
Handler,
).serve_forever()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread Dockerfile
FROM python:3.11-slim
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY . .

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread index.html
})
});

const r = await response.json();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread service.py
Comment on lines +54 to +55
except Exception as exc:
self.reply(500, {"error": str(exc)})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant