import Terria from "../../../lib/Models/Terria"; import ViewState from "../../../lib/ReactViewModels/ViewState"; import FeatureInfoDownload from "../../../lib/ReactViews/FeatureInfo/FeatureInfoDownload"; import { renderWithContexts } from "../withContext"; // The feature-info "Download" links build a CSV from the (attacker-influenceable) // feature properties. Cell values a spreadsheet would treat as a formula must be // neutralised in the downloaded CSV (CWE-1236). describe("FeatureInfoDownload", function () { it("neutralises CSV/formula injection in the downloadable CSV", function () { const viewState = new ViewState({ terria: new Terria({ baseUrl: "./" }) }); const data = { "=formulaKey": "=cmd|'/c calc.exe'!A1", total: "@SUM(A1:A5)", delta: -5 }; const { container } = renderWithContexts( , viewState ); const csvLink = container.querySelector( 'a[download="feature.csv"]' ); expect(csvLink).not.toBeNull(); const href = csvLink!.getAttribute("href") ?? ""; const csv = decodeURIComponent(href.replace("data:attachment/csv,", "")); // Both keys and values that look like formulas are prefixed with a quote. expect(csv).toContain("'=formulaKey"); expect(csv).toContain("'=cmd|'/c calc.exe'!A1"); expect(csv).toContain("'@SUM(A1:A5)"); // No cell is left as a bare formula. expect(csv).not.toMatch(/(^|,)=/m); // Numbers are preserved (not turned into text). expect(csv).toContain("-5"); }); });