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

> Explore the common/runner directory in Tencent/AI-Infra-Guard, the core scanning engine. Discover how it orchestrates infrastructure scanning from target discovery to vulnerability assessment and security scoring.

- Repository: [Tencent/AI-Infra-Guard](https://github.com/tencent/AI-Infra-Guard)
- Tags: internals
- Published: 2026-08-22

---

**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](https://github.com/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:

- [`common/runner/runner.go`](https://github.com/Tencent/AI-Infra-Guard/blob/main/common/runner/runner.go) — the full `Runner` implementation
- [`common/runner/runner_test.go`](https://github.com/Tencent/AI-Infra-Guard/blob/main/common/runner/runner_test.go) — table-driven tests validating the runner's behavior

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

```go
// 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

```go
// 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.