diff --git a/runner/src/analytics-inject.js b/runner/src/analytics-inject.js new file mode 100644 index 0000000..d1a41ab --- /dev/null +++ b/runner/src/analytics-inject.js @@ -0,0 +1,66 @@ +// Website-Analyse: Skript-Tag in veröffentlichte HTML-Dateien einsetzen und Datenschutz-Zusatzseite schreiben. +import * as nodeFs from 'node:fs/promises'; +import { join } from 'node:path'; + +export const RE_SITE_KEY = /^[a-z0-9]{16,40}$/; +export const RE_COLOR = /^#[0-9a-f]{6}$/; +const MAX_HTML_BYTES = 5 * 1024 * 1024; +const PAGE = 'datenschutz-analyse.html'; +const TEMPLATE_URL = new URL('./templates/datenschutz-analyse.html', import.meta.url); + +const esc = (s) => String(s).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + +export const isValidBase = (b) => typeof b === 'string' && /^https:\/\/[^\s"'<>]+\/$/.test(b); + +export function scriptTag(analytics, baseUrl) { + // Attributwerte nur aus geprüften Eingaben (Regex), daher ohne Maskierung sicher. + return ``; +} + +// Arbeitet auf latin1-Zeichenketten (1 Byte = 1 Zeichen), damit beliebige Kodierungen byte-genau erhalten bleiben. +export function injectIntoHtml(buf, tag) { + let s = buf.toString('latin1'); + const old = /`; +const dir = () => mkdtempSync(join(tmpdir(), 'inj-')); +const put = (d, f, c) => { mkdirSync(join(d, f, '..'), { recursive: true }); writeFileSync(join(d, f), c); }; +const get = (d, f) => readFileSync(join(d, f), 'utf8'); +const count = (s, sub) => s.split(sub).length - 1; + +test('mehrere Dateien inkl. Unterordner: Tag vor , node_modules/Punktordner übersprungen', async () => { + const d = dir(); + put(d, 'index.html', '
b
'); + put(d, 'node_modules/p/x.html', ''); + put(d, '.hidden/x.html', ''); + await injectAnalytics(d, A(), { baseUrl: BASE }); + assert.equal(get(d, 'index.html'), `b
`); + assert.equal(get(d, 'node_modules/p/x.html'), ''); + assert.equal(get(d, '.hidden/x.html'), ''); +}); + +test('zweimal ausführen: kein Doppel; alter Tag wird ersetzt', async () => { + const d = dir(); + put(d, 'index.html', ''); + await injectAnalytics(d, A(), { baseUrl: BASE }); + await injectAnalytics(d, A(), { baseUrl: BASE }); + assert.equal(count(get(d, 'index.html'), 'data-wk-analytics'), 1); + put(d, 'b.html', ''); + await injectAnalytics(d, A(), { baseUrl: BASE }); + assert.equal(get(d, 'b.html'), `${TAG}`); +}); + +test('ohne head: nach ; ohne body: am Dateianfang; großes ', async () => { + const d = dir(); + put(d, 'a.html', 'hi'); + put(d, 'b.html', 'nur text
'); + put(d, 'c.html', 'nur text
`); + assert.equal(get(d, 'c.html'), `