You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
2.5 KiB
JavaScript
95 lines
2.5 KiB
JavaScript
"use strict";
|
|
|
|
const sax = require("sax");
|
|
const unreachable = require("@joepie91/unreachable")("detect-svg-scripts");
|
|
|
|
const { validateArguments } = require("@validatem/core");
|
|
const either = require("@validatem/either");
|
|
const required = require("@validatem/required");
|
|
const isString = require("@validatem/is-string");
|
|
const isBuffer = require("@validatem/is-buffer");
|
|
const isNodeStream = require("@validatem/is-node-stream");
|
|
|
|
module.exports = function detectSVGScripts(svgFile) {
|
|
validateArguments(arguments, {
|
|
svgFile: [ required, either([ isString, isBuffer, isNodeStream.anyReadable ]) ]
|
|
});
|
|
|
|
let parser = sax.createStream(false);
|
|
|
|
if (typeof svgFile === "string" || Buffer.isBuffer(svgFile)) {
|
|
// Make sure that all event listeners have been attached before we feed in any data
|
|
process.nextTick(() => {
|
|
parser.end(svgFile);
|
|
});
|
|
} else {
|
|
svgFile.pipe(parser);
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
let occurrences = [];
|
|
|
|
function detachListeners() {
|
|
parser.removeListener("end", onEnd);
|
|
parser.removeListener("error", onError);
|
|
parser.removeListener("opentag", onTag);
|
|
}
|
|
|
|
function onEnd() {
|
|
resolve(occurrences);
|
|
detachListeners();
|
|
}
|
|
|
|
function onError(error) {
|
|
reject(error);
|
|
detachListeners();
|
|
}
|
|
|
|
function onTag(tag) {
|
|
let normalizedTag = tag.name
|
|
.toLowerCase()
|
|
.replace(/.+:([^:]+)$/, (_, actualTag) => actualTag);
|
|
|
|
let normalizedAttributes = Object.entries(tag.attributes)
|
|
.map(([ attribute, value ]) => [ attribute.toLowerCase(), value ]);
|
|
|
|
if (normalizedTag === "script") {
|
|
let externalSources = normalizedAttributes
|
|
.filter(([ attribute, _value ]) => attribute === "src")
|
|
.map(([ _attribute, value ]) => value);
|
|
|
|
if (externalSources.length === 1) {
|
|
occurrences.push({
|
|
type: "externalScriptFile",
|
|
tag: tag,
|
|
file: externalSources[0]
|
|
});
|
|
} else if (externalSources.length === 0) {
|
|
occurrences.push({
|
|
type: "inlineScriptTag",
|
|
tag: tag
|
|
});
|
|
} else {
|
|
unreachable("Encountered more than one 'src' key");
|
|
}
|
|
}
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Events
|
|
let eventHandlers = normalizedAttributes
|
|
.filter(([ attribute, _value ]) => attribute.startsWith("on"));
|
|
|
|
for (let [ attribute, _value ] of eventHandlers) {
|
|
occurrences.push({
|
|
type: "eventHandler",
|
|
tag: tag,
|
|
attribute: attribute
|
|
});
|
|
}
|
|
}
|
|
|
|
parser.on("error", onError);
|
|
parser.on("end", onEnd);
|
|
parser.on("opentag", onTag);
|
|
});
|
|
};
|