the ever-watchful guardian — all-seeing dynamic security
A cloud-native, multi-target DAST orchestrator with a correlation brain.
Yubel runs the best open-source dynamic security engines — ZAP, Nuclei, Nikto, Wapiti, testssl.sh, sqlmap, dalfox, katana, schemathesis, graphql-cop, kube-hunter — against web apps, REST/GraphQL APIs, cloud assets, containers and Kubernetes, then does what no single scanner can: it correlates their output into corroborated findings and synthesizes multi-step attack chains. Runs installed (pip), in Docker, as a Kubernetes Job/CronJob, or in CI.
web · REST/GraphQL APIs · cloud · containers · Kubernetes | consensus · attack chains · systemic correlation · baseline diff · OWASP/CWE/MITRE
🛡️ Air-gapped by design
Yubel needs no LLM and no cloud to work. Its core makes zero outbound calls — it only ever talks to the targets you point it at — so results never leave your perimeter. Add
--offlineto also stop the underlying engines from reaching external services (no OAST/interactsh, no update checks). Runs fully inside regulated, isolated and on-prem networks where AI-driven tools simply can't go. Deterministic, reproducible, auditable — the same scan yields the same result, every time.
Why Yubel
No single scanner is best at everything. ZAP excels at deep authenticated web crawls; Nuclei is unbeatable for templated checks and fast fuzzing; testssl.sh owns TLS; kube-hunter is the way to dynamically pentest a cluster; schemathesis tears apart OpenAPI contracts. Real programs already run several of these — badly glued together with bespoke scripts, incompatible reports and no shared severity model.
Yubel is the glue, done properly:
- One config, many engines. Describe your targets once; Yubel routes each to every engine that can handle it.
- One normalized model. Every engine's output becomes a
Findingon a shared 5-level severity scale, then duplicates across engines are merged (and credited). - One set of reports. JSON, a self-contained HTML dashboard, Markdown, and SARIF for GitHub code scanning — from the same run.
- Runs anywhere. Laptop, Docker, Kubernetes Job/CronJob, or a CI pipeline with a real fail-gate.
- Honest and resilient. A missing engine is skipped, not fatal. One engine crashing never kills the scan. Every execution is recorded.
Yubel does not reinvent scanning — it stands on the shoulders of the OSS ecosystem catalogued in docs/LANDSCAPE.md (382 tools surveyed).
What makes Yubel different
Most tools stop at "run scanner, print findings". Because Yubel sees every engine's output for a target at once, it adds an analysis layer that single scanners structurally cannot:
- Cross-engine consensus. A finding independently reported by two or more engines is flagged corroborated and its confidence is upgraded — cutting false positives by using the engines as a jury, deterministically (no probabilistic LLM validator). Duplicates merge, keeping the worst severity and crediting every reporter.
- Attack-chain synthesis. Yubel recognizes when separate findings combine into a real exploitation path and promotes it to its own high-impact finding — 13 rules today, e.g. SSRF + cloud host → IMDS credential theft, XSS + non-HttpOnly cookie → account takeover, anonymous K8s API + exposed kubelet → cluster takeover, JWT alg=none + admin → auth bypass, request smuggling + cache poisoning. No isolated scanner reports these.
- Systemic correlation across targets. When the same weakness class shows up on two or more targets, Yubel raises a single systemic finding — "fix centrally, resolve everywhere". A tool that sees one app at a time structurally cannot.
- "Why we believe this" evidence trail. Every finding carries a deterministic, reproducible rationale (which engines, corroboration, taxonomy, risk) — auditable reasoning instead of a model's guess.
- Baseline diff. Point
--baselineat a prior run and every finding is tagged new / existing / regressed, with fixed issues tracked separately.--fail-on-newgates CI on newly introduced risk only, so a backlog never blocks a pipeline. - Taxonomy + risk scoring. Every finding is mapped to OWASP Top 10 (2021), OWASP API Top 10 (2023), CWE and MITRE ATT&CK, given a composite 0–100 risk score, and rolled up into a per-target A–F grade and an OWASP coverage matrix.
- Noise clustering. 200 "missing header" hits collapse into one finding with an instance count, so real issues aren't buried.
Yubel is the conductor, not another instrument — it can even orchestrate an AI-agent scanner as just one more engine, while its own core stays deterministic and offline. See a rendered report: docs/sample-report.html.
Quick start
pip install yubel # core (orchestrator + reporters + analysis)
yubel setup # detect & install the scanning engines (one command)
yubel setup --install # actually install the missing ones (brew/pip)
yubel engines # see what's registered and available here
yubel selftest # synthetic run — validates the pipeline, no network
yubel scan -t https://example.com --fail-on high -o report/
Don't want to install anything? The Docker image below bundles every engine.
Prefer everything bundled? The Docker image ships the orchestrator and the engines:
docker run --rm -v "$PWD/out:/out" ghcr.io/ggeorgeazevedo/yubel:latest \
scan -t https://example.com -o /out
open out/yubel.html
Targets it understands
| Type | Example | Engines routed to it |
|---|---|---|
web |
app, portal, dashboard, SPA | zap, nuclei, wapiti, nikto, dalfox, testssl, katana, httpx |
api |
REST + OpenAPI/Swagger | zap (api-scan), nuclei, schemathesis, wapiti, testssl |
graphql |
GraphQL endpoint | graphw00f, graphql-cop, zap, schemathesis |
cloud |
external/attack-surface asset | httpx, katana, nuclei, testssl |
kubernetes |
cluster (remote/internal/pod) | kube-hunter, nuclei (via ingress) |
container / host |
exposed service | httpx, nuclei, nikto, testssl |
A real config
yubel init # writes a documented yubel.yaml
yubel scan -c yubel.yaml -o report/
parallelism: 4
fail_on: high # CI gate: exit non-zero if any finding >= high
targets:
- { name: shop, type: web, url: https://shop.example.com,
auth: { kind: bearer, token: ${SHOP_TOKEN} } }
- { name: payments, type: api, url: https://api.example.com,
openapi: https://api.example.com/openapi.json }
- { name: graph, type: graphql, url: https://api.example.com/graphql }
- { name: prod-cluster, type: kubernetes, host: 10.0.0.10, k8s_mode: remote }
output: { dir: report, formats: [json, html, markdown], sarif: true }
Secrets stay out of git via ${ENV_VAR} expansion.
Deploy
Kubernetes (Job / CronJob)
kubectl apply -f deploy/k8s/configmap.yaml
kubectl apply -f deploy/k8s/job.yaml
kubectl logs -f job/yubel-scan
Or with Helm (supports mode: job|cronjob, runs non-root, read-only rootfs):
helm install yubel deploy/helm/yubel \
--set mode=cronjob --set schedule="0 2 * * *"
For an internal cluster pentest, run the Job in-cluster with k8s_mode: pod — kube-hunter then probes the control plane and kubelets from inside the pod network, exactly as a compromised workload would.
GitHub Actions (with SARIF upload)
- uses: ggeorgeazevedo/yubel@v0
with:
target: https://staging.example.com
fail-on: high
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: yubel-report/yubel.sarif }
A ready-made workflow lives in .github/workflows/dast.yml.
Baseline / trend scanning
yubel scan -c yubel.yaml -o report/ # run 1
cp report/yubel.json baseline.json
# ... later, in CI ...
yubel scan -c yubel.yaml --baseline baseline.json \
--fail-on critical --fail-on-new -o report/ # fails only on NEW criticals
Reports
yubel.html— editorial, self-contained assessment report: executive summary with an A–F risk grade, an attack-paths section, an OWASP Top 10 coverage matrix, and risk-scored findings with corroboration and taxonomy chips. Works offline, light/dark, print-friendly.yubel.sarif— SARIF 2.1.0 (withsecurity-severityfrom the risk score + OWASP/MITRE tags) → GitHub Security → Code scanning.yubel.json— full machine-readable result: findings, enrichment, per-engine run log and the baseline diff.yubel.md— Markdown summary (exec summary, attack paths, OWASP coverage, findings) for PRs/tickets.
Pipeline
target ─▶ registry.select_for(target) ─▶ [engines for this type, minus deny-list,
minus intrusive/opt-in]
─▶ ThreadPool (bounded by parallelism)
─▶ each engine: build_command → run in temp workdir → parse → [Finding]
─▶ collect ─▶ dedupe (merge cross-engine dups, keep worst severity)
─▶ ANALYSIS ── taxonomy (OWASP/CWE/MITRE) + risk score
├─ consensus (corroboration → confidence uplift)
├─ cluster noise (info/low repetition → 1 finding)
├─ attack-chain synthesis (composite findings)
└─ baseline diff (new / existing / regressed / fixed)
─▶ reporters (json/html/md/sarif) ─▶ fail-gate exit code
Intrusive engines (currently sqlmap) are off by default and only run when you pass --include-intrusive or name them explicitly with -e sqlmap.
Adding an engine
- Subclass
Engineinsrc/yubel/engines/, declaringname,supports,binary. - Implement
build_command()andparse()(return normalizedFindings). - Register it in
engines/registry.py.
That's it — availability probing, target routing, CLI listing, parallelism and reporting all come for free. See engines/nuclei.py for a compact reference and CONTRIBUTING.md.
Releasing (how the badges go green)
The badges light up automatically once the repo is on GitHub:
- CI / CodeQL — run on every push/PR (green after the first successful run).
- License / Stars — populate as soon as the repo is public.
- Release / PyPI / Docker — populate when you cut a release:
git tag v0.3.0 && git push origin v0.3.0
That tag triggers release.yml (builds the wheel, publishes to PyPI via
Trusted Publishing, and creates a GitHub Release) and docker.yml (pushes
the image to ghcr.io). One-time setup: on PyPI add a Trusted Publisher for
this repo (Project → Publishing → GitHub) — no API token needed. Your repo's
Community Standards checklist (Insights → Community Standards) is already
100%: README, LICENSE, CODE_OF_CONDUCT, CONTRIBUTING, SECURITY, issue templates
and a PR template are all present.
Architecture
See docs/ARCHITECTURE.md for the full design and docs/LANDSCAPE.md for the surveyed ecosystem.
⚠️ Legal & ethical use
DAST is active testing: it sends real attack traffic. Only scan systems you own or are explicitly authorized to test. Intrusive engines can modify data. You are responsible for your use of Yubel. See SECURITY.md.
License
Apache-2.0. Yubel orchestrates third-party engines that carry their own licenses (GPL, Apache, MIT, proprietary, …) — installing or bundling them is your responsibility; see docs/LANDSCAPE.md for each tool's license.
Release files for yubel 0.5.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| yubel-0.5.7.tar.gz | 70.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| yubel-0.5.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 146.4 kB
Release files / yubel-0.5.7.tar.gz
| Download URL | yubel-0.5.7.tar.gz |
|---|---|
| Size | 70.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bbdc8baf901d0827c46e25cf2e3e313d8414f36975a66fbc081b1322784bc7c2
|
|
BLAKE2b-256 checksum How to use checksums |
803a9852651928e596e018c2f84d9b712399e557fdef73cb1a6fd7acd3c22354
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 29, 2026.
Transparency logRelease files / yubel-0.5.7-py3-none-any.whl
| Download URL | yubel-0.5.7-py3-none-any.whl |
|---|---|
| Size | 75.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3c999333b3bd21100789d49953c9a61682d2589aea9d8cb436d13f9016e40427
|
|
BLAKE2b-256 checksum How to use checksums |
430a2c78bed893c35067374299a63cceb1f7886c8a444a31d4d76b879db2d5b2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 29, 2026.
Transparency log