I got tired of pasting the same scanner block into yet another repo.

It always started small. “Can we add Trivy here?” Sure. Semgrep next week. Secrets the week after. Then repo A and repo B quietly drifted apart—one job wrote into a folder git clean would erase, another uploaded an empty {} to DefectDojo like that counted as diligence. I wasn’t managing security. I was managing copies of the same bad mood.

So I did the unglamorous thing. Frontend (SvelteKit / Node / TypeScript) and backend (Go / Gin) stayed where they were. The scans moved into one vulnerability-management pipeline: clone the targets, run the tools in Docker, keep the mess under /tmp, upload only reports that actually exist. Schedule it. Web-trigger it when someone is nervous. Don’t make every app repo wear a Halloween costume of CI jobs.

That part felt like winning.

Building it was mostly swearing at YAML

I didn’t start with a architecture diagram. I started with Ctrl+V.

Paste a job. Change a path. Pipeline red—for permissions, for a missing lockfile, for a scanner that wrote somewhere git would tidy up, for an upload script that treated placeholder JSON as a finding. Paste again. Hate myself a little. Eventually the pattern stuck: shallow clones, read-only mounts, allow_failure because I wanted evidence more than a fake green check, artifacts hanging around for a week so I could download the corpse of a failed scan.

flowchart LR
  trigger[Schedule / web] --> pipe[One scan pipeline]
  pipe --> fe[Frontend clone]
  pipe --> be[Backend clone]
  fe --> tools[Too many tools]
  be --> tools
  tools --> dojo[DefectDojo]
  tools --> arts[Artifacts]

Frontend lane got the Node-shaped stuff: npm audit when there’s a lockfile, Semgrep (security + secrets), OSV, Gitleaks, Trivy on the filesystem, Bearer, Syft/Grype, Checkov. Backend got Gosec, govulncheck, Semgrep for Go, the same secret and dependency circus, Trivy again, Bearer, SBOM tools, Checkov. Optional image scans if someone actually set an image name—otherwise that job stays home. I’m not failing a pipeline because nobody shipped today.

I counted them once. Twenty-two scanner jobs, give or take how you group Syft with Grype. It looked serious on the pipeline graph. DefectDojo started filling up. I told myself this was what “coverage” felt like.

Then a pentester opened the app

Not the JSON. The app.

They clicked around. They tried the boring attacks scanners promise to catch and the awkward ones that live between tickets—auth edges, business logic, “why is this endpoint polite to strangers,” the stuff that doesn’t always light up a SARIF file. Some findings overlapped what the pipeline already yelled about. Plenty didn’t.

That’s the part that screwed me, honestly. Not in a dramatic breach way—in a quiet, sit-with-your-coffee way. I had built a machine that produces receipts. A pentester produces judgment. Twenty-two jobs will enumerate packages and patterns until the runner gets tired. They will not feel embarrassed for you when a flow is technically “clean” in the report and still wrong in the product.

I still keep the pipeline. I’m not throwing Trivy out the window because a human was better at being curious. But I stopped treating the green-or-not-quite-green stage view like a force field.

What I actually learned

Scattered scanners feel busy until you need one timeline. Centralizing was the right fix for my pain—the copy-paste, the empty uploads, the “which repo has the real Semgrep config?” mystery.

allow_failure is fine when the goal is collecting evidence. Empty reports should not get a VIP pass into DefectDojo. Put workdirs where CI cleanup can’t eat them. Use tools that match the stack instead of one hammer for every language.

And the punchline I didn’t want: volume of scanners ≠ done. The pipeline is how I stop forgetting to look. The pentester is how I find out looking wasn’t enough.

Then both piles land on the same desk.

DefectDojo tickets. SARIF noise. “please check this dependency.” Pentest write-ups with screenshots that make your stomach drop. Twenty-two automated jobs plus one curious human, and somehow the developers inherit the whole circus—patch this, explain that, reopen the PR, ship the fix before someone asks why the scanner count looked impressive while the app still had holes.

So yeah. Centralize the scans. Keep the pipeline. Hire the pentester. Just don’t pretend the story ends at the green check. In the end, all of this fucks the developers—they’re the ones who have to turn other people’s findings into working code again.

And this site? No scanners. Come get it.

Funny part: this website doesn’t run that pipeline. No twenty-two jobs. No DefectDojo dashboard glowing at midnight. No scheduled Trivy hugging my Hugo build. albanna.id is just a blog and a notulensi toy I shipped because I was annoyed—not a fortress with a coverage report.

So to every hacker reading this instead of sleeping: fuck it. Hack this web. Break something cute. Find the dumb mistake. If you actually get in, tell me like an adult—I’ll fix it, maybe write another post, and the developers (hi, that’s still me) get fucked again. Same story, smaller blast radius.