2 min read

Working Code Is Not Safe Code

AI-assisted coding makes it easy to ship small services quickly. That makes basic security hygiene part of the builder's job.
Working Code Is Not Safe Code

AI agents make it easy to create a working service.

They do not make it obvious whether that service is safe enough to deploy.

That gap is where security work starts.

Not movie hacking. Not enterprise ceremony. The practical version: secrets, dependencies, request limits, headers, lockfiles, input validation, build paths, and deployment assumptions.

The lesson from my latest security pass was simple:

Working code is not automatically safe code.

What AI Makes Easy to Miss

Generated code usually optimizes for the prompt in front of it.

  • make the feature work
  • fix the build
  • add the endpoint
  • wire the integration
  • move on

Security depends on the wider system.

Where do secrets live? Which packages are installed during deploy? Does rendered Markdown become executable HTML? Can a slow client hold a server open? Does CI trust whatever SSH host key it sees first?

Those questions rarely show up in a first prompt.

AI-assisted code can be functional while still missing critical security controls. The model gives you what you asked for. It does not automatically know the operating context.

What I Changed

The fixes were boring. That is the point.

On static sites:

  • committed lockfiles
  • deploys with npm ci
  • pinned supported runtime versions
  • security headers
  • textContent instead of innerHTML for form responses

On build scripts:

  • no production npx
  • local binaries
  • validated path inputs before resolving directories

On server routes:

  • sanitized Markdown
  • safer request parsing
  • email normalization
  • request size limits
  • simple rate limiting
  • fail-closed behavior when required secrets are missing

On redirect and edge code:

  • server timeouts
  • capped request headers
  • stricter redirect target validation
  • pinned SSH host keys instead of runtime ssh-keyscan

None of this is advanced security research.

Small services usually fail in boring ways: drifting dependencies, unsafe HTML rendering, oversized requests, loose redirects, dynamic deploy trust, or secrets in the wrong place.

AI makes those misses easier because it lowers the cost of adding one more thing.

My Minimum Security Floor

Before a small AI-built service goes public, I want this baseline:

  • Deterministic installs. Commit lockfiles. Use npm ci.
  • Pinned runtimes. Know what runs in production.
  • No runtime magic. Avoid production npx.
  • Patched dependencies. Run the boring scanners and tests.
  • Hostile input by default. Treat forms, route params, Markdown, API responses, and generated HTML as untrusted.
  • Request limits. Add body limits, rate limits, timeouts, and header caps.
  • Browser guardrails. Use CSP, nosniff, frame restrictions, and referrer policy.
  • Boring secrets. No secrets in repos or frontend bundles.
  • Pinned deploy trust. Do not collect SSH host keys dynamically in CI.
  • Narrow agent access. Give agents only the files, tools, and credentials needed for the task.

This checklist is not complete. It is a floor.

But a floor matters when the new default is "ship before lunch."

The goal is not to stop building quickly. The goal is to make the fast path safer than the lazy path.