diff --git a/README.md b/README.md index 9a0af9bf..8dde048a 100644 --- a/README.md +++ b/README.md @@ -147,8 +147,9 @@ This section describe all the possible warnings returned by JSXRay. Click on the ```ts interface RuntimeOptions { - module?: boolean; - isMinified?: boolean; + module?: boolean; + isMinified?: boolean; + removeHTMLComments?: boolean; } ``` @@ -156,11 +157,11 @@ The method take a first argument which is the code you want to analyse. It will ```ts interface Report { - dependencies: ASTDeps; - warnings: Warning[]; - idsLengthAvg: number; - stringScore: number; - isOneLineRequire: boolean; + dependencies: ASTDeps; + warnings: Warning[]; + idsLengthAvg: number; + stringScore: number; + isOneLineRequire: boolean; } ``` @@ -171,8 +172,9 @@ interface Report { ```ts interface RuntimeOptions { - module?: boolean; - isMinified?: boolean; + module?: boolean; + isMinified?: boolean; + removeHTMLComments?: boolean; } ``` diff --git a/index.js b/index.js index 41e54362..ec873a03 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,7 @@ import isMinified from "is-minified-code"; // Import Internal Dependencies import Analysis from "./src/Analysis.js"; import { warnings } from "./src/warnings.js"; +import * as utils from "./src/utils.js"; // CONSTANTS const kMeriyahDefaultOptions = { @@ -20,13 +21,19 @@ const kMeriyahDefaultOptions = { }; export function runASTAnalysis(str, options = Object.create(null)) { - const { module = true, isMinified = false } = options; + const { + module = true, + isMinified = false, + removeHTMLComments = false + } = options; // Note: if the file start with a shebang then we remove it because 'parseScript' may fail to parse it. // Example: #!/usr/bin/env node const strToAnalyze = str.charAt(0) === "#" ? str.slice(str.indexOf("\n")) : str; - const isEcmaScriptModule = Boolean(module); - const body = parseScriptExtended(strToAnalyze, isEcmaScriptModule); + const body = parseScriptExtended(strToAnalyze, { + isEcmaScriptModule: Boolean(module), + removeHTMLComments + }); const sastAnalysis = new Analysis(); sastAnalysis.analyzeSourceString(str); @@ -57,14 +64,20 @@ export function runASTAnalysis(str, options = Object.create(null)) { export async function runASTAnalysisOnFile(pathToFile, options = {}) { try { - const { packageName = null, module = true } = options; + const { + packageName = null, + module = true, + removeHTMLComments = false + } = options; + const str = await fs.readFile(pathToFile, "utf-8"); const filePathString = pathToFile instanceof URL ? pathToFile.href : pathToFile; const isMin = filePathString.includes(".min") || isMinified(str); const data = runASTAnalysis(str, { isMinified: isMin, - module: path.extname(filePathString) === ".mjs" ? true : module + module: path.extname(filePathString) === ".mjs" ? true : module, + removeHTMLComments }); if (packageName !== null) { data.dependencies.removeByName(packageName); @@ -87,13 +100,24 @@ export async function runASTAnalysisOnFile(pathToFile, options = {}) { } } -function parseScriptExtended(strToAnalyze, isEcmaScriptModule) { +function parseScriptExtended(strToAnalyze, options = {}) { + const { isEcmaScriptModule, removeHTMLComments } = options; + + /** + * @see https://github.com/NodeSecure/js-x-ray/issues/109 + */ + const cleanedStrToAnalyze = removeHTMLComments ? + utils.removeHTMLComment(strToAnalyze) : strToAnalyze; + try { - const { body } = meriyah.parseScript(strToAnalyze, { - ...kMeriyahDefaultOptions, - module: isEcmaScriptModule, - globalReturn: !isEcmaScriptModule - }); + const { body } = meriyah.parseScript( + cleanedStrToAnalyze, + { + ...kMeriyahDefaultOptions, + module: isEcmaScriptModule, + globalReturn: !isEcmaScriptModule + } + ); return body; } @@ -102,9 +126,13 @@ function parseScriptExtended(strToAnalyze, isEcmaScriptModule) { error.description.includes("The import keyword") || error.description.includes("The export keyword") )) { - const { body } = meriyah.parseScript(strToAnalyze, { - ...kMeriyahDefaultOptions, module: true - }); + const { body } = meriyah.parseScript( + cleanedStrToAnalyze, + { + ...kMeriyahDefaultOptions, + module: true + } + ); return body; } diff --git a/src/utils.js b/src/utils.js index 312bb153..489241a9 100644 --- a/src/utils.js +++ b/src/utils.js @@ -42,3 +42,7 @@ export function extractNode(expectedType) { } }; } + +export function removeHTMLComment(str) { + return str.replace(/)/g, ""); +} diff --git a/test/fixtures/regress/html-comments.js b/test/fixtures/regress/html-comments.js new file mode 100644 index 00000000..a9f66663 --- /dev/null +++ b/test/fixtures/regress/html-comments.js @@ -0,0 +1,9 @@ +; + +var bar; + +; diff --git a/test/regress.spec.js b/test/regress.spec.js index 92a104dd..cfd2e754 100644 --- a/test/regress.spec.js +++ b/test/regress.spec.js @@ -24,3 +24,26 @@ test("it should not crash for JSX", (tape) => { tape.end(); }); + +// Regression test for https://github.com/NodeSecure/js-x-ray/issues/109 +test("it should not crash for a JavaScript file containing HTML comments (and removeHTMLComments option enabled)", (tape) => { + const htmlComment = readFileSync(new URL("html-comments.js", FIXTURE_URL), "utf-8"); + runASTAnalysis(htmlComment, { + removeHTMLComments: true + }); + + tape.end(); +}); + +test("it should crash for a JavaScript file containing HTML comments", (tape) => { + const htmlComment = readFileSync(new URL("html-comments.js", FIXTURE_URL), "utf-8"); + try { + runASTAnalysis(htmlComment); + tape.fail(); + } + catch { + // do nothing + } + + tape.end(); +}); diff --git a/test/utils.spec.js b/test/utils.spec.js new file mode 100644 index 00000000..24e2c530 --- /dev/null +++ b/test/utils.spec.js @@ -0,0 +1,27 @@ +// Import Third-party Dependencies +import test from "tape"; + +// Import Internal Dependencies +import { removeHTMLComment } from "../src/utils.js"; + +test("removeHTMLComment() function should remove singleline HTML comment from string", (tape) => { + const result = removeHTMLComment(` + + `); + tape.strictEqual(result.trim(), ""); + + tape.end(); +}); + +test("removeHTMLComment() function should remove multiline HTML comment from string", (tape) => { + const result = removeHTMLComment(` + + `); + tape.strictEqual(result.trim(), ""); + + tape.end(); +}); diff --git a/types/api.d.ts b/types/api.d.ts index 08caa251..04b5f4e3 100644 --- a/types/api.d.ts +++ b/types/api.d.ts @@ -13,8 +13,18 @@ export { } interface RuntimeOptions { + /** + * @default true + */ module?: boolean; + /** + * @default false + */ isMinified?: boolean; + /** + * @default false + */ + removeHTMLComments?: boolean; } interface Report { @@ -27,7 +37,14 @@ interface Report { interface RuntimeFileOptions { packageName?: string; + /** + * @default true + */ module?: boolean; + /** + * @default false + */ + removeHTMLComments?: boolean; } type ReportOnFile = {