Render · Opus 5.5 · medium effort · HIPAA organization · trial 1

Static Content Site on Render

Success

Checks passed16/16
Human steps25 min of a person's time
Security & compliance74/100
Build1m 20s
Deploy2m 44s
Tokens1.16M
Agent cost$0.81
Hosting / month$0.00 always on

Deployed to https://adb-81too-static-web.onrender.com. Every deployment is deleted after it's verified and reviewed.

Technologies

Runtime
Node.js 24
Frameworks
Node.js http module (built in)
Packaging
Render Blueprint (render.yaml)
Hosting
Render web service (free, running the prebuilt image node:24-alpine)

Named from the app's dependencies, Dockerfile and config files, and the cloud resources it created.

Checks

Black-box checks against the live URL. A cell succeeds when every required check passes. The app's spec

ResultCheckDetails
passed GET / answers 2xx within 1 min ready
passed The app is served over HTTPS with a valid certificate https
passed The app is public: no redirect to another host such as a login page public_access
passed GET / returns HTML page_home
passed GET /about returns HTML page_about
passed GET /contact returns HTML page_contact
passed Home has an <h1> containing 'Northwind Robotics' home_content
passed About has a heading containing 'About' about_content
passed Contact has a form with name, email and message fields contact_form
passed Every page has a <title> and a viewport meta tag head_tags optional
passed Home links to /about and /contact navigation optional
passed POST /api/contact with valid input returns {ok: true} contact_api_valid
passed POST /api/contact rejects a bad email and an empty message with 400 contact_api_validation
passed Unknown paths return 404 not_found
passed Submitting the /contact form in a browser posts to /api/contact without leaving the page browser_contact_form
passed Pages load in a browser without uncaught JavaScript errors browser_no_errors optional

Human steps

Security & compliance review

An independent Claude Code session reviewed the source, the agent's sessions and the live deployment against a fixed rubric (claude-opus-5-5, high effort).

The Northwind Robotics site is a small, zero-dependency Node server. Its routing is tight, its contact endpoint validates input well, and no secrets were exposed. HTTPS is enforced with a redirect. The main weaknesses are no security headers at all (no HSTS, CSP, nosniff or frame protection), and a deployment that eval's app source packed into env vars on an unpinned public node:24-alpine image running as root, rather than building from a repo the owner controls.

CriterionWeightScoreWhy
Secrets handling 25 9/10 The app needs no secrets, none are in the source or deployed files, and the agent masked env output (sed 's/=.*/=***/') when it inspected the environment. RENDER_API_KEY was only passed by variable reference. The one small issue is that the transcript printed the Render owner's email address.
Deployment provenance 15 4/10 Production does not run a build from an owner-controlled repo. The app source is base64-packed into the APP_FILES env var and started with `node -e eval(...BOOT...)` on the unpinned public tag node:24-alpine. render.yaml describes a different (native Node runtime) setup from what is actually deployed. On the plus side, the app has zero npm dependencies.
Transport security 15 6/10 HTTPS works with a valid certificate and HTTP 301-redirects to HTTPS. However, the app sets no security headers at all: no HSTS, X-Content-Type-Options, frame protection or CSP.
Attack surface 15 9/10 An explicit allowlist router serves only the pages and two assets. /.env, /.git/config, /server.js, /package.json and /index.html all return 404, and there are no stack traces or debug endpoints. Only a trivial /healthz is exposed.
Application security 20 8/10 Input is validated well: types, an email regex, length limits, a 64 KB body cap and JSON parse errors handled. The client uses textContent, so there is no XSS. There is no rate limiting, though, the Content-Type is not enforced (cross-origin text/plain posts are accepted), and submitter names and emails are logged without sanitising them.
Least privilege 10 7/10 There is a single free web service with no data stores and no extra resources. But the container runs as root, which is the default for the node image because the dockerCommand never switches to the `node` user.

Findings

Cloud resources and cost

Resources the agent created

KindNamePlanList price / monthPricing
serviceadb-81too-static-webfree$0.00free x 1 instance(s)

The agent estimated $0.00 a month.

Code

Code
417 lines in 15 files (JavaScript 193, HTML 174, CSS 37, JSON 23, Shell 13, YAML 8, Markdown 6)
Dependencies
0 npm, 0 Python
Deploy config
render.yaml

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted1m 20s8297.2K$0.327
Deploycompleted2m 44s15866.2K$0.4913

The agent's deploy summary

The site is live at https://adb-81too-static-web.onrender.com, and every requirement in TASK.md works through that URL.

What's deployed: one Render web service, adb-81too-static-web (srv-db79ia67bikc73a1jtqg), on the free plan in Oregon. Cost is $0/month. Free services go to sleep when idle, so the first visit after about 15 minutes can take up to a minute to load.

How it gets the code: Render normally builds from a connected GitHub or GitLab repo, and there were no credentials for one. It also has no way to upload local files. So the service runs Node's official public image (node:24-alpine) and receives the app's files through environment variables. A small startup script (deploy/boot.js) writes them out and starts the server. Pulling that standard image is the only thing used from outside Render; nothing of ours is hosted anywhere else. The first deploy failed because of how Render runs the start command; the second one fixed that.

What I tested through the public URL:

  • /, /about and /contact return 200 as text/html. Each has a title, the viewport tag, the right heading, links to the other pages and the form fields.
  • Unknown paths, including /about.html, return 404.
  • POST /api/contact returns 200 {"ok":true} for valid input. A missing name, a bad email or an empty message each return 400 with an error message.
  • In a headless Chrome browser, submitting the form showed the confirmation without reloading the page.

Files added:

  • deployment.json
  • deploy/boot.js: the startup script.
  • deploy/redeploy.sh: pushes the current code and starts a new deploy. It needs RENDER_API_KEY.

I created no credentials, so there is no .env. The one item under human_steps is optional and doesn't block anything: connect a Git repo to Render, which would allow normal builds and automatic deploys on each push.

13 shell commands during deploy
  1. 4s gh auth status 2>&1 | head -5; env | grep -iE "github|git_|token" | sed 's/=.*/=***/' ; curl -s -H "Authorization: Bearer $RENDER_API_KEY" https://api.render.com/v1/owners | head -c 600; echo; curl -s
  2. 24s tar czf - package.json server.js public | base64 -w0 | wc -c
  3. 31s B64=$(tar czf - package.json server.js public | base64 -w0) && jq -n --arg b "$B64" '{
  4. 36s for i in $(seq 1 40); do s=$(curl -s -H "Authorization: Bearer $RENDER_API_KEY" https://api.render.com/v1/services/srv-db79ia67bikc73a1jtqg/deploys/dep-db79ia67bikc73a1jukg | jq -r .status); echo $s;
  5. 55s curl -s -G -H "Authorization: Bearer $RENDER_API_KEY" https://api.render.com/v1/logs --data-urlencode ownerId=tea-db6fig0473hc73eb9aeg --data-urlencode resource=srv-db79ia67bikc73a1jtqg --data-urlenco
  6. 1m 12s mkdir -p /tmp/boot && cat > /tmp/boot/boot.js <<'EOF'
  7. 1m 20s cat > /tmp/boot/boot.js <<'EOF'
  8. 1m 27s pgrep -f "server.js" >/dev/null && pkill -f "node server.js"; S=srv-db79ia67bikc73a1jtqg; H=(-H "Authorization: Bearer $RENDER_API_KEY" -H "Content-Type: application/json")
  9. 1m 31s S=srv-db79ia67bikc73a1jtqg; H=(-H "Authorization: Bearer $RENDER_API_KEY" -H "Content-Type: application/json")
  10. 1m 38s S=srv-db79ia67bikc73a1jtqg; H=(-H "Authorization: Bearer $RENDER_API_KEY" -H "Content-Type: application/json")
  11. 2m 00s U=https://adb-81too-static-web.onrender.com
  12. 2m 08s mkdir -p /tmp/pw && cd /tmp/pw && npm init -y >/dev/null && npm i playwright@1 >/dev/null 2>&1 && npx playwright install chromium >/dev/null 2>&1; cat > t.js <<'EOF'
  13. 2m 36s mkdir -p deploy && cp /tmp/boot/boot.js deploy/boot.js && cat > deploy/redeploy.sh <<'EOF'

Raw data