Strategic Framework
October 2026 · Vincent Verdet

The Security Onion: Nine Layers of Automation for an Open Source Project

How a small open source team can let automation carry most of its security work, and where people still have to decide.

11 min read

An open source project with one or two maintainers cannot review its way to security. Every pull request, base image and dependency update is a chance to ship something that should not ship, and nobody has the hours to inspect each one by hand. What a small team can do is stack automated checks in layers, so that each layer catches some of what the one before it missed.

This article describes nine such layers, the way Turbo EA, an open source enterprise architecture platform, runs them today. For each layer it names the tools, when they run and whether a finding blocks the change or only reports it. It then sets out the guardrails that keep the automation honest and the decisions that stay with people. Most of the tools are open source, and GitHub’s code scanning, secret scanning and Dependabot are free for public repositories.

Why an onion

Security people have long used the onion as a picture of defence in depth. No single layer is expected to stop everything, and an attacker has to get through all of them. For a software project, the picture works best when you read it as the path of a change.

A change begins at the centre, as a request to write some code. On its way out it crosses every ring: the rules built into the code base, the checks before a merge, the build that signs it, the scanners that inspect it, a test against the running application, the dependency updates around it, the protections inside the running product and, at the edge, the practices of the team that operates it. Each ring has its own trigger and its own tools, and none of them depends on a person remembering to run it.

09Your part08In the app07Updates06Pen test05Scanning04Signed releases03Merge checks02Built-in rules01The brief
  1. 1Security from the first request
  2. 2Rules built into the code
  3. 3Checks before every merge
  4. 4Signed releases
  5. 5Vulnerability scanning
  6. 6Automated penetration test
  7. 7Up-to-date dependencies
  8. 8Protection inside the application
  9. 9The operator’s part
The nine layers of the Turbo EA security onion, from the first request at the centre to the operator’s practice at the edge. The interactive version on the security page lists every safeguard per layer and shows when each one acts.
Key takeaway

Every layer should answer three questions in writing: what it stops, when it runs, and whether a finding blocks the change or only reports it.

The nine layers

Each layer below explains what it is there to stop, which tools run it in Turbo EA, and what happens when it finds something.

1Security from the first request

Turbo EA is written with an AI coding assistant, Claude Code, so the first place to put security is the request itself. Each change starts as a written brief that says who may use the feature, what data it touches and who must never see it, and the plan is reviewed before any code is written.

The assistant also reads a standing file of project rules at the start of every session. In Turbo EA that file, CLAUDE.md, requires permission checks on every endpoint, reads included, validated input everywhere, no hand-written SQL and encrypted credentials. A rule that lives in a file survives the next contributor, whether that contributor is a person or a model. A pull-request template then repeats the most important items as a checklist, so a skipped box stays visible to the reviewer.

  • ToolsA project rules file, a pull-request template
  • RunsWhile the change is written
  • ResultShapes the change; a person reviews the plan

2Rules built into the code

Rules on paper are a start, but the ones that matter most should fail a build when they are broken. Turbo EA turns its access model into tests. Every action that changes data checks the person’s permissions, and a single visibility rule decides which cards a role may see. A static test scans the API package for any module that returns cards without applying that rule, so a new endpoint cannot quietly forget it.

Before a change is even committed, pre-commit hooks format and lint the code and run Gitleaks over the staged changes to catch passwords and keys. Credentials that the application stores at runtime, such as single sign-on and email secrets, are encrypted in the database rather than kept in plain text.

  • ToolsGuard tests, pre-commit hooks, Gitleaks
  • RunsOn every commit and in CI
  • ResultA broken rule fails the test

3Checks before every merge

The pull request is the natural gate for an open source project, because every change has to pass through it. Turbo EA runs four kinds of checks there.

  • GitHub code scanning with CodeQL analyses the Python and TypeScript code and the GitHub Actions workflows themselves, on pull requests and again every week. It runs in GitHub’s default setup, so there is no workflow file to maintain.
  • Gitleaks scans the full commit range of every pull request and every push to the main branch for secrets. It is deliberately not limited to certain paths, so no change can skip it.
  • pip-audit and npm audit check the production dependencies of the backend and the frontend against public vulnerability databases whenever a change touches them. A known vulnerability fails the check.
  • The test suite and an API contract check confirm that the change still does what the code base promises.

A small GitHub Actions workflow also closes pull requests from accounts outside a short allowlist. For a project that accepts few outside contributions, this removes a whole class of hostile pull requests before any check runs.

  • ToolsGitHub CodeQL, Gitleaks, pip-audit, npm audit, the test suite
  • RunsOn every pull request, every push to main and weekly
  • ResultA failing check marks the pull request as not ready

4Signed releases

Users of a self-hosted product install whatever the project publishes, so the release itself needs proof of origin. Turbo EA publishes five container images and a Helm chart to GitHub Container Registry and signs each of them with Sigstore cosign. The signing is keyless: the GitHub Actions job receives a short-lived certificate tied to its own workflow and Git reference, and the signature is recorded in Sigstore’s public Rekor transparency log. There is no long-lived signing key to store, rotate or leak.

Right after signing, the job verifies the signature with the oldest cosign version that the documentation promises to support, using a strict certificate identity. Every image also carries a software bill of materials (SBOM) in SPDX format and a build provenance record, both generated by Docker BuildKit, so an operator can see what is inside and how it was built. Every GitHub Action in the pipeline is pinned to a full commit SHA, and the application containers run as a non-root user with all extra Linux capabilities dropped.

  • ToolsSigstore cosign with GitHub OIDC, BuildKit SBOM and provenance
  • RunsOn every merge to main and every release
  • ResultA signature the documented client cannot verify fails the publish

5Vulnerability scanning

An image that is clean today can carry a known vulnerability tomorrow, because new advisories are published every day. Turbo EA therefore scans its images when they are built and keeps scanning them afterwards.

At publish time, Trivy from Aqua Security and Docker Scout check each image against their own vulnerability databases. High and critical findings are uploaded to the GitHub Security tab, and a critical vulnerability that already has a fix fails the run. Every morning, a scheduled job scans the published images again. When it finds a high or critical vulnerability for which a fix exists, it starts a rebuild of all five images on its own. Every Monday the images are also rebuilt without cache, which pulls in the latest operating system patches even when no scanner has asked for them.

  • ToolsTrivy, Docker Scout, the GitHub Security tab
  • RunsAt publish, every day and every week
  • ResultA fixable critical finding fails the build, and a fixable finding in a published image triggers a rebuild

6Automated penetration test

Code and image scanners look at what was built. They do not look at the application while it runs, with its real web server, headers and cookies. Turbo EA closes that gap with a weekly automated penetration test.

Every Tuesday, a workflow starts the published images with the unchanged Docker Compose file in production mode, loads sample data and runs the OWASP ZAP baseline scan against it twice: once as an anonymous visitor and once signed in as an administrator. ZAP explores every page and API route it can reach and reports missing or weak security headers, unsafe cookie settings and information leaks. The scan is passive, so it observes and never changes data. Any alert that is not listed in a rules file with a written reason fails the run. The first runs already led to concrete fixes, such as hiding the web server version and adding a frame-ancestors directive to the Content Security Policy.

  • ToolsOWASP ZAP baseline, Docker Compose
  • RunsEvery week and on demand
  • ResultAn unreviewed alert fails the run

7Up-to-date dependencies

Most of the code in a modern application was written by someone else. Keeping it current has to run on a schedule, because nobody remembers to do it by hand.

Dependabot raises an alert and proposes a fix when a security advisory affects a dependency, and every week it proposes grouped updates for the pinned base images. Once a month it also refreshes the commit pins of the GitHub Actions in a single grouped pull request. Some components are invisible to the bots. In Turbo EA these are the embedded editors for diagrams, process models and data grids, which a scheduled script bumps once a month. Every update arrives as a pull request, so it passes through the merge checks and the scanners like any other change.

  • ToolsDependabot, a scheduled bump script
  • RunsEvery week and every month
  • ResultOpens pull requests; the merge checks decide

8Protection inside the application

Some protection has to ship inside the product, because it must hold in every installation without anyone configuring it. In Turbo EA, the session lives in a cookie that page scripts cannot read, passwords are stored as bcrypt hashes, and five failed sign-ins lock an account for 15 minutes. In production mode the backend refuses to start with the example secret key from its documentation.

Every page is served with a strict Content Security Policy and other browser security headers. Uploaded files are checked by their actual content rather than by the name or type the browser reports. Extensions only install with a valid vendor signature, and AI assistants connected through the Model Context Protocol preview every change before it is saved.

  • ToolsThe application’s own secure defaults
  • RunsAlways, in every installation
  • ResultOn by default, with nothing to configure

9The operator’s part

A self-hosted product is only as safe as the way it is run, so the outer ring belongs to the team that operates it. The documentation asks operators to verify the image signatures with cosign before deploying, to install a pinned release rather than the moving latest tag, to back up before every upgrade and to generate a long random secret key.

It also recommends keeping Turbo EA off the open internet altogether. The application needs no inbound access from outside, so the best practice is to let only approved corporate devices reach it, through a VPN or a zero-trust access gateway, and to sign people in through the company’s identity provider, where multi-factor authentication is enforced.

  • Toolscosign verify, a VPN or zero-trust gateway, the identity provider
  • RunsAt deployment and in operation
  • ResultThe operator decides

When each layer runs

Timing matters as much as coverage. A layer that only runs at release cannot catch a secret in a pull request, and a layer that only runs at merge cannot notice a vulnerability published next month. The matrix shows when each Turbo EA layer is at work, from the moment a change is written to the moment it runs in production.

LayerWhile writingBefore savingIn reviewOn mergeOn releaseEvery dayEvery weekIn production
1The briefacts
2Built-in rulesactsactsacts
3Merge checksactsactsacts
4Signed releasesactsacts
5Scanningactsactsactsacts
6Pen testacts
7Updatesacts
8In the appacts
9Your partacts
A dot marks a moment at which a layer acts. The daily and weekly columns run without any change at all.

Those last two columns deserve attention. The daily scan and the weekly rebuild, penetration test and dependency updates protect a release after it has shipped. A project that only scans on merge stops looking at its releases the moment they leave the pipeline.

Guardrails that keep the automation honest

Automation that nobody trusts gets switched off. These eight guardrails keep Turbo EA’s pipeline useful rather than noisy.

1Observe broadly, gate narrowly

Send every finding to one place and block only on the subset you are sure about. Turbo EA’s scanners upload high and critical findings to the GitHub Security tab, but only a critical finding with an available fix fails the build. A gate that fires on every advisory teaches people to ignore it.

2Give every exception a reason and a review date

Accepting a finding is sometimes right, for example when a vulnerable function is never reached. Every accepted finding in Turbo EA’s allowlists carries a written reason, secret-scan exceptions match an exact value in an exact file rather than a whole directory, and the lists are re-read every quarter so that an exception disappears once a fix exists.

3Pin everything and let bots move the pins

Every GitHub Action is pinned to a commit SHA and every base image to a tag, so nothing changes underneath the build without a pull request. Dependabot then moves those pins on a schedule, and the merge checks judge each move.

4Close the loop without waiting for a person

When the daily scan finds a fixable high or critical vulnerability in a published image, it starts the rebuild itself. The fix can ship before anyone has read the alert.

5Verify the promise you make to users

Signing an image proves little if users cannot verify it. Turbo EA checks each signature with the oldest cosign version its documentation supports, and a test fails if the workflows and the documentation name different versions.

6Test the configuration, not only the code

Many security properties live in configuration files rather than in code. Turbo EA has tests that read the repository from disk and check that every web server location keeps the security headers, that upload limits agree between the web server and the application, and that the list of signed images matches the images the compose file can pull.

7Give the pipeline least privilege

The pipeline is an attack surface of its own. Each workflow job declares only the permissions it needs, and the workflow that inspects pull requests from outside accounts never checks out their code.

8Say plainly what blocks and what only reports

A check that looks like a gate but is not one creates false confidence. Turbo EA’s security pipeline document states which checks fail a change and which only report, and its security page describes written policies as practice rather than as enforced gates.

What automation leaves to people

Automation narrows the work that needs a person, but it does not remove it. Four things stay with people in Turbo EA, on purpose.

  • Second factors. Turbo EA has no built-in multi-factor authentication. People sign in through the company’s identity provider, which is where a second factor belongs and is enforced.
  • Attack-mode testing. The weekly penetration test observes and never sends harmful input. Active scanning needs a throwaway environment of its own, and a manual penetration test needs a person.
  • The operator’s network. The project’s checks cover its published images in their default setup. Each operator’s servers, proxy and identity provider are theirs to secure and to scan.
  • Judgement on plans and exceptions. A person still reads the plan before code is written, decides whether an advisory really affects the product and re-reads the exceptions every quarter.

A starter kit for your own project

An open source project on GitHub does not need all nine layers on day one. The table lists what Turbo EA uses per layer, when it runs and what a finding does. A sensible order is to start with the merge checks and signed releases, and then add the daily scan and the weekly penetration test.

LayerWhat Turbo EA usesWhen it runsWhat a finding does
1Security from the first requestA project rules file for the AI assistant, a pull-request templateWhile a change is writtenA person reviews the plan
2Rules built into the codeGuard tests, pre-commit hooks, GitleaksEvery commit and every CI runA broken rule fails the test
3Checks before every mergeGitHub CodeQL, Gitleaks, pip-audit, npm audit, the test suiteEvery pull request, every push to main, weeklyA failing check marks the change as not ready
4Signed releasesSigstore cosign, BuildKit SBOM and provenance, SHA-pinned ActionsEvery merge to main and every releaseAn unverifiable signature fails the publish
5Vulnerability scanningTrivy, Docker Scout, the GitHub Security tabAt publish, daily, weekly rebuildA fixable critical finding fails the build
6Automated penetration testOWASP ZAP baseline against the published stackWeekly and on demandAn unreviewed alert fails the run
7Up-to-date dependenciesDependabot, a scheduled bump scriptWeekly and monthlyOpens pull requests; the merge checks decide
8Protection inside the applicationThe application’s own secure defaultsAlways, in every installationOn by default
9The operator’s partcosign verify, a VPN or zero-trust gateway, the identity providerAt deployment and in operationThe operator decides
Where to start

Turn on GitHub code scanning in its default setup, add Gitleaks to your pull-request workflow and pin every GitHub Action to a commit SHA. None of the three needs new infrastructure, and together they close some of the most common gaps in an open source pipeline.

Turbo EA’s security page shows the same onion interactively, with every safeguard per layer, and the security pipeline document in the repository describes each workflow in technical detail. Both are public, so you can copy whatever fits your project.

Comments

Which layer is missing from your own pipeline, or which one was hardest to automate? Share what you see.