The engine
Scanner output in. A deliverable somebody can defend, out.
A report is only as good as the parse underneath it. Most of this engine is not the drafting — it is the seven places between a scan file and a signed document where a system like this can quietly lose the truth, and what it does at each of them.
Seven stages
Every stage has a rule that fails loudly
A pipeline described by what it produces tells you nothing. Described by what it refuses to do, it tells you where the system will break and how you will find out.
- 01
Ingest
One file, one import
A scan lands inside an engagement — Nessus, Burp, ZAP, Qualys or a SIEM export — encrypted at rest under a key scoped to your organisation, with a checksum so the same file submitted twice becomes one import rather than two sets of findings.
- 02
Parse
Parsed + rejected + duplicate = rows in the file
Every input row is recorded with a parse status. Anything unreadable is listed with a reason a human can act on, and an unrecognised format raises an error rather than returning an empty finding list that then renders as a perfectly clean report. A truncated file fails naming the offset where it stopped making sense.
- 03
Score
An absent score is not a zero
Severity is computed from the CVSS vector, and the vector round-trips: parse it, compute it, rebuild it, and get the same string back. Where the scanner supplied no score, the finding carries the status “not scored by tool” and no number appears anywhere it could be mistaken for one.
- 04
Override
No adjustment without a justification
An analyst can disagree with a computed score — environmental context is exactly the thing a scanner cannot know. The override requires written reasoning, is stored against the person who made it, and travels with the finding into the report rather than silently replacing the number.
- 05
Verify
Nothing renders on an unresolved CVE
Every CVE cited resolves against the national vulnerability database before the renderer runs, and one that does not exist terminates the job naming the reference. Where a CVE’s published score disagrees with the draft, both numbers are surfaced as a discrepancy instead of being quietly reconciled. Anything on the CISA exploited-vulnerabilities list is marked and sorted above score order, because a medium being exploited today outranks a high that is not.
- 06
Reconcile
One weakness is one finding, with a history
Findings get a fingerprint stable across scanner versions, so the same weakness reported by two tools collapses into one finding retaining both sources. Against the previous scan of the same estate, each finding is classified as new, persisting, reappeared or fixed — and that movement is written down when it happens rather than reconstructed later for a chart.
- 07
Assemble
A claim with no finding behind it does not ship
Executive summary, methodology, scope, risk matrix, per-finding detail with business impact and reproduction steps, a prioritised remediation roadmap. Sections are drafted against the findings that support them, and the section-to-finding link is stored rather than inferred — which is what makes the provenance view possible at all.
Provenance
Click a paragraph, see the scan row behind it
The chain is stored at write time, not reconstructed on demand. That is the difference between provenance and a plausible explanation.
Report section → findings
Each section records the findings it was drafted from. A narrative paragraph with an empty finding list is a validator failure, not a stylistic choice.
Finding → evidence
Raw scanner output, request and response bodies, log lines and screenshots attach to the finding, redacted where they carry credentials.
Finding → scan row
Every finding points back to the numbered row of the file it was parsed from, so a reviewer can open the original export and read it.
Everything → the audit log
Reads and exports are recorded with actor, address and timestamp. Who has seen a client’s attack surface is a question with an answer.
Output
Four exports, and a watermark until somebody signs
A report that cannot leave the tool in the format the client already works in is a report the analyst rewrites by hand. The register matters as much as the document — it is the artifact that actually gets tracked.
DOCX
The deliverable, rendered by a clause engine rather than written into an editor by a model.
PDF
The same document, for the client who files it rather than edits it.
XLSX finding register
Severity and status mapped so it imports cleanly into a tracker instead of being retyped.
JSON
The whole engagement, for the team that would rather build their own view of it.
An unapproved export carries an “AI-assisted draft — not reviewed” watermark. Approval by a lead clears it and is recorded against a named person and a timestamp. The watermark is the default, not the exception.
How it is built
Four decisions, and why they are the way they are
A graph of small nodes, not one prompt
Intake, parsing, scoring, research, structure, critic, drafting, rendering and healing are separate steps with their own inputs and failure modes. A golden set asserts the section plan for every job type, so a prompt change that drops the risk matrix is caught by the harness rather than by a human reading a document.
Two model tiers, on purpose
Classification, triage and critique run on a fast router model; structure and section drafting run on a stronger one. Every call writes a ledger row with tokens and cost, so the cost per report is a number that can be hand-checked rather than estimated.
Tenancy in the data layer
Scoping is enforced where the query is built, never in a route handler somebody can forget to guard. A cross-tenant request returns “not found” rather than “forbidden”, so existence does not leak either.
Inference where your data is allowed to be
Model inference is deployed per jurisdiction rather than routed to whichever provider is cheapest. If your scan data cannot leave a region, the model comes to the region.
Threat model
Scan data is attacker-controlled input
A vulnerability report is a targeting package, and the file it was built from was written by whatever was on the network. Both halves of that are treated as hostile.
- XML external entity resolution is disabled in every parser and tested rather than assumed — a payload embedded in an uploaded Nessus file has to be neutralised by a test that would fail if it were not.
- Finding titles and descriptions render inert in the console. The one application on your estate that displays attacker-authored text on every screen cannot be the one that trusts it.
- Any URL appearing in scan data is treated as a request-forgery vector before anything fetches it.
- Hosts, addresses, credentials and payloads are redacted before logs are written, not after they are found in one.
Integration
Callable by machines
The console is one client of the API, not a wrapper around a private one. If your pipeline should be uploading scans rather than a person, that is the same surface.
- Signed webhooks — HMAC over the raw body with the timestamp inside the signed payload, backoff on failure, and a delivery record per attempt.
- Streamed job progress: parsed 412 findings, drafting section 4 of 9, rendering — a readable narrative rather than a spinner.
- Idempotency on submission, so the same scan sent twice inside the window is one job and one execution.
- Quotas returning a real, handled state with what was used and when it resets — never a 500 dressed as a limit.
Tell us which stage is wrong
Seven stages written by people who read the domain carefully and have not run your engagements. If one of them would not survive contact with your work, that is worth more to us than a signup.
Get in touch
Talk to the people building it
No chatbot and no ticket queue. Tell us what your security reporting actually looks like — which tools you live in, how long a deliverable takes, what a client has sent back — and someone who works on the software will reply.
info@legosphere.com