Skip to content

clawosiris/rust-gvm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

200 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rust-gvm

⚠️ Prototype / Experimental This project is purely a prototype to experiment with the concepts of GVM + Rust and agentic engineering. It is not production-ready, not endorsed by Greenbone, and not intended as a replacement for python-gvm. APIs, architecture, and scope may change without notice.

Rust client library for the Greenbone Management Protocol (GMP) — a reimplementation of python-gvm with type safety, async-first design, and a programmable mock server for testing.

CI Security

Release and nightly builds publish CycloneDX 1.5 SBOMs generated by cargo-cyclonedx; the JSON artifacts are post-processed in CI to add CC0 document licensing, build lifecycle metadata, and supplier hints before sbomqs scoring and packaging. CPEs are intentionally left untouched unless a reliable package mapping is available.

Note

Releases are managed via the release-orchestrator. To create a nightly/alpha build, create an alpha release in the orchestrator. See RELEASING.md for details.

Overview

rust-gvm provides everything needed to talk to Greenbone Vulnerability Manager (gvmd) — from low-level XML framing to high-level typed commands — plus a standalone mock server that speaks the GMP protocol over Unix sockets or TCP, enabling integration testing without a real Greenbone instance.

Crate Structure

Crate Purpose Status
gvm-protocol Sans-I/O XML framing, command builder, response parser ✅ Implemented
gvm-mock-server Programmable mock GMP server (4 modes, fault injection) ✅ Implemented
gvm-connection Transport layer (Unix socket, SSH) ✅ Unix + SSH done
gvm-gmp Typed GMP command builders per version (22.4–22.8+) ✅ Implemented
gvm-client High-level async client with version negotiation ✅ Implemented
┌─────────────────────────────────┐
│         gvm-client              │  High-level API, version negotiation
├─────────────────────────────────┤
│         gvm-gmp                 │  Typed commands per GMP version
├─────────────────────────────────┤
│       gvm-protocol              │  Sans-I/O XML framing + response parsing
├─────────────────────────────────┤
│      gvm-connection             │  Unix socket / TLS / SSH transports
└─────────────────────────────────┘

  gvm-mock-server  ← Standalone mock for testing any GMP client

Quick Start

Client — Connect to gvmd

The high-level client handles version negotiation automatically and exposes typed convenience methods for every GMP domain:

use gvm_client::{GmpClient, GvmError};
use gvm_connection::{UnixSocketConfig, UnixSocketConnection};
use gvm_gmp::commands::targets::CreateTargetOpts;
use gvm_gmp::commands::tasks::CreateTaskOpts;

#[tokio::main]
async fn main() -> Result<(), GvmError> {
    // 1. Create a transport and connect — auto-negotiates GMP version (22.4–22.7+)
    let conn = UnixSocketConnection::new(UnixSocketConfig::new("/run/gvmd/gvmd.sock"));
    let mut client = GmpClient::connect(conn).await?;
    println!("Connected, GMP version: {}", client.version());

    // 2. Authenticate
    client.authenticate("admin", "admin").await?;

    // 3. Create a target — typed response, no manual XML parsing
    let target = client.create_target("My Target", CreateTargetOpts {
        hosts: vec!["192.168.1.0/24".to_string()],
        ..Default::default()
    }).await?;
    println!("Created target: {}", target.id);

    // 4. List all targets
    let targets = client.get_targets(Default::default()).await?;
    for t in &targets.items {
        println!("  {} — {}", t.meta.id, t.meta.name);
    }

    // 5. Create and start a scan task
    let config_id = "daba56c8-73ec-11df-a475-002264764cea".parse().unwrap();
    let scanner_id = "08b69003-5fc2-4037-a479-93b440211c73".parse().unwrap();
    let task = client.create_task(
        "My Scan", &config_id, &target.id, &scanner_id,
        CreateTaskOpts::default(),
    ).await?;
    client.start_task(&task.id).await?;
    println!("Started task: {}", task.id);

    client.disconnect().await?;
    Ok(())
}

Raw API (send/call)

For full control you can use the underlying send() / call() methods directly with command builders from gvm-gmp:

use gvm_gmp::commands::{authentication, targets};

// call() raises GvmError::Server on non-2xx; send() returns the raw Response
client.call(authentication::authenticate("admin", "admin")).await?;
let response = client.call(targets::get_targets(Default::default())).await?;
println!("Raw XML: {} bytes", response.data().len());

Version-aware client

Use GmpVersioned when you need to branch on the server's GMP version:

use gvm_client::GmpVersioned;
use gvm_connection::{UnixSocketConfig, UnixSocketConnection};

let conn = UnixSocketConnection::new(UnixSocketConfig::new("/run/gvmd/gvmd.sock"));
let mut client = GmpVersioned::connect(conn).await?;

match &client {
    GmpVersioned::V225(_) => println!("GMP 22.5"),
    GmpVersioned::V226(_) => println!("GMP 22.6"),
    _ => println!("Other version: {}", client.version()),
}

// All versions share the same send/call API
client.call(authentication::authenticate("admin", "admin")).await?;

SSH transport

Connect to a remote gvmd over SSH tunnel:

use gvm_client::GmpClient;
use gvm_connection::{SshConfig, SshAuth, SshConnection};

let config = SshConfig::new("scanner.example.com", "gvm", SshAuth::Agent)
    .with_port(22)
    .with_remote_socket("/run/gvmd/gvmd.sock");
let conn = SshConnection::new(config);

let mut client = GmpClient::connect(conn).await?;
// Same API as Unix socket — connect/call/disconnect

Mock Server (library usage)

use gvm_mock_server::{MockGmpServer, ServerMode, GmpVersion};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let server = MockGmpServer::builder()
        .mode(ServerMode::Stateful)
        .version(GmpVersion::V22_5)
        .credentials("admin", "admin")
        .unix_socket_auto()
        .build()
        .await?;

    println!("Mock GMP server listening at: {:?}", server.socket_path());
    // Connect with any GMP client (rust-gvm, python-gvm, etc.)

    server.shutdown().await;
    Ok(())
}

Mock Server (standalone binary)

# Start a stateful mock server on a Unix socket
gvm-mock-server --mode stateful --version 22.5 --socket /tmp/gvmd.sock

# Or on TCP
gvm-mock-server --mode stateful --version 22.5 --tcp 127.0.0.1:9390

Then connect with python-gvm:

from gvm.connections import UnixSocketConnection
from gvm.protocols.gmp import GMP
from gvm.transforms import EtreeCheckCommandTransform

conn = UnixSocketConnection(path="/tmp/gvmd.sock")
with GMP(connection=conn, transform=EtreeCheckCommandTransform()) as gmp:
    gmp.authenticate("admin", "admin")
    targets = gmp.get_targets()
    print(targets)

Mock Server

The mock server is the most developed component. It's designed to be a drop-in test server for any GMP client library — Rust, Python, Go, or shell scripts.

Server Modes

Mode Description
Echo Returns well-formed <command_response status="200"/> for any recognized command
Fixture Returns pre-built realistic GMP XML from a fixture library (90+ commands)
Stateful Full in-memory CRUD with authentication, task lifecycle, and resource relationships
Scenario Plays back scripted command→response sequences (strict or lenient matching)

Features

  • 4 server modes — from simple echo to full stateful CRUD
  • Unix socket + TCP listeners — compatible with python-gvm's UnixSocketConnection and TLSConnection
  • Per-session authentication — supports python-gvm's two-connection flow (version probe + auth session)
  • Task lifecycle — New → Running → Stopped → Done state machine
  • Fault injection — disconnect, delay, malformed XML, error codes, truncated responses
  • Scenario playback — deterministic scripted sequences for regression tests
  • Command history — inspect what the server received after tests
  • Pre-seeding — populate the store before tests via builder API
  • Template substitution{{uuid}}, {{now}}, {{version}} in fixture responses
  • Resource filtering — basic GMP filter string support (name=foo status=Running)

Validated Against

The mock server is validated against python-gvm in CI, exercising the full protocol flow: version negotiation, authentication, target/task CRUD, notes lifecycle, and cleanup.

Connection Crate

gvm-connection provides async transport implementations behind the GvmConnection trait:

use gvm_connection::{GvmConnection, UnixSocketConfig, UnixSocketConnection};

let config = UnixSocketConfig::new("/run/gvmd/gvmd.sock");
let mut conn = UnixSocketConnection::new(config);

conn.connect().await?;
conn.send(b"<get_version/>").await?;
let response_bytes = conn.read().await?;  // Uses XmlReader for frame detection
conn.disconnect().await?;

Transports

Transport Status Feature Flag
Unix socket ✅ Implemented unix (default)
SSH tunnel ✅ Implemented ssh
TLS over TCP 📋 Planned tls

Both transports support the full python-gvm reconnect pattern (connect → get_version → disconnect → reconnect → authenticate → commands) and implement the GvmConnection trait, so they're interchangeable with GmpClient.

Protocol Crate

gvm-protocol provides the transport-agnostic building blocks:

  • XmlCommand — Builder for GMP XML commands with attributes, child elements, and text content
  • Response — Parser for GMP XML responses (status codes, child text extraction, id extraction)
  • XmlReader — Streaming XML completeness detector for framing GMP messages over byte streams

Implementation Status

See docs/STATUS.md for detailed implementation status of each crate and GMP command coverage.

Building

# Build everything
cargo build --workspace

# Run all tests (630+ tests)
cargo test --workspace

# Run python-gvm integration tests
make test-integration

# Build the mock server binary
cargo build --release -p gvm-mock-server

Requirements

  • Rust 1.75+ (MSRV)
  • Python 3.10+ with python-gvm (for integration tests only)

CI/CD

Workflow Trigger What it does
CI Push/PR to main Format, clippy, test, doc, deny, coverage, MSRV, python-gvm integration
Nightly Daily 04:00 UTC + manual Full CI + cross-platform binary builds (5 targets)
Release v* tag push Full test → cross-platform builds → GHCR mock-server image → GitHub Release with checksums

Pre-built Binaries

Download from GitHub Releases or the rolling nightly pre-release.

Platform Binary
Linux x86_64 (glibc) gvm-mock-server-linux-amd64.tar.gz
Linux x86_64 (musl, static) gvm-mock-server-linux-amd64-musl.tar.gz
Linux ARM64 gvm-mock-server-linux-arm64.tar.gz
macOS x86_64 gvm-mock-server-macos-amd64.tar.gz
macOS ARM64 gvm-mock-server-macos-arm64.tar.gz

GHCR Image

Tagged releases also publish a container image at ghcr.io/clawosiris/gvm-mock-server:<tag>.

See docs/mock-server-consumption.md for downstream CI usage guidance.

Specs

Design specifications live in spec/:

License

AGPL-3.0-or-later — matching the Greenbone ecosystem.

Copyright 2026 Greenbone AG

Related Projects

About

Rust client library for the Greenbone Management Protocol (GMP)

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages