This document describes how to report a security vulnerability in OXware and what to expect after you report it.
Only the latest minor release receives security patches.
| Version | Status |
|---|---|
| 2.7.x | Supported (current) |
| 2.6.x | Supported |
| 2.5.x | End of life 2026-09-01 |
| 2.4.x | End of life |
| older | End of life |
If you are running an unsupported version, upgrade before reporting an issue. We will not produce patches for end-of-life branches.
Do not open a public GitHub issue for a security vulnerability.
Send a report to root@oxware.top.
If you prefer encrypted email, use our PGP key. The current fingerprint is:
PGP fingerprint: TBD (will be published before 2.7.1)
Include in your report:
- A description of the vulnerability.
- A minimal proof of concept, if you have one.
- The affected version (
oxware --version). - Your name or handle if you would like to be credited.
You may also submit privately through GitHub Security Advisories: https://github.com/ShinnAsukha/oxware-hypervisor/security/advisories/new
- Acknowledgement within 72 hours of receipt.
- Initial triage and severity assessment within 7 days.
- Patch for critical issues within 14 days.
- Patch for high-severity issues within 30 days.
- Patch for medium and low severity within 90 days.
These are targets, not guarantees. We will keep you informed if a fix takes longer.
We follow coordinated disclosure with a 90-day deadline.
- We will work with you on a fix and a disclosure date.
- If we are unable to release a fix within 90 days, you may publish your findings. We ask that you tell us first.
- After a fix is released we publish a GitHub Security Advisory and note the
issue in
CHANGELOG.md.
We credit researchers who report vulnerabilities responsibly.
| Researcher | Issue | Year |
|---|---|---|
| (placeholder) | (placeholder) | (placeholder) |
If you want to be listed, say so in your report.
Published advisories are listed at: https://github.com/ShinnAsukha/oxware-hypervisor/security/advisories
The following hardening was issued in the 2.7.0 release:
- SEC-009 — OAuth2 token leak via URL: callback now responds with a tiny
HTML bridge page that stashes the JWT in
sessionStorageand immediately callshistory.replaceState("/", …). The token never appears in the URL (query or fragment), the Referer header, the browser back-button history, or proxy access logs. The bridge page itself isCache-Control: no-store, Referrer-Policy: no-referrer. CI has a regression check (?oauth2_token=patterns are now a hard build failure). - SEC-010 — Setup endpoint takeover:
/api/setup/initnow returns 403 for any non-loopbackrequest.remote_addr. A fresh node bound to a public interface can no longer be claimed by whoever reaches port 8006 first; the first admin must be created from the host itself (over SSH or the serial console) viacurl -X POST http://127.0.0.1:8006/api/setup/init. Override (development only):OXWARE_SETUP_ALLOW_REMOTE=1, which is logged at WARN level every time it triggers. - SEC-011 — High-risk feature default-off:
feature_registrynow defines aHIGH_RISK_FEATURESset (plugin_sdk, marketplace, container_runtime, os_branding, oxupdate, bare_metal, cloud_burst, kubevirt, gitops, k8s_operator, k8s_csi, federation). These features are shipped disabled regardless ofstatus. An operator must explicitly enable each one via the Settings → Features panel or theoxctl feature enable <id>command, which writes an audit-log entry. - SEC-012 — Token storage migration to sessionStorage: the panel now
reads tokens from
sessionStoragefirst, falling back tolocalStorageonly for password-login sessions that pre-date this release. Logout clears both storages. A cookie-based HttpOnly/Secure/SameSite session is planned for v2.8 (large refactor — tracked separately). - SEC-014 — HttpOnly cookie session: login responses (
/api/auth/login,/api/auth/2fa/verify-login,/api/setup/init, OAuth2 callback) now attach the JWT in anHttpOnly; Secure; SameSite=Strictcookie (oxware_access) alongside the JSON response. A readable double-submit CSRF cookie (oxware_csrf) is issued at the same time; the panel echoes it in theX-CSRF-TOKENheader on every state-changing call. JS cannot read the access cookie, so an XSS payload cannot exfiltrate the session. Legacy Bearer header is still accepted for backward compatibility with out-of-panel clients. New/api/auth/logoutendpoint unsets the cookies and revokes the session record. - SEC-015 — SSO fail-closed:
sso_manager.oidc_handle_callbackandsso_manager.saml_process_acsnow refuse to trust an assertion unless the appropriate signature-verification library is installed (python-josefor OIDC,python3-samlfor SAML). With the library present, OIDC ID tokens are verified against the issuer's JWKS, with audience and issuer claims enforced. Without it, the callback returns an error instead of silently decoding base64. Dev override:OXWARE_ALLOW_UNVERIFIED_SSO=1, WARN-logged on every callback.sso_manager.crypto_status()lets the panel surface a clear banner. - SEC-016 — Modularization seed: a new
bp_v270.pyFlask Blueprint hosts the v2.7 Confidential VM / Runbook / Federation endpoints under/api/v2/...instead of growingapp.py. The blueprint imports nothing fromapp.py; dependencies (decorators, response helpers) are wired viainit_bp_v270(...). This is the first concrete step toward the larger app.py split flagged in the external review. - SEC-013 — CI hardening: the GitHub Actions pipeline now hard-fails
on (a) any module that does not compile, (b) Flake8 critical errors
(E9/F63/F7/F82) in
app.py, (c) Bandit findings at HIGH severity, (d) duplicate Flask routes or endpoint functions inapp.py, and (e) regression of the token-in-URL pattern. Style nits, mypy hints, and shellcheck warnings remain informational by design.
The following patches were issued in the 2.7.2 release:
- SEC-029 — Safe archive extraction (HIGH): All
tarfile.extractall()andzipfile.extractall()call sites inapp.pynow route throughsecurity_utils.safe_tar_extract/safe_zip_extract, which reject any member that is an absolute path, contains a..parent reference, points at a device file, or is a symlink/hardlink that escapes the destination directory. On Python 3.12+ we also applytarfile's built-indatafilter for setuid/setgid stripping. Replaces the unguarded extracts that Bandit flagged as B202. - SEC-030 — DNS rebinding mitigation (MEDIUM): New
security_utils.resolve_safe_host()performs a single DNS resolve, runs the result through the SSRF block list, and returns the literal IP for the caller to connect to. Outbound federation, runbook, and SSO callers now bind to the resolved IP so a rebinding response between resolve and connect cannot redirect the connection. - SEC-031 — FTP backup hardening (MEDIUM, B321/B402):
backup_schedulerno longer unconditionally importsftplib. Plaintext-FTP upload is gated byOXWARE_ENABLE_INSECURE_FTP=1. When unset,_upload_ftplogs a warning and returns immediately; operators are pointed at SFTP. CI baseline drops the--skip B321,B402exemption. - SEC-032 — SSH known-hosts + first-contact UI (MEDIUM, B507): New
ssh_known_hostsmodule replaces theparamiko.AutoAddPolicypattern with a persistent/var/lib/oxware/known_hostsfile and a queue of pending fingerprint approvals visible in the panel (Security → SSH Known Hosts). Trust-on-first-use is opt-in viaOXWARE_SSH_TOFU=1during migration. Backup-target SFTP is wired through this policy as the canonical example. - SEC-033 — pip-audit informational baseline (LOW):
make securityrunspip-audit -r requirements.txt; advisories with a clean upgrade path are tracked in CHANGELOG. CI artifact preserved every run.
The following patches were issued in the 2.7.1 release:
- SEC-017 — Runbook api_call SSRF (CRITICAL):
runbook_executor._run_stepnow passes everyapi_callstep URL throughsecurity_utils.validate_external_urlbefore invokingurllib.request.urlopen. Private (RFC 1918), loopback, link-local, CGNAT, and IPv6 ULA/link-local ranges are rejected. The cloud metadata addresses (169.254.169.254, IPv6fd00:ec2::/32) and rawlocalhostare blocked. Operators who really need to call a loopback internal API from a runbook must opt-in per step withallow_loopback: true(the four defaultDEFAULT_RUNBOOKSare updated accordingly). Default scheme ishttps. - SEC-018 — Runbook vm_action argv injection (CRITICAL):
vm_idextracted fromctx["metric_key"]is validated against^[A-Za-z0-9._-]{1,128}$viasecurity_utils.validate_vm_idbefore being passed as avirshargv element. Theactionfield is also restricted to the libvirt verb allowlist (start,shutdown,reboot,destroy,suspend,resume).virshis invoked by absolute path (/usr/bin/virsh, fallback/usr/sbin/virsh) so a poisoned$PATHcannot redirect the call. - SEC-019 — Federation member URL SSRF + TLS bypass (HIGH):
cluster_federation.add_memberandcluster_federation.update_membernow validate URLs throughsecurity_utils.validate_external_url. The same private/loopback/link-local/metadata block list applies.verify_tls=Falseis silently coerced back toTrueunlessOXWARE_FEDERATION_ALLOW_INSECURE=1is set in the environment, which is for local-cluster testing only and produces a WARN log on every coercion. - SEC-020 — Federation forward path allowlist (MEDIUM):
cluster_federation.forwardandcluster_federation.bulk_actionnow validate thepathargument against an allowlist (/api/vms,/api/hosts,/api/alerts,/api/networks,/api/storage,/api/monitoring,/api/health). Auth, setup, internal admin, user management, and session paths are explicitly blocked. A federation admin on this node can no longer proxy a request to the remote member's/api/auth/*or/api/internal/*. - SEC-021 — Federation add_member URL pre-validation (MEDIUM):
bp_v270.api_fed_addpre-validates the URL before invokingadd_member, so a malformed URL returns a clean400instead of a 500 with a stack trace. - SEC-022 — Runbook shell allowlist + per-step rate limit (MEDIUM):
Runbook
shellsteps may only invoke binaries on a fixed allowlist (/usr/bin/virsh,/bin/systemctl,/usr/bin/nft,/usr/bin/journalctl, plus a few diagnostics). Every argv element is scanned for shell metacharacters viasecurity_utils.safe_subprocess_arg.api_callsteps are additionally capped at 120 invocations per runbook per hour to prevent a single runbook from being used as a request-flooder against an internal API. - SEC-023 — Force-run confirmation (MEDIUM):
POST /api/v2/runbooks/<id>/runwithforce: truenow requires aconfirm_tokenderived from the runbook id, a 60-second time bucket, and a server-side rotation key. The first force call returns409with the expected token; the second call (within 60 seconds) executes. This stops a stolen admin session from chain-running a single runbook past its quota. - SEC-024 — Plugin SDK AST sandbox hardening (MEDIUM): The plugin
validator now treats sandbox-escape patterns as errors instead of
warnings, including
eval/exec/__import__/compile,getattr/setattr/delattr/globals/locals/vars,os.system/os.popen/subprocess.run(shell=True),importlib/marshalserialization calls (anddill/cloudpicklevariants), and attribute chains through__class__/__mro__/__subclasses__/__bases__/__globals__/__builtins__. A plugin that hits any of these is rejected at upload and never written to/opt/oxware/plugins/<id>/plugin.py. - SEC-027 — Plugin route namespace enforcement (MEDIUM): Plugins are
no longer handed the live Flask app object. Instead they receive
plugin_sdk._PluginAppProxy, which only allowsapp.route(...)/app.add_url_rule(...)for paths under/plugins/<plugin_id>/*. A malicious or buggy plugin can no longer overwrite/api/auth/loginor any other core route. All other Flask attributes are read-only forwarded so the existing plugin API remains usable. - SEC-025 — Bulk-delete confirm token nonce (MEDIUM): The bulk-delete
confirm token is now a server-side random nonce (
secrets.token_urlsafe(24)) bound to the exact sorted VM-id set via HMAC-SHA256, single-use, five-minute expiry, and constant-time compared. The oldsha256(sorted_ids)[:16]token was deterministic and brute-forceable offline. - SEC-026 — Per-VM bulk audit (MEDIUM): Bulk deletions now write
one append-only audit line per VM to
/var/lib/oxware/bulk_audit.jsonlwithts,op,vm_id,ok,message, andrequesterfields. The single per-job summary inbulk_jobs.jsonis preserved for backwards compatibility, but per-VM traceability is now available for forensic review. - SEC-028 — confidential_vm CPUID read (LOW):
confidential_vm.detect_supportreads/proc/cpuinfodirectly viapathlib.Path.read_textinstead of spawningcat /proc/cpuinfothrough a subprocess. Removes one unnecessary external command from the hot path.
The following patches were issued in the 2.6.1 release:
- SEC-001: API keys are read from
/etc/oxware/oxware.conf(mode 0600) instead of process environment variables. - SEC-002: WebSocket authentication tokens are no longer passed in the URL query string; they are sent in the first frame after connect.
- SEC-003: JWT revocation list is consulted on every request, not only at refresh time.
- SEC-004: Audit log entries are hash-chained with SHA-256 so deletion or reordering is detectable.
- SEC-005: Login timing is equalized between unknown user and incorrect password to prevent user enumeration.
- SEC-006: Console session recordings are stored in a directory with mode
0700, owned by the
oxwareuser. - SEC-007: Storage endpoints resolve paths with
os.path.realpathand reject any path outside the configured root. - SEC-008: All state-changing endpoints require a double-submit CSRF token.
The 2.7 CI pipeline blocks new Bandit HIGH findings. The following
pre-existing items are tracked, explicitly skipped in CI for now, and will
be addressed in 2.8. They are visible in --skip B202,B321,B324,B402,B507
on the security audit job.
| ID | Where | Status | Plan |
|---|---|---|---|
| B202 | archive extract paths in app.py | Open | Add path-normalising filter (tarfile.data_filter) + reject absolute/.. members. |
| B321 | ftplib.FTP() in backup_scheduler |
Open | Move legacy FTP backup target behind feature flag = off and warn loudly. |
| B324 | RFC 6455 WebSocket handshake | Won't fix | SHA1 is mandated by the protocol; not a security choice. |
| B402 | import ftplib in backup_scheduler |
Open | Removed together with B321. |
| B507 | paramiko.AutoAddPolicy() |
Open | Add known-hosts pinning to the OXware credential vault + first-contact prompt. |
| dep-audit | pip-audit on requirements.txt | Open | Run informationally; bump pinned deps when an advisory has a clean upgrade path. Artifact uploaded every CI run. |
When an item lands, drop its skip from .github/workflows/ci.yml.
See the "Security" section of the README.md for the recommended
configuration of nginx, sudo, file permissions, and operating system
hardening.
OXware welcomes external penetration tests. The following are explicitly in scope:
- Authentication: JWT signing, refresh-token flow, 2FA bypass, account lockout evasion, session fixation, OAuth2/SAML/OIDC binding attacks.
- Authorization: RBAC bypass, vertical/horizontal privilege escalation, cross-tenant data leakage in multi-tenant deployments.
- API: input validation, mass-assignment, IDOR on
/api/vms/*,/api/storage/*,/api/network/*, SSRF in remote-fetch endpoints, RCE in webhook/plugin paths. - Web panel: stored/reflected XSS, DOM clobbering, CSRF on state-changing endpoints (double-submit token), CSP bypass, prototype pollution.
- Console: noVNC token replay, WebSocket auth, VNC password leakage.
- Storage: path traversal in datastore browser, symlink attacks, ISO upload smuggling.
- Network: nftables/microsegmentation rule bypass, east-west traffic between tenants.
- Plugin sandbox: sandbox escape, audit-log forgery, malicious plugin upload flow (note: catalog is maintainer-curated; no public upload exists).
- Cryptographic: weak random, hardcoded secrets, LUKS2 key handling, vault unseal flow.
- Infrastructure: container escape on the controller host, hypervisor escape via QEMU/KVM device emulation, libvirt API privilege boundaries.
- Use rate-limited credential testing; do not flood the lockout system.
- Avoid destructive operations against production data. Use the demo
environment at
pentest.oxware.topor your own self-hosted instance. - Report multi-step exploit chains with PoC scripts (Python preferred).
- Provide CVSS v3.1 vector and a short remediation suggestion.
The following findings are not eligible for a security advisory:
- Denial of service against publicly available test instances.
- Missing security headers on
/docs(Swagger UI) when the docs endpoint is enabled. - Self-XSS that requires a user to paste content into their own browser console.
- Missing best-practice cookie flags on a development server running in debug mode.
- Reports generated solely by automated scanners without a working proof of concept.
- Issues in dependencies for which an upstream advisory already exists.
- CSP
unsafe-inlinefor inline panel scripts (planned for nonce-based CSP in v2.8 — large refactor in progress). - Social engineering against OXware staff or community members.
- Physical attacks on hardware.
OXware does not currently offer a paid bug bounty.
If a bounty program is established in a future release, it will be announced on the project website and linked from this document.
- Security email:
root@oxware.top - General contact:
root@oxware.top - GitHub: https://github.com/ShinnAsukha/oxware-hypervisor