Skip to content

feat(report): add Markdown export with metadata, disclaimer and escaping (#16) - #26

Merged
hsinatfootprintai merged 13 commits into
mainfrom
agent/issue-16
Oct 24, 2026
Merged

hsinatfootprintai merged 13 commits into
mainfrom
agent/issue-16

Conversation

@containarium-ci

Copy link
Copy Markdown
Contributor

For #16: [S8] Markdown export with metadata, disclaimer and escaping of vendor strings

What and why

The platform made the commits and the branch from the files the agent handed back; it does not use the agent's own git state on the box, so anything the summary says about committing, branching or pushing describes only what the agent could do there.

The agent's summary, quoted:

Adds the S8 Markdown export. `touchstone scan --format md` (alias `markdown`) renders the ScanReport
via a new `internal/report` package instead of protojson. The renderer always prints the
started/completed timestamps, tool version, vulnerability-data date and the fixed disclaimer,
printing "unknown" for metadata the report lacks rather than omitting the line. Whenever
`lookup_status` is not COMPLETE the report-level line and every per-component vulnerability line say
"lookup incomplete" and no count (including "no known vulnerabilities") is ever rendered; counts
appear only when the lookup completed. Every vendor-originated string (target URL, names, versions,
purl/CPE, caveats, evidence, vulnerability ids/aliases/summaries, VEX justifications, checked URLs,
source errors) passes through an escaper that turns &, <, > into entities, backslash-escapes
Markdown punctuation and replaces control characters and line separators with a space, so
Markdown/HTML injection is inert. Golden-file tests cover complete, partial, failed, unreachable and
injection fixtures in internal/report/testdata plus the canned CLI report in
testdata/report.golden.md; further tests assert metadata/disclaimer presence on every fixture
(including nil/empty reports), that incomplete lookups never render a count, and that every raw
injection string is absent from the output. The disclaimer constant moves to report.Disclaimer
(service re-exports it) and gains the Low-confidence sentence; the cross-language golden JSON is
updated accordingly and the web Vitest decode test still passes. README mentions the new flag.
Verified with gofmt, go vet, staticcheck, go test, tsc --noEmit and vitest, all green.

Tools used:
- go 1.27.3 linux/amd64 (already on the box, ~/.local)
- gofmt from go1.27.3 (already on the box, ~/.local)
- staticcheck 2026.2.2 (0.8.2) (already on the box, ~/.local)
- node v24.22.1 (already on the box, ~/.local)
- npx running tsc and vitest 3.2.7 from the repo's existing web/node_modules (already on the box)
Nothing was installed.

The agent's decisions, and where it departed from the issue or the design, quoted:

1. The issue names a fixed 'Low-confidence disclaimer' but the existing service.Disclaimer had no
   Low-confidence sentence, so the constant moved to report.Disclaimer and gained one
   ('Low-confidence findings are inferred from passive fingerprints and are unverified.'); service
   re-exports it and testdata/report.golden.json was regenerated, which also changes the JSON
   output's disclaimer field.
2. The renderer prints the disclaimer from the report package constant rather than from the
   ScanReport.disclaimer field, so a report that arrives without the field still carries it; the
   field is kept in the proto and canned report unchanged.
3. 'lookup incomplete' is rendered both on the report-level 'Vulnerability lookup' line (with the
   PARTIAL/FAILED/unknown reason in parentheses) and on every component's 'Vulnerabilities' line;
   individual vulnerability rows that were found are still listed, since they are findings and not a
   count.
4. Missing metadata (nil timestamps, empty tool version) renders as 'unknown' rather than dropping
   the line, so the timestamp, tool-version and vuln-data-date lines are present in every rendering
   as the issue requires.
5. Golden fixtures are built in Go in the test file (complete, partial, failed, unreachable,
   injection) rather than as JSON fixture files, so the fixtures cannot drift from the proto;
   rendered output lives in internal/report/testdata/*.golden.md and is refreshed with 'go test
   ./internal/report -update'.
6. Escaping strategy: & < > become HTML entities, Markdown punctuation (\ ` * _ [ ] | ~) is
   backslash-escaped, and control characters plus Unicode line/paragraph separators become a space;
   block-level markers are not escaped because the renderer never places a vendor string at the
   start of a line, and a test asserts this.
7. --format accepts 'markdown' as an alias of 'md'; an unknown format is rejected before the scan
   runs so a typo never spends a request budget.
8. README's status paragraph gained two sentences describing --format md; no design document was
   edited.

Changes

  • internal/report/markdown.go — 358 lines
  • internal/report/markdown_test.go — 342 lines
  • internal/report/testdata/complete.golden.md — 87 lines
  • internal/report/testdata/partial.golden.md — 47 lines
  • internal/report/testdata/failed.golden.md — 34 lines
  • internal/report/testdata/unreachable.golden.md — 27 lines
  • internal/report/testdata/injection.golden.md — 52 lines
  • cmd/touchstone/main.go — 80 lines
  • cmd/touchstone/main_test.go — 176 lines
  • internal/service/scan.go — 100 lines
  • testdata/report.golden.md — 39 lines
  • testdata/report.golden.json — 47 lines
  • README.md — 39 lines

13 file(s) in all.

Acceptance, from the issue

  • ✅ met `touchstone scan --format md` renders the report via golden-file tests — the agent says: --format md renders via report.WriteMarkdown; golden tests in internal/report (5 fixtures) and cmd/touchstone (testdata/report.golden.md) pass.
  • ✅ met Timestamp, tool version, vuln-data date and the fixed Low-confidence disclaimer are always present; `lookup_status` not COMPLETE renders "lookup incomplete", n… — the agent says: Header always prints Started/Completed, Tool version, Vuln data as of ('unknown' when absent) and the disclaimer; non-COMPLETE renders 'lookup incomplete', tes…
  • ✅ met A fixture with Markdown/HTML injection in a component name and evidence string renders escaped — the agent says: injection fixture puts script/img/iframe, links, emphasis, table pipes and newlines in name, evidence and every vendor field; golden plus TestInjectionIsEscape…

The items are the issue's own; each status is the agent's report, which the platform has not checked.

Checks

Review this like any other change. The agent ran on the member's own machine from the text of the issue; the platform has not built or tested it. CI results appear on this pull request.

  • Tests, as reported by the agent: passed
How this pull request was made

Opened by the platform from a coding agent's run on a member's box (run 5c8dd9c0). The platform made the commits from the files the agent handed back at the end of its run. Nothing is merged automatically.

@hsinatfootprintai
hsinatfootprintai merged commit e54d2fd into main Oct 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant