The 1-Hour Audit: How I Walk a Contractor-Built Stack on Day One
Contractor-built applications fail in the same five places, in the same five ways, every time. Here is the seven-step walk I do in an hour, and the severity rule I hand the founder at the end.
The 1-Hour Audit: How I Walk a Contractor-Built Stack on Day One
The stake: when a founder calls me about an inherited application, the first thing I sell them is an hour of my time. Not a week. Not a fixed engagement. Just an hour, on a screen-share, walking through their stack in a specific order.
The reason an hour is enough is that contractor-built applications fail in the same five places, in the same five ways, every time. By the end of sixty minutes you know which findings are P0 (active exposure), which are P1 (real risk, not on fire), and which are P2 (structural, plan around them). Enough to write a one-page severity report. Enough to know whether the right next step is "rotate credentials this weekend" or "we can plan a proper migration over the next quarter."
This is the order I walk the stack, and what I'm looking for at each step. If you want to do it yourself before paying me, use the self-audit checklist — same ground, worksheet form. If you want me to do it, the CTA is at the bottom.
Step 1 — Registrar and DNS (5 minutes)
Outside the application first. whois yourdomain.com. Then dig yourdomain.com and dig api.yourdomain.com.
- Who is the registrant? If it's the contractor's personal name, the contractor's company, or a name you've never heard of — P0. The domain is the front door and you don't have the key.
- What nameservers serve the zone? If the contractor controls them, the contractor can repoint your domain anywhere, swap your TLS certs, intercept your traffic. P0.
- Where do your hostnames resolve? If
app.yourdomain.comresolves to a VPS IP — Hostinger, DigitalOcean, Linode, anything that's not a cloud-provider load balancer — that VPS is your front door. The contractor likely owns it. P0.
By the end of step 1 I know whether the production traffic path is in your control or theirs. Usually the most important finding of the hour.
Step 2 — Source control and CI (10 minutes)
I ask for the GitHub/GitLab org. I check who's on it. I check the deploy pipeline.
- Who has push access to
main? If the contractor still does, P0. - Is there a CI/CD pipeline? If deploys happen from someone's laptop, P1 (see post #2).
- Where do secrets live?
.envfiles committed to the repo, P0. Environment variables in CI with no audit trail, P1.
Step 3 — Cloud project IAM (10 minutes)
gcloud projects get-iam-policy <project-id> --format=json. Or the equivalent in AWS/Azure.
- Who has Owner / Editor? If the contractor's email is still there, P0.
- Are there service account keys?
.jsonfiles exported and emailed, P0. Workload identity bound to a Cloud Run service, fine. - Default compute SA with
roles/editor? P1 — the blast radius of any compromised workload is the entire project.
Step 4 — Cloud Run (or equivalent compute) (10 minutes)
gcloud run services list. For each: gcloud run services describe.
ingress. If it'sINGRESS_TRAFFIC_ALL(the default), the service is publicly reachable on its*.run.appURL regardless of whatever proxy is in front. P0. The "security perimeter" is a vanity URL.--allow-unauthenticated. If yes, no IAM check on invocation. Combined with the above, anyone on the internet can hit the API directly. P0.- Environment variables. Look for plaintext secrets — DB password, JWT secret, encryption key, webhook keys. P1. Even out of the repo, they're readable by anyone with project-viewer IAM.
- Service account. What identity does the service run as? Default compute SA with
roles/editor, P1.
I also curl the *.run.app URL directly. 200 = the service really is publicly invokable. Confirms the IAM finding.
Step 5 — Database (5 minutes)
gcloud sql instances list. gcloud sql instances describe on prod.
- Public or private IP?
ipv4Enabled: true, P0. The database is reachable from the internet. authorizedNetworks? P1 — allowlists drift, contractor IPs sit there forever.- TLS mode.
requireSsl: false, P1.requireSsl: truebut the application usesrejectUnauthorized: false— also P1, TLS happens but identity isn't verified. - Automated backups. Off, P0. On, P2 — confirm point-in-time recovery is also on.
Step 6 — Integrations (10 minutes)
If the application talks to Twilio, Stripe, SendGrid, IMAP — anything with webhooks — go check:
- Webhook signature validation. Read the handlers. If any of them have signature validation gated on an env var that's set to
falsein production, P0. - Where do webhooks POST? Look in the provider's console. If the URL is the contractor's VPS, the contractor sees every webhook. P0.
- Who owns the provider account? Billing email is the contractor's? Transferring the account during decommission is a separate two-week project. Track as P1 even though it's not technical.
Step 7 — Quick application read-through (10 minutes)
I open the main controllers — particularly controllers/auth*, controllers/user*, anything dealing with login or registration. I scan for:
console.log(req.body)or similar on auth routes. P0. Passwords in your logs (see post #8).- JWT in
localStorageon the frontend. P1. XSS-readable session tokens. - In-process anything — IMAP listeners, cron jobs, file-system uploads. P2 (structural, but it's the reason you can't horizontally scale).
- Migrations on first request. P1 — every cold start carries deadlock risk.
The output
By the end of the hour I have a list of findings, grouped by severity. The deliverable is a one-page report — usually about 20 bullets — that the founder can hand to anyone (their lawyer, their board, the contractor) without explanation.
The decision rule I give the founder:
- 5+ P0 findings. Active incident state. Don't refactor anything. Rotate credentials this weekend. Take back the front door this week. Engage someone to plan the migration.
- 2–4 P0 findings. At least one will become a story before the end of the year. Plan a one-week stabilization sprint this month.
- 0–1 P0 findings, lots of P1s. Healthy under current load, fragile under any change. Plan an infrastructure migration this quarter.
- 0–1 P0, few P1s. Reasonable shape. Address what you have on a normal cadence.
- 0 P0, 0 P1. Confirm with an engineer. Either you're doing well or the answers were generous.
In a year of running this audit, exactly one stack came back in the bottom tier. The other dozen had at least two P0s. The contractor pattern is consistent.
What this turns into
The hour is the qualifier. From there:
- Clean report. You don't need me. Save it, re-run in 90 days, move on.
- P0s but small application. $5k written report + 4-week migration plan an in-house engineer can execute.
- P0s and mission-critical. 6–10 week done-for-you migration on the pattern documented across this series.
What this costs you if you skip it
You inherit an application without knowing where it's exposed. You find out one finding at a time, after the fact, usually during an incident. The hour is the cheapest insurance you'll buy.
If your stack matches what I described in any of the eleven prior posts and you want a real audit on it instead of a self-graded one — I do four of these a month. $5k flat, takes a week, viewer-only IAM access, ends with a 20-page report. Optional readout call. If the result is "you're fine," I tell you that and refund half.
I'd rather you self-audit. The checklist is free.
Run the audit → /audit-checklist
Book the paid version → /contact
Run the audit on your own stack
A 30-question self-audit. P0/P1/P2 severity. Takes about an hour.
Open the checklist →