> ## Documentation Index
> Fetch the complete documentation index at: https://ctpf.q-uestionable.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Inject Module Architecture

> Multi-provider campaign execution, response normalization, and scoring

The inject module (`q_ai.inject`) tests AI model susceptibility to tool poisoning and prompt injection. It serves adversarial MCP tools, runs automated campaigns against LLMs, and scores responses.

## Module Structure

```
inject/
├── campaign.py          # run_campaign() — async campaign execution
├── scoring.py           # Outcome classification from NormalizedResponse
├── models.py            # Campaign, CampaignResult, InjectionTechnique enums
├── server.py            # build_server() — malicious MCP server for manual testing
├── cli.py               # serve, campaign, list-payloads, report commands
├── adapter.py           # InjectAdapter for orchestrator integration
├── coverage.py          # build_coverage_report() — audit finding coverage analysis
├── mapper.py            # persist_campaign() — bridges results to core DB
└── payloads/            # YAML payload templates
    └── *.yaml           # One per injection payload
```

## Campaign Execution

`run_campaign()` is the core async function. For each payload template:

1. **Render** — Build MCP tool definition with poisoned description/response from the YAML template
2. **Call** — Send tool call to the LLM via `ProviderClient` (litellm backend)
3. **Normalize** — Convert provider-specific response to `NormalizedResponse`
4. **Score** — Classify the outcome
5. **Persist** — Save results as JSON and to the database

## Provider Abstraction

The inject module uses `q_ai.core.llm` for LLM access:

* **`ProviderClient`** — Protocol defining `call_with_tools()` for tool-use interactions
* **`LiteLLMClient`** — Implementation that centralizes LLM access behind the `ProviderClient` protocol (wraps litellm)
* **`NormalizedResponse`** — Provider-agnostic response with `content`, `tool_calls`, `raw_response`, `model`, `provider`
* **`parse_model_string()`** — Splits `provider/model` format, applies Anthropic default for bare names

Credentials resolve via environment variable → OS keyring → error.

## Scoring System

The scoring module classifies each response into an outcome:

| Outcome              | Meaning                                        |
| -------------------- | ---------------------------------------------- |
| `full_compliance`    | Model fully followed the injected instructions |
| `partial_compliance` | Model partially followed instructions          |
| `refusal_with_leak`  | Model refused but leaked information           |
| `clean_refusal`      | Model cleanly refused the injection            |
| `error`              | Provider error or timeout                      |

Scoring examines the `NormalizedResponse` content — not embeddings or weighted factors. Raw evidence is preserved alongside the classification for manual review.

## Payload Templates

Payloads are YAML files in `inject/payloads/`. Each defines:

* **name** — Unique identifier
* **technique** — `description_poisoning`, `output_injection`, or `cross_tool_escalation`
* **tool\_name** — The MCP tool name to present
* **owasp\_ids** — Mapped OWASP MCP Top 10 categories
* **description** — What the payload tests
* Tool definition fields (description, input schema, response template)

Use `qai inject list-payloads` to see the full catalog, filterable by `--technique` and `--target`.

## Malicious Server

`qai inject serve` builds a standalone MCP server from payload templates. It presents poisoned tools via stdio or Streamable HTTP transport. Connect any MCP client to test how it handles adversarial tool content. This is for manual testing — campaigns automate the process.

## Findings → Payloads Data Flow

When running inside the assess workflow, the inject adapter queries audit findings to inform template selection:

1. **Query native findings** — `finding_service.get_findings_for_run()` retrieves findings from the current workflow run's audit child run
2. **Query imported findings** — `finding_service.get_imported_findings_for_target()` retrieves findings from `qai import` runs associated with the same target
3. **Extract categories** — finding categories are collected into native and imported sets
4. **Prioritize templates** — templates whose `relevant_categories` overlap with the combined finding categories are moved to the front of the execution queue
5. **Run campaign** — all templates still run (prioritization, not exclusion)
6. **Build coverage report** — `build_coverage_report()` compares which finding categories were exercised by security-relevant outcomes

The coverage report is persisted as evidence (`type="coverage_report"`) on the inject child run, making it available to the web UI.

## Adapter

`InjectAdapter` wraps `run_campaign()` for orchestrator integration, handling child run lifecycle, progress events, finding emission, and findings-informed template prioritization. It queries audit findings via `finding_service`, builds a coverage report, and persists it as evidence.
