Security for a one-person estate

The attack surface of a small independent maintainer, and what closing it actually costs. Secrets that scrub themselves, dependencies you did not choose, and a cleanup tool that nearly deleted its own hands.

25 posts

Start here

The shape every finding turned out to have, and the graded report card that started it.

  1. I had the framework audited: every finding was the same shapeEvery finding in a security audit reduced to untrusted input reaching a powerful operation unchecked. One checked chokepoint per boundary fixes it. Pioneering
  2. OpenSSF Scorecard graded my supply chainRunning OpenSSF Scorecard on a Go project flagged mutable action tags, over-broad workflow token permissions, and a missing maintenance signal. Pioneering

Signing your releases

The deepest thread in here has its own route: sixteen posts, a seven-part tutorial, and the arguments underneath it.

Signing your releasesHow to prove a binary came from you, from a laptop to production.

Secrets and credentials

Where a secret lives, what happens to it in memory, and how it gets out anyway.

  1. Where should a CLI keep your API keys?Credential storage for a Go CLI: an env-var reference by default, an opt-in OS keychain, and plaintext only as a last resort and banned in CI. Pioneering
  2. Redacting the secret you didn't know was in the stringCatching secrets by shape rather than by name: patterns for provider key prefixes, URL userinfo and auth headers, plus a fuzzy fallback. Pioneering
  3. Secrets that scrub themselves from RAMStorage answers where a secret lives, not what happens to it in memory. Wrapping secrets so they redact in Debug and zero on drop. Pioneering
  4. The secret that wasn't on my branchA secret scanner failed a merge request over a test key and a documentation PEM that the change did not contain. Scoping a scan properly. Pioneering
  5. Who holds the client secret?When a desktop CLI posts on your behalf, the OAuth tutorial assumption of a server holding the client secret collapses. What replaces it. Pioneering
  6. No access keys in CIReplacing long-lived AWS access keys in CI with OIDC federation, so the pipeline mints a short-lived token instead of holding a secret. Pioneering

Dependencies, and the supply chain

Everything you did not write, and the scores that claim to tell you whether it is safe.

  1. Anything under an 8The national vulnerability database is buckling under defunding and volume, which changes what a severity score is actually worth. Soapbox
  2. The version pin you can't lowerA standard-library security fix forced a Go version bump, and then the release failed because the build image shipped an older toolchain. Pioneering
  3. Waivers with an expiry dateA vulnerability scanner is a one-day yes or no. Running cargo-deny as a standing policy gate instead, with waivers that expire on a date. Pioneering
  4. The blank import that keeps a dependency out of your binaryMaking OS-keychain support provably absent from a Go binary for regulated or air-gapped builds, using a registry and a blank import. Pioneering
  5. Routing security findings without the noiseRouting AWS GuardDuty and Security Hub findings so an alert still means something: forward high severity only, and drop the duplicates. Pioneering
  6. The security finding you must not fixA checkov finding you must suppress rather than fix: that KMS policy statement is the escape hatch that stops you locking yourself out for good. Pioneering

Hardening the estate

The AWS account that holds the keys, and the tooling that can take it all away again.

  1. Hardening the account that will hold the keysApplying a security baseline to a fresh AWS account: audit logging, config recording, threat detection and an operator role that is not root. Pioneering
  2. A state bucket that defends itselfAn OpenTofu state bucket that defends itself against corruption, deletion and its own operator, using lockfiles and prevent_destroy. Pioneering
  3. The security service I had to switch offA fresh AWS account cannot enable GuardDuty or Security Hub without a subscription, so the security baseline failed on the account it protects. Pioneering
  4. The cleanup tool that almost deleted its own handsReading an aws-nuke dry run: the screenfuls of red are harmless noise, and the real hazard is one quiet line in the middle of them. Pioneering

AI as an attack surface

What changes when part of your stack takes instructions from strangers.

  1. A configurable AI endpoint is an attack surfaceA user-settable AI base URL decides where your API key gets sent. Validating it to reject userinfo, non-HTTPS schemes and redirects. Pioneering
  2. The marketplace I had to defend from my own attack surfaceOpening a public marketplace for agent workflows meant publishing an attack surface, and then having to defend it from my own convenience. Pioneering
  3. Nobody told it toA model in an internal evaluation did something nobody asked it to do, and the interesting part is what that does and does not prove. Soapbox
  4. AI didn't kill curl's bug bounty. The bounty did.The cash prize for anything that looked like a finding was the accelerant, and AI only made plausible-looking reports free to produce. Soapbox

Untrusted input, and language-level safety

Two different answers to the same question: what stops a hostile input doing damage.

  1. The interpreter we forgot to sandboxHundreds of malicious package versions across three ecosystems, and the uncomfortable fact that installing one runs arbitrary code. Soapbox
  2. A framework that contains no unsafeWhy every shipping crate uses forbid(unsafe_code) rather than deny, which any module inside it can quietly override from the inside. Pioneering
  3. forbid means forbid, until linkme needs a wordforbid(unsafe_code) is absolute, which is a problem when a dependency emits a link_section the lint counts as unsafe. Where the line ends up. Pioneering

Where to next

Everything, newest first →