diff --git a/README.md b/README.md index b7bf6ea7..1102886c 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,8 @@ type WarningName = "parsing-error" | "suspicious-file" | "obfuscated-code" | "weak-crypto" -| "unsafe-import"; +| "unsafe-import" +| "shady-link"; declare const warnings: Record **Note** credit goes to the [guarddog](https://github.dev/DataDog/guarddog) team. + +## Example + +```js +const foo = "http://foo.xyz"; +``` diff --git a/src/probes/isLiteral.js b/src/probes/isLiteral.js index 9c48349a..9a8388d5 100644 --- a/src/probes/isLiteral.js +++ b/src/probes/isLiteral.js @@ -6,6 +6,10 @@ import { Hex } from "@nodesecure/sec-literal"; // CONSTANTS const kNodeDeps = new Set(builtinModules); +const kShadyLinkRegExps = [ + /(http[s]?:\/\/bit\.ly.*)$/, + /(http[s]?:\/\/.*\.(link|xyz|tk|ml|ga|cf|gq|pw|top|club|mw|bd|ke|am|sbs|date|quest|cd|bid|cd|ws|icu|cam|uno|email|stream))$/ +]; /** * @description Search for Literal AST Node @@ -39,6 +43,14 @@ function main(node, options) { } // Else we are checking all other string with our suspect method else { + for (const regex of kShadyLinkRegExps) { + if (regex.test(node.value)) { + analysis.addWarning("shady-link", node.value, node.loc); + + return; + } + } + analysis.analyzeLiteral(node); } } diff --git a/src/warnings.js b/src/warnings.js index 21dbd99c..5f672ff9 100644 --- a/src/warnings.js +++ b/src/warnings.js @@ -45,6 +45,11 @@ export const warnings = Object.freeze({ i18n: "sast_warnings.weak_crypto", severity: "Information", experimental: true + }, + "shady-link": { + i18n: "sast_warnings.shady_link", + severity: "Warning", + experimental: true } }); diff --git a/test/probes/isLiteral.spec.js b/test/probes/isLiteral.spec.js index c2850a86..ca7ab2ad 100644 --- a/test/probes/isLiteral.spec.js +++ b/test/probes/isLiteral.spec.js @@ -86,3 +86,28 @@ test("should not throw any warnings without hexadecimal value (and should call a tape.end(); }); + +test("should detect shady link when an URL is bit.ly", (tape) => { + const str = "const foo = 'http://bit.ly/foo'"; + const ast = parseScript(str); + const sastAnalysis = getSastAnalysis(str, isLiteral).execute(ast.body); + + tape.strictEqual(sastAnalysis.warnings().length, 1); + const warning = sastAnalysis.getWarning("shady-link"); + tape.strictEqual(warning.value, "http://bit.ly/foo"); + + tape.end(); +}); + + +test("should detect shady link when an URL has a suspicious domain", (tape) => { + const str = "const foo = 'http://foobar.link'"; + const ast = parseScript(str); + const sastAnalysis = getSastAnalysis(str, isLiteral).execute(ast.body); + + tape.strictEqual(sastAnalysis.warnings().length, 1); + const warning = sastAnalysis.getWarning("shady-link"); + tape.strictEqual(warning.value, "http://foobar.link"); + + tape.end(); +}); diff --git a/types/warnings.d.ts b/types/warnings.d.ts index 5b7b9cb8..5185a444 100644 --- a/types/warnings.d.ts +++ b/types/warnings.d.ts @@ -15,7 +15,8 @@ type WarningNameWithValue = "parsing-error" | "suspicious-literal" | "suspicious-file" | "obfuscated-code" -| "weak-crypto"; +| "weak-crypto" +| "shady-link"; type WarningName = WarningNameWithValue | "unsafe-import"; type WarningLocation = [[number, number], [number, number]];