-
Notifications
You must be signed in to change notification settings - Fork 0
Reports
AISRF builds every report as a format agnostic document and renders it into any of 12 formats, so the same campaign looks consistent as a PDF for management, SARIF for a code scanning dashboard, JUnit for CI or CSV for a spreadsheet.
Source files: aisrf/reports/model.py (document model), aisrf/reports/builders.py (one builder per kind, BUILDERS and KINDS), aisrf/reports/renderers.py (FORMATS, RENDERERS, render, negotiate), aisrf/reports/router.py (REST), aisrf/codereview/report.py and aisrf/codereview/sarif.py (code review report and its dedicated SARIF).
A Report has title, subtitle, generated_at (UTC), generated_by (aisrf), meta (a dict with at least kind) and an ordered list of sections. Every section has a title and a kind discriminator:
| Section class | kind |
Content | Flattened as table |
|---|---|---|---|
KeyValueSection |
keyvalue |
list of (key, value) rows |
Key, Value
|
TableSection |
table |
columns, rows, optional notes, optional role (cases marks the table whose rows become JUnit test cases) |
as is |
TextSection |
text |
text, markdown flag |
one row per non empty line |
FindingsSection |
findings |
list of finding dicts (severity, category, title, description, evidence, location, ticket_id, probe_id, verdict, confidence, ...) |
only the columns that carry values |
ChartSection |
chart |
chart (bar or pie), labels, values
|
Label, Value
|
report_from_dict and section_from_dict round trip the JSON output back into a Report, so a JSON export can be re-rendered later into another format.
GET /api/reports returns KINDS (with accepted params) and FORMATS. Every report needs the viewer role or the admin token.
| Kind | Endpoint | Parameters |
|---|---|---|
summary |
GET /api/reports/summary |
since, until
|
tickets |
GET /api/reports/tickets |
status (repeatable), agent_id, min_risk, since, until, limit (500, max 5000) |
ticket |
GET /api/reports/ticket/{ticket_id} |
path only |
agent |
GET /api/reports/agent/{agent_id} |
since |
campaign |
GET /api/reports/campaign/{campaign_id} |
path only |
audit |
GET /api/reports/audit |
limit (500, max 10000) |
codereview |
GET /api/reports/codereview/{run_id} |
include_dismissed (SARIF only) |
Title AISRF platform summary, subtitle Scope: <since .. until or all time>.
-
Overview(key value): tickets all time and in scope, pending, last 24 h, average upstream latency, average human decision time. -
Tickets by status(bar chart) andTickets by risk level(pie chart). -
Tickets by agent(table: Agent, Agent id, Tickets) andTickets by model (in scope). -
Decision latency (human decisions in scope)(key value: decisions, mean, max and min seconds), present when at least one human decision exists in scope. -
Top finding categories(table), then the findings breakdown:Findings by category(bar),Findings by severity(pie), or aFindings breakdowntext section when there are none. -
Agents(table: Agent id, Name, Owner, Provider, Requires approval, Active, Requests, Last seen). -
Recent campaigns(table, with a note when none exist).
Title Tickets report, subtitle <n> of <total> matching tickets.
-
Filters(key value of the active filters, orfilters: none). -
Tickets(table with rolecases: number, id, created, agent, model, path, risk, level, policy action, status, decided by, latency, prompt preview). - Findings breakdown charts (
Findings by category,Findings by severity). -
Findings(findings section, one entry per finding across the selected tickets, each withticket_id).
Title Ticket #<number> dossier, subtitle <id> (<status>, risk <level> <score>).
-
Metadata(key value: id, number, status, agent, model, method, path, source, correlation id, risk, policy action, decision, timestamps, latency). -
Declared tools(table: Tool, Description) when the request declared tools or functions. -
Normalized messages(table: #, Role, Content, Tool; note with model, provider and stream flag). -
Findings(request findings). -
Policy decision(key value: action, matched rules, reasons). -
Timeline(table: Time, Event, Actor, Detail). -
Response preview(text) andError(text) when present.
Title Agent report: <name>, subtitle <agent id>.
-
Configuration(key value of provider, base URL, approval settings, thresholds, allow-lists, rate limit, canary, tags, owner). -
Statistics(tickets, average risk, count per status). -
Tickets by status(bar chart) when there are tickets. -
Tickets(table, rolecases, note<n> of <total> tickets shown). - Findings breakdown charts and
Findings. -
Recent events(table: Time, Level, Event, Ticket, Detail). -
Campaignsrun through this agent, when any.
Title Campaign report: <name>, subtitle <id> against <target model> (<status>).
-
Campaign(id, name, agent, target model, status, probe counters, creator, timestamps, error). -
Configuration(categories, techniques, mutators, max probes, concurrency, seed, path, system prompt, extra body). -
Summary(verdict counts, vulnerability rate, weighted score). -
Results by verdict(pie chart). -
Vulnerabilities by category(table: Category, Probes, Vulnerable, Resisted, Blocked, Other, Vulnerable %) andVulnerable probes by category(bar chart). -
Probe results(table, rolecases: Result, Probe, Category, Technique, Severity, Verdict, Confidence, Latency ms, Ticket, Prompt). -
Top vulnerable probes(findings withprobe_id,verdict,evidence,confidence,technique).
Title Audit log report, subtitle <n> of <total> entries, chain intact or chain BROKEN.
-
Chain verification(key value:ok,checked,headorbroken_at, entries in log). -
Entries by action(bar chart). -
Audit entries(table: Id, Time, Actor, Action, Target type, Target id, Detail, Hash, Previous hash).
Built by aisrf/codereview/report.py.
-
Run(id, name, source, commit, status, created by, engines, error). - Risk summary (key value: risk score and level, open findings, counts per severity).
-
Open findings by severity,Open findings by pack,Open findings by engine(charts). -
OWASP LLM Top 10 coverage,Top rules,Files with most findings(tables). - Inventory tables:
Languages,AI frameworks and SDKs,Model artifacts,Dependency manifests,Inventory highlights,Engines(availability, findings, seconds, note). -
Findings(table, rolecases: Rule, Severity, Confidence, Pack, Engine, File, Line, Title, Status, OWASP, CWE). -
Finding details(findings withlocationasfile:line,rule_id,pack,engine,cwe,owasp,fingerprint,remediation) orNoteswhen the run has no findings.
FORMATS in aisrf/reports/renderers.py:
| Format | Media type | Extension | Renderer notes |
|---|---|---|---|
json |
application/json |
.json |
the document verbatim (title, subtitle, generated_at, generated_by, meta, sections) |
yaml |
application/yaml |
.yaml |
same document as YAML |
csv |
text/csv |
.csv |
one block per section: a title line, the header row, the rows, a blank line |
tsv |
text/tab-separated-values |
.tsv |
as CSV with tabs |
md |
text/markdown |
.md |
headings, pipe tables, charts as label/value tables |
html |
text/html |
.html |
self contained, printable, inline SVG bar and pie charts, light and dark palettes |
pdf |
application/pdf |
.pdf |
reportlab: A4 with header and footer (Confidential - generated by AISRF, page numbers), bar and pie charts, small sections kept together |
xlsx |
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
.xlsx |
openpyxl workbook, one sheet per section (sheet names shortened to 31 characters) |
txt |
text/plain |
.txt |
aligned tables for terminals and email, cells truncated at 40 characters |
xml |
application/xml |
.xml |
generic XML of the document (<report><section kind="..."><row>...) |
sarif |
application/sarif+json |
.sarif |
SARIF 2.1.0 (details below) |
junit |
application/xml |
.xml |
JUnit XML test suites (details below) |
Aliases accepted in ?format= and the CLI: yml (yaml), markdown (md), text (txt), excel (xlsx), junitxml (junit), sarif.json (sarif). A leading dot is stripped and the value is case insensitive.
negotiate(accept_header, explicit_format):
- An explicit
?format=wins; an unknown value returns400 unknown report format '<x>'; supported: json, yaml, .... - Otherwise the
Acceptheader is parsed with quality values. Each media type is mapped throughMEDIA_TO_FORMAT(application/json,text/json,application/yaml,application/x-yaml,text/yaml,text/csv,text/tab-separated-values,text/markdown,text/html,application/xhtml+xml,application/pdf, the xlsx media type,application/vnd.ms-excel,text/plain,application/xml,text/xml,application/sarif+json,application/junit+xml).*/*andapplication/*meanjson,text/*meanstxt. Entries withq=0are ignored; the highestqwins and earlier entries win ties. - No header, or no recognised media type, means
json.
Browsers send text/html first, so opening a report URL without ?format= in a browser tab renders the HTML version.
Every response sets Content-Disposition: attachment; filename="aisrf-<kind>-<YYYYMMDD-HHMMSS>.<ext>" so browsers download the file. ?inline=true (the dashboard uses inline=1) switches to Content-Disposition: inline, which displays HTML and PDF in the tab. The kind in the filename is specific: ticket-<number>, agent-<id>, campaign-<id>, codereview-<run id>. Two extra headers identify the payload: X-Report-Kind and X-Report-Format.
Two SARIF producers exist.
Used for every kind except code review. Every finding from all FindingsSections becomes a result:
-
runs[0].tool.driver:name: "AISRF",fullName,version,rules(one rule per finding category,idequal to the category,defaultConfiguration.levelfrom the first severity seen, tagssecurity,llm,<category>). -
result.ruleId= category,levelfromSEVERITY_TO_SARIF(CRITICALandHIGH=error,MEDIUM=warning,LOWandINFO=note,NONE=none),message.text= title plus description,message.markdownwith the evidence when present. -
locations[0].logicalLocations[0].name=location, elseprobe_id, elseticket_id(kindmember); there is no physical file location for gateway findings. -
partialFingerprints["aisrf/v1"]=category|ticket_id|probe_id|title. -
propertiescarries every other finding field plusseverity. -
automationDetails.id=aisrf/<kind>,invocations[0].endTimeUtc, run levelpropertieswith title, subtitle and meta.
GET /api/reports/codereview/{run_id}?format=sarif and GET /api/codereview/runs/{run_id}/sarif produce a code scanning ready document:
-
tool.driver.name: "AISRF Code Review",rulesfrom the rule catalogue withhelpUri,defaultConfiguration.level,properties.precision(highfor confidence >= 0.7,medium>= 0.45, elselow),properties["security-severity"](CVSS style number used by GitHub to bucket severity), tags with the OWASP ids and CWE. -
taxonomies:OWASP LLM Top 10 2025with one taxon per item; each result references its taxa. -
result.locations[0].physicalLocation:artifactLocation.urirelative to%SRCROOT%(originalUriBaseIds),region.startLine,endLineand asnippet(max 2000 characters). -
partialFingerprints:aisrf/v1(the stable AISRF fingerprint) andprimaryLocationLineHash(<path>:<line>), so GitHub deduplicates findings across uploads. - Dismissed findings (
false_positive,accepted) are omitted unlessinclude_dismissed=true, in which case they carrysuppressionswith the reviewer note;fixesholds the remediation text. -
versionControlProvenancewith the repository URL and commit for git sources;invocations[0].executionSuccessfulis true only forCOMPLETEDruns.
- name: AISRF code review
run: |
aisrf codereview run --git "${{ github.server_url }}/${{ github.repository }}.git" --ref "${{ github.sha }}" \
--token-env GITHUB_TOKEN -f sarif -o aisrf.sarif
env:
AISRF_URL: ${{ secrets.AISRF_URL }}
AISRF_ADMIN_API_TOKEN: ${{ secrets.AISRF_ADMIN_API_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: aisrf.sarif
category: aisrf-code-reviewGateway findings can be uploaded the same way (aisrf report tickets -f sarif); they appear with logical locations only.
render_junit turns the report into <testsuites name="<title>">:
- Suites come from every
TableSectionwithrole="cases"(the ticket tables ofticketsandagent,Probe resultsofcampaign,Findingsofcodereview). When no such table exists theFindingsSections are used instead; when nothing yields cases a single passing placeholder case is emitted. - Each row becomes
<testcase name="<probe, ticket, #, id or name column>" classname="aisrf.<kind>.<section slug>" time="<latency ms / 1000>">. - The row is a
<failure type="AssertionError">when any of the columnsverdict,risk,risk_level,severity,statusorlevelcontainsVULNERABLE,CRITICALorHIGH; an<error type="Error">forERRORorFAILED;<skipped message="pending">forpending; otherwise the row detail goes to<system-out>. - Findings based cases fail for
CRITICALorHIGHseverity or verdictVULNERABLE. - Suite and root elements carry
tests,failures,errors,skipped,timestampandhostname="aisrf".
aisrf redteam run --agent agt_ci --model gpt-4o-mini --category prompt_injection --max-probes 40 --wait
aisrf report campaign/cmp_123 -f junit -o results.xmlAny CI system that ingests JUnit (GitHub Actions test reporters, GitLab artifacts:reports:junit, Jenkins) then fails the job when a probe was VULNERABLE. A ticket report (aisrf report tickets -f junit) fails on HIGH and CRITICAL tickets, and a code review report fails on HIGH and CRITICAL findings.
Inside the process (for example in a scheduled task or a test):
from aisrf.db import get_sessionmaker
from aisrf.reports import builders
from aisrf.reports.renderers import render
async with get_sessionmaker()() as session:
report = await builders.BUILDERS["campaign"](session, campaign_id="cmp_123")
payload, media_type, ext = render(report, "pdf")
open(f"campaign.{ext}", "wb").write(payload)Builders raise builders.NotFound for unknown ids (mapped to HTTP 404). Building your own document uses the model directly:
from aisrf.reports.model import Report, TableSection
from aisrf.reports.renderers import render
r = Report(title="Custom", meta={"kind": "custom"})
r.add(TableSection("Cases", columns=["Probe", "Verdict"], rows=[["p1", "VULNERABLE"]], role="cases"))
xml, _, _ = render(r, "junit")Over HTTP, httpx.get(f"{url}/api/reports/summary", params={"format": "xlsx"}, headers={"Authorization": f"Bearer {token}"}) returns the bytes; the dashboard's report links are the same URLs.
CLI (CLI-Reference):
aisrf report summary -f pdf -o weekly.pdf
aisrf report audit -f xlsx -o audit.xlsx
aisrf report ticket/tkt_5f3a -f md
aisrf codereview run --git https://github.com/org/app.git -f sarif -o app.sarifMCP (MCP-Server): generate_report(kind, format="json", output_path=None, params=None) and codereview_report(run_id, format="sarif", output_path=None) download through the REST API and return {"path", "media_type", "bytes"}; list_report_kinds() mirrors GET /api/reports.
Dashboard (Dashboard-Guide): the Reports page shows a kind by format matrix with shared filters; ticket, agent, campaign and code review pages carry a Reports card with one button per format (html and pdf open inline in a new tab).
AISRF, AI Security & Research Framework. github.com/keyuraghao/aisrf, Apache License 2.0.
Start
Gateway
- Gateway-Endpoints-and-Headers
- Request-Normalization
- Policy-Engine
- Agents-and-Credentials
- Configuration-Reference
- Settings-Center
Review
Security analysis
Red teaming
Code review
Interfaces
Operations
Project