Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,21 @@ 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;
}
```

The method take a first argument which is the code you want to analyse. It will return a Report Object:

```ts
interface Report {
dependencies: ASTDeps;
warnings: Warning[];
idsLengthAvg: number;
stringScore: number;
isOneLineRequire: boolean;
dependencies: ASTDeps;
warnings: Warning[];
idsLengthAvg: number;
stringScore: number;
isOneLineRequire: boolean;
}
```

Expand All @@ -171,8 +172,9 @@ interface Report {

```ts
interface RuntimeOptions {
module?: boolean;
isMinified?: boolean;
module?: boolean;
isMinified?: boolean;
removeHTMLComments?: boolean;
}
```

Expand Down
56 changes: 42 additions & 14 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ export function extractNode(expectedType) {
}
};
}

export function removeHTMLComment(str) {
return str.replace(/<!--[\s\S]*?(?:-->)/g, "");

Check failure

Code scanning / CodeQL

Incomplete multi-character sanitization

This string may still contain [<!--](1), which may cause an HTML element injection vulnerability.
}
9 changes: 9 additions & 0 deletions test/fixtures/regress/html-comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!-- const foo = 5; //-->;

var bar;

<!--
// == fake comment == //

const yo = 5;
//-->;
23 changes: 23 additions & 0 deletions test/regress.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
27 changes: 27 additions & 0 deletions test/utils.spec.js
Original file line number Diff line number Diff line change
@@ -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(`
<!-- const yo = 5; -->
`);
tape.strictEqual(result.trim(), "");

tape.end();
});

test("removeHTMLComment() function should remove multiline HTML comment from string", (tape) => {
const result = removeHTMLComment(`
<!--
// == fake comment == //

const yo = 5;
//-->
`);
tape.strictEqual(result.trim(), "");

tape.end();
});
17 changes: 17 additions & 0 deletions types/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,18 @@ export {
}

interface RuntimeOptions {
/**
* @default true
*/
module?: boolean;
/**
* @default false
*/
isMinified?: boolean;
/**
* @default false
*/
removeHTMLComments?: boolean;
}

interface Report {
Expand All @@ -27,7 +37,14 @@ interface Report {

interface RuntimeFileOptions {
packageName?: string;
/**
* @default true
*/
module?: boolean;
/**
* @default false
*/
removeHTMLComments?: boolean;
}

type ReportOnFile = {
Expand Down