Vulnerability: Injection and Cross-site Scripting in osm-static-maps This affects all versions of package osm-static-maps under 3.9.0. User input given to the package is passed directly to a template without escaping ({{{ ... }}}). As such, it is possible for an attacker to inject arbitrary HTML/JS code and depending on the context. It will be outputted as an HTML on the page which gives opportunity for XSS or rendered on the server (puppeteer) which also gives opportunity for SSRF and Local File Read.
Vulnerability ID: GHSA-pxcf-v868-m492
CWE: CWE-79
Severity score: 7.6
Project: https://github.com/jperelli/osm-static-maps
Published: 2021-05-10
File: src/server.js
Commit message: fix: escape special characters before insertion to template
Commit author: snoopysecurity

Vulnerable version (before fix):
 app.use((req, res, next) => {   next(); });  app.get("/health", (req, res) => res.sendStatus(200));  const handler = (res, params) => { const handler = (res, params) => { app.get("/", (req, res) => handler(res, req.query)); app.post("/", (req, res) => handler(res, req.body));  app.get("/dynamic", (req, res) =>   handler(res, { ...req.query, renderToHtml: true }) );  app.post("/dynamic", (req, res) =>   handler(res, { ...req.body, renderToHtml: true }) );  module.exports = http.createServer(app);

Fixed version (after fix):
 app.use((req, res, next) => {   next(); });   function htmlEscape(text) {   return text.replace(/&/g, '&amp;').   replace(/</g, '&lt;').   replace(/"/g, '&quot;').   replace(/'/g, '&#039;'); }   function sanitize(params) {   result = {}   for (let [key, value] of Object.entries(params)) {       result[key] = htmlEscape(value)   }   return result; }  app.get("/health", (req, res) => res.sendStatus(200));  const handler = (res, params) => { const handler = (res, params) => { app.get("/", (req, res) => handler(res, req.query)); app.post("/", (req, res) => handler(res, req.body));  app.get("/dynamic", (req, res) => {   var sanitized = sanitize(req.query)   handler(res, { ...sanitized, renderToHtml: true }) })  app.post("/dynamic", (req, res) => {   var sanitized = sanitize(req.body)   handler(res, { ...sanitized, renderToHtml: true }) })  module.exports = http.createServer(app);

Patch:
patch: @@ -19,6 +19,23 @@ app.use((req, res, next) => {
   next();
 });
 
+
+function htmlEscape(text) {
+  return text.replace(/&/g, '&amp;').
+  replace(/</g, '&lt;').
+  replace(/"/g, '&quot;').
+  replace(/'/g, '&#039;');
+}
+
+
+function sanitize(params) {
+  result = {}
+  for (let [key, value] of Object.entries(params)) {
+      result[key] = htmlEscape(value)
+  }
+  return result;
+}
+
 app.get("/health", (req, res) => res.sendStatus(200));
 
 const handler = (res, params) => {
@@ -40,12 +57,14 @@ const handler = (res, params) => {
 app.get("/", (req, res) => handler(res, req.query));
 app.post("/", (req, res) => handler(res, req.body));
 
-app.get("/dynamic", (req, res) =>
-  handler(res, { ...req.query, renderToHtml: true })
-);
+app.get("/dynamic", (req, res) => {
+  var sanitized = sanitize(req.query)
+  handler(res, { ...sanitized, renderToHtml: true })
+})
 
-app.post("/dynamic", (req, res) =>
-  handler(res, { ...req.body, renderToHtml: true })
-);
+app.post("/dynamic", (req, res) => {
+  var sanitized = sanitize(req.body)
+  handler(res, { ...sanitized, renderToHtml: true })
+})
 
 module.exports = http.createServer(app);
path: src/server.js