Understanding the `common/runner` Directory in Tencent/AI-Infra-Guard: The Core Scanning Engine

TLDR: The common/runner directory in Tencent/AI-Infra-Guard is the central execution engine that orchestrates the entire infrastructure scanning pipeline — from target discovery and HTTP probing to fingerprint matching, vulnerability advisory lookup, and security score calculation.

The Tencent/AI-Infra-Guard repository is an open-source infrastructure security scanner designed to detect AI-related assets, fingerprint their technologies, and correlate them with known vulnerabilities. At the heart of this system lies the common/runner package, which acts as the orchestration layer that ties together every other component in the tool. This article breaks down what this directory does, how its internal functions work, and how you can use the Runner type in your own infrastructure security workflows.

What Is the common/runner Package?

The common/runner package is a self-contained Go module that implements the complete lifecycle of a security scan. It is not merely a utility helper — it is the conductor that assembles all subsystems, manages the flow of data, and produces actionable output.

The package contains two primary files:

According to the source code, the Runner struct created by New() sequentially initializes storage, processes targets, builds the fingerprint engine, and loads the vulnerability database before enumeration begins.

How the Runner Constructs a Scan

The New() function is the entry point. It takes an options.Options struct and returns a configured *Runner ready for execution. The initialization order is deliberate:

  1. initStorage — sets up hybrid (in-memory + disk) map storage
  2. processTargets — normalizes and expands user-supplied targets
  3. initComponents — builds the HTTP client, rate limiter, and engines
  4. initFingerprints — loads and parses fingerprint templates
  5. initVulnerabilityDB — loads advisory templates for CVE correlation
// Minimal setup to construct a Runner
opts := &options.Options{
    Target:       []string{"http://127.0.0.1:8080", "https://example.com"},
    RateLimit:    20,
    TimeOut:      10,
    FPTemplates:  "data/fingerprints",   // path to fingerprint YAMLs
    AdvTemplates: "data/vuln",           // path to advisory YAMLs
}
r, err := runner.New(opts)
if err != nil {
    panic(err)
}
defer r.Close()

Role 1: Target Processing and Expansion

The processTargets function handles the crucial job of converting raw user input into a normalized set of hosts to scan. Users can supply:

  • Direct URLs (e.g., http://188.9.21.3:8080)
  • CIDR blocks (e.g., 192.168.1.0/24)
  • Files containing multiple hosts
  • Local open ports (scanned directly on the machine)

Each target is expanded and stored in a hybrid map that supports both IP-based and domain-based lookup. This design enables the subsequent fingerprint engine to efficiently group results by host.

Target Probing: HTTP and HTTPS Requests

Once targets are prepared, the runner performs network-level detection through two dedicated functions:

  • runHostRequest — probes a host at the given port (used for infrastructure-level enumeration)
  • runDomainRequest — issues requests to full URLs directly

Both share the same httpx.HTTPX client, applying rate limiting and timeout constraints supplied in Options. The raw response is forwarded to extractContent for deeper analysis.

Response Extraction and Fingerprint Matching

The extractContent function is where the magic happens. It processes each HTTP response by:

  1. Extracting the page title
  2. Determining the HTTP status code
  3. Computing a favicon hash (used to identify platforms via their icon)
  4. Running the fingerprint engine (fpEngine.RunFpReqs) against the response
  5. Querying the advisory engine (advEngine.GetAdvisories) for matching CVEs
// Example: examine all matched fingerprints and associated CVEs
opts := &options.Options{
    FPTemplates:  "data/fingerprints",
    AdvTemplates: "data/vuln",
}
r, _ := runner.New(opts)
defer r.Close()

// Print a summary of all fingerprints and their vulnerabilities
r.ShowFpAndVulList(true)

This design means each scanned host produces a rich HttpResult struct that captures the protocol, title, status, fingerprint names, and advisory data.

Result Aggregation and Output Handling

The handleOutput function is the tail end of the pipeline. It writes results to an optional output file, prints formatted tables to the console using the gotable library, and invokes any user-supplied callback function (for custom processing). This is how the CLI and web UI receive their data.

Security Scoring

A standout feature of the runner is the CalcSecScore function (located at common/runner/runner.go#L702-L730), which converts advisory severity into a single numeric score:

Severity Deduction
Critical / High -70
Medium -30
Low -10

The score is clamped to the 0-100 range, giving administrators an at-a-glance risk assessment for each target.

Graceful Cleanup and Resource Management

The Close method releases the HTTP client connection pool and flushes hybrid storage. It is designed to be safe for multiple invocations, making the runner usable in both one-off CLI commands and long-running service contexts.

How the Runner Fits Into AI-Infra-Guard

The runner is leveraged by virtually every other component of the project:

  • CLI interface — builds an Options from command-line flags and calls RunEnumeration
  • Web UI — exposes scan results through the aggregated HttpResult channel
  • Agent deployments — utilize Close and RunEnumeration in scheduled security checks

Summary

Here is a quick recap of the key takeaways:

  • The common/runner directory is the orchestration engine for all scanning activity in AI-Infra-Guard.
  • It handles target expansion, HTTP/HTTPS probing, fingerprint matching, advisory correlation, and scoring within a single call to RunEnumeration().
  • The package exposes a clean Runner API (New, RunEnumeration, Close) that CLI, web, and agent layers consume.
  • Security scores are derived deterministically from severity levels, enabling programmatic remediation workflows.

Frequently Asked Questions

What kind of targets can the common/runner package scan?

The runner accepts direct URLs, IP addresses, CIDR blocks, and local ports. It also supports reading targets from a file, and each input is normalized into a hybrid map for fast lookup during fingerprint matching.

How does the fingerprint engine work inside the runner?

The initFingerprints function loads YAML template files on startup, parses them with parser.InitFingerPrintFromData, and builds a preloaded match runner. During a scan, each HTTP response is fed through fpEngine.RunFpReqs, which compares headers, body features, and favicon hashes to identify products.

How does AI-Infra-Guard calculate the security score?

The CalcSecScore function starts at a score of 100 and deducts points for each found advisory: 70 for critical or high severity, 30 for medium, and 10 for low. The final score is clamped to a 0–100 range for consistent interpretation.

Can I use the runner programmatically in my own Go project?

Yes. The runner exposes a minimal API through runner.New(opts), RunEnumeration(), and Close(). You can provide your own options.Options with custom target lists, rate limits, fingerprint template paths, and advisory template paths.

How are the formatting and output handled?

After scanning, handleOutput aggregates the results and chooses between console tables, a file write, and callback execution depending on the configuration. The ShowFpAndVulList(true) method prints a matrix of fingerprints with their matching CVEs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →