Security and accessibility
For security reviews and accessibility checks: what the library sends over the network, what it never sends, how to run it with no outside requests, and how far the viewer meets KWCAG 2.2.
What the library sends
On a public domain, once a day per browser, the library sends one usage report to https://freereport.app/api/usage (navigator.sendBeacon):
- the domain (from the browser's Origin header)
- the page address without query string or hash (origin + path)
- the library version
- the license key, when one is set (the server checks it itself)
Without a valid key it also asks https://freereport.app/api/domain-status whether to show the small "free use" badge (no cookies, cached for a day). Nothing is sent from localhost, private IP addresses or intranet host names (no dot, .local, .internal, .test).
The server keeps per-domain totals (first and last seen, days, version, license status, one sample page address). It does not store the IP address or browser of visitors; to limit forged reports it keeps a one-way hash of the sending IP for two days, only for new domains.
What it never sends
- report data, parameters and report definitions
- PDF, Excel, HWPX, CSV and PNG files: they are made in the browser (or on your server with renderPdf / renderXlsx)
- who printed or downloaded: the onOutput event goes only to your own code
- your cookies, local storage or form fields
- AI assistant prompts: the designer calls your own assistant function and server; FreeReport holds no API key
If your network or CSP blocks freereport.app, reports work exactly the same: the report is not sent and nothing on screen changes. The designer's [Share link] on this website uploads a form only when you press it. Privacy policy
Self-hosting fonts (no outside requests)
By default the built-in Nanum fonts load from fonts.googleapis.com (screen and print) and cdn.jsdelivr.net (TrueType for PDF) the first time they are needed. On an intranet, or to make no outside requests, put the files on your own server and register them before the first report:
import { registerFont } from '@freereportjs/core';
// files on your own server (intranet): no request leaves the network
registerFont('Nanum Gothic', {
css: '/fonts/nanum-gothic.css', // @font-face for screen and print
normal: '/fonts/NanumGothic.ttf', // TrueType for PDF
bold: '/fonts/NanumGothicBold.ttf',
});
registerFont('Nanum Myeongjo', { css: '/fonts/nanum-myeongjo.css', normal: '/fonts/NanumMyeongjo.ttf' });Install the library from npm (or copy freereport.min.js to your server) instead of loading it from a CDN. Then, with the beacon blocked, the library makes no request outside your network. Fonts
Content Security Policy
The library uses no eval or new Function: expressions run in its own small interpreter. A policy that works with the defaults:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: blob:;
connect-src 'self' https://cdn.jsdelivr.net https://freereport.app;
object-src 'none';
base-uri 'self'- style-src 'unsafe-inline': the viewer and designer position elements with style attributes, and printing writes a styled document into a hidden iframe.
- img-src data: blob:: embossing, seals, PNG export and downloads use data: and blob: URLs.
- With self-hosted fonts, drop fonts.googleapis.com, fonts.gstatic.com and cdn.jsdelivr.net. Leaving out freereport.app only stops the usage report.
- A REST data source (definition.data.url) needs its host in connect-src.
PDF protection, signatures and verification
- Encryption: AES-256 (PDF 2.0, V5/R6) with an opening password and permissions that block printing, copying or changes, made with WebCrypto in the browser. PDF security
- Digital signatures: writePdf can reserve a signature field and signPdf fills it with a detached CMS (PKCS#7, or CAdES for PAdES) that you make with your own certificate, HSM or signing server. FreeReport never handles certificates or private keys.
- Printed-document verification: a QR code on the printout opens your verify page, which re-renders the stored original and compares SHA-256 fingerprints. The data stays on your servers. Verification
- Watermarks and output log: watermarks can carry the user and print time; onOutput records or blocks every print and download. Output log
KWCAG 2.2 self-check of the viewer
Our own reading of the viewer and designer source, not a certification or an audit. Items that depend only on your page (page title and language) or only on the form's content are marked as such. Report content (colors, alt texts of images) is up to the form author.
| Item | Status | Notes |
|---|---|---|
| 1.1.1 Text alternatives | Supported | Toolbar icon buttons have aria-label and title. Images, charts, barcodes, maps and seals take an alt template; charts without one are described from their data ("막대 차트: 1월 820, 2월 950…"), barcodes by their value and generated seals by their text. The viewer reads them as role="img" in its text layer and puts them in <title> in the page SVG; tagged PDFs write them as Figure /Alt. An image without alt counts as decoration, so give informative images an alt — checkReport warns (missing-alt) when the definition sets accessibility or lang. |
| 1.3.1 Table structure | Partial | Table and crosstab elements are real tables: <table> with <th scope> in the viewer's screen-reader text layer and Table/TR/TH/TD with /Scope in tagged PDFs. Tables drawn as bands (a column header band over detail rows of positioned fields) are read as paragraphs, row by row; for those, use a table element or offer the "Excel (data)" / CSV export. |
| 1.3.2 Meaningful sequence | Supported | The text layer and tagged PDFs follow the printed order of bands (title, headers, rows, totals) and, inside a band, position: top to bottom, then left to right. Page headers, footers, watermarks and lines are left out as decoration; role: 'artifact' leaves out more. Check layouts with overlapping elements. |
| 1.3.3 Clear instructions | Supported | Parameter fields have visible labels, a required mark and optional hint text linked with aria-describedby. |
| 1.4.1 Not relying on color | Partial | The viewer UI uses icons with labels and pressed states. Colors inside a report are chosen by the form author. |
| 1.4.3 Text contrast | Supported | Viewer text #0e1726 and #4a5568 on white are 17:1 and 7.5:1; hint and placeholder grays were darkened to at least 4.5:1, checked by a test. Report content contrast depends on the form. |
| 2.1.1 Keyboard | Supported | Viewer: every toolbar control, the contents and thumbnails are buttons or inputs; the save menu works with arrows, Home/End and Escape; the page area takes focus and pages with PageUp/PageDown and arrows, Ctrl+Home/End, Ctrl+F, Ctrl+P, Ctrl+plus/minus; links in the report are reached in the text layer. Designer: toolbox buttons add an element with Enter, Tab walks through the elements of the form and the arrow keys move them (Shift: 5mm, Alt: 0.1mm); size and every property are fields in the properties panel. |
| 2.1.2 Focus order and visibility | Supported | Buttons, inputs, menu items, contents and thumbnails show a 2px focus outline (:focus-visible); the scrolling page area shows one inside its edge. A focused link of the text layer appears at the top of its page. |
| 2.1.3 Target size | Supported | Toolbar controls are 30px high (about 8mm). |
| 2.1.4 Character key shortcuts | Supported | No single-letter shortcuts; all use Ctrl or Cmd and work only while the viewer has focus. |
| 2.2.1 Timing | Not applicable / your page | No time limits. |
| 2.3.1 Flashing | Supported | Nothing flashes; the loading spinner slows down with prefers-reduced-motion. |
| 2.4.1 Skip blocks | Supported | The viewer starts with a skip link (shown on focus) that jumps past the toolbar to the pages. Skip links of the page that embeds the viewer are up to that page. |
| 2.4.4 Fixed reference location | Supported | Page numbers match the printout; the page box takes a number and shows the total. |
| 2.5.1 Single pointer | Supported | Ctrl+wheel zoom also has buttons and a zoom list (50–200%, fit width, fit page). |
| 2.5.3 Label in name | Supported | Icon buttons use the same text for title and aria-label. |
| 3.1.1 Language of page | Not applicable / your page | The lang attribute of the page belongs to your site. The report's own language (definition lang, default ko-KR; element lang for other languages) goes on the viewer's text layer and into tagged PDFs as /Lang; the viewer's lang option only switches its UI text (Korean / English). |
| 3.2.1 On user request | Supported | Parameters run the report only on [Run] (조회); nothing opens new windows by itself. Links in reports open in a new tab with rel=noopener. |
| 3.3.1 Error correction | Supported | Required parameters and invalid values are marked and block the run until fixed. |
| 3.3.2 Labels | Supported | Every parameter input has a label element; the page box, zoom and search have aria-label. |
| 4.1.2 Web application accessibility | Supported | role="toolbar", a labelled page region, role="group" per page, aria-expanded/aria-pressed, a role="menu" save menu with arrow-key navigation, aria-live announcements of page changes and search results, and status. The page SVG is aria-hidden while the text layer carries its content. Designer: labelled toolbar and zoom buttons, labelled property fields, and an aria-live note of the selected element. |
PDF: toPdf({ tagged: true }) writes a tagged PDF and toPdf({ pdfua: true }) PDF/UA-1 (with pdfa: true also PDF/A-2b); sample reports with headings, tables, charts, seals, links and fillable fields pass veraPDF 1.30 (PDF/UA-1 and PDF/A-2b). A validator pass is not a full manual check: reading order and alt texts still depend on the form.
Zoom: pages are vector SVG and stay sharp at any zoom (browser zoom, 50–400% list, Ctrl+wheel). Pages keep their paper size, so the page content does not reflow at narrow widths; the toolbar wraps and keeps every control. Problems or requests: the forum.