Skip to content
Open
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
34 changes: 34 additions & 0 deletions PR_7_js/js/filtered_js/000_wkhtmltopdf-injection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const wkhtmltopdf = require('wkhtmltopdf')
const express = require("express");
const app = express();


function input() {
app.get("/add/:userInput", function (req, res) {
return req.params['userInput']
});
}
// {fact rule=server-side-request-forgery@v1.0 defects=1}
// ruleid: wkhtmltopdf-injection
wkhtmltopdf(input(), { output: 'vuln.pdf' })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The input() function call on this line will evaluate to undefined. The input function (defined on lines 6-10) registers an Express route but does not return a value. Therefore, wkhtmltopdf is being called with undefined as its first argument, which fails to demonstrate the intended security vulnerability.

// {/fact}

// {fact rule=server-side-request-forgery@v1.0 defects=1}
app.get("/add/:userInput", function (req, res) {
// ruleid: wkhtmltopdf-injection
return wkhtmltopdf(req.params['userInput'], { output: 'vuln.pdf' })
});
// {/fact}

// {fact rule=server-side-request-forgery@v1.0 defects=0}
// ok: wkhtmltopdf-injection
wkhtmltopdf('<html><html/>', { output: 'vuln.pdf' })
// {/fact}

// {fact rule=server-side-request-forgery@v1.0 defects=0}
function okTest(userInput) {
var html = '<html><html/>';
// ok: wkhtmltopdf-injection
return wkhtmltopdf(html, { output: 'vuln.pdf' })
}
// {/fact}
20 changes: 20 additions & 0 deletions PR_7_js/js/filtered_js/001_puppeteer-evaluate-arg-injection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const puppeteer = require('puppeteer');
const express = require('express')
const app = express()

app.get('/user/:userInput', async function (req, res) {

const browser = await puppeteer.launch();
const page = await browser.newPage();
// {fact rule=server-side-request-forgery@v1.0 defects=0}
// ok
await page.evaluate(x => console.log(x), 5);
// {/fact}

// {fact rule=server-side-request-forgery@v1.0 defects=1}
// ruleid:puppeteer-evaluate-arg-injection
await page.evaluate(x => fetch(x), req.params.userInput);
// {/fact}
await page.screenshot({path: 'example.png'});
await browser.close();
});
30 changes: 30 additions & 0 deletions PR_7_js/js/filtered_js/002_puppeteer-setcontent-injection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const puppeteer = require('puppeteer');
const express = require("express");
const app = express();

function unverifiedInput() {
app.get("/add/:userInput", function (req, res) {
return req.params['userInput']
});
}


// {fact rule=server-side-request-forgery@v1.0 defects=0}

(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();

// ok
await page.setContent('<html></html>');
// {/fact}

// {fact rule=server-side-request-forgery@v1.0 defects=1}

// ruleid:puppeteer-setcontent-injection
await page.setContent(unverifiedInput());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: Unverified user input is directly used in page.setContent(), potentially leading to XSS attacks. Sanitize or validate the input from unverifiedInput() before passing it to page.setContent().

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix addresses the vulnerability by sanitizing the user input before passing it to page.setContent(). It uses the DOMPurify library to remove potentially malicious content from the input, preventing XSS attacks. The sanitized input is then used in page.setContent(), making the operation safer.

Suggested change
await page.setContent(unverifiedInput());
// {fact rule=server-side-request-forgery@v1.0 defects=1}
// ruleid:puppeteer-setcontent-injection
// Import DOMPurify for sanitization
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
const sanitizedInput = DOMPurify.sanitize(unverifiedInput());
await page.setContent(sanitizedInput);
await page.screenshot({path: 'example.png'});
await browser.close();


await page.screenshot({path: 'example.png'});
await browser.close();
})();
// {/fact}
43 changes: 43 additions & 0 deletions PR_7_js/js/filtered_js/003_puppeteer-goto-injection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const puppeteer = require('puppeteer');
const express = require('express')
const app = express()

userInput = ''

app.get('/user/:userInput', async function (req, res) {
userInput=req.params.userInput
});

function unverifiedInput(){
app.get('/user/:userInput', async function (req, res) {
retrun = req.params.userInput

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This line contains a typo (retrun instead of return). This will result in an assignment to an implicit global variable retrun instead of returning a value from the callback.

Suggested change
retrun = req.params.userInput
return req.params.userInput

});
}

const testFunc = async (userInput) => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
let url = 'https://hardcoded.url.com'
// {fact rule=server-side-request-forgery@v1.0 defects=0}
// ok
await page.goto('https://example.com');
// {/fact}

// {fact rule=server-side-request-forgery@v1.0 defects=0}
// ok
await page.goto(url);
// {/fact}

// {fact rule=server-side-request-forgery@v1.0 defects=1}
// ruleid:puppeteer-goto-injection
await page.goto(unverifiedInput());
// {/fact}

const newUrl = userInput;
// {fact rule=server-side-request-forgery@v1.0 defects=1}
// ruleid:puppeteer-goto-injection
await page.goto(newUrl);
// {/fact}
await page.screenshot({path: 'example.png'});
await browser.close();
};
31 changes: 31 additions & 0 deletions PR_7_js/js/filtered_js/004_puppeteer-evaluate-code-injection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const puppeteer = require('puppeteer');
const express = require("express");
const app = express();

async function test2(userInput) {

const browser = await puppeteer.launch();
const page = await browser.newPage();
// {fact rule=server-side-request-forgery@v1.0 defects=0}

// ok:puppeteer-evaluate-code-injection
await page.evaluate(x => console.log(x), 5);
// {/fact}

// {fact rule=server-side-request-forgery@v1.0 defects=1}

// ruleid:puppeteer-evaluate-code-injection
await page.evaluate(`fetch(${userInput})`);
// {/fact}

await page.screenshot({path: 'example.png'});
await browser.close();
}

function call() {
app.get("/add/:userInput", function (req, res) {
test2(req.params['userInput'])
});
}

call()
167 changes: 167 additions & 0 deletions PR_7_js/js/filtered_js/005_express-vm-injection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const vm = require('vm')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This file uses app to define routes, but express is not imported and app is not initialized. This will cause a ReferenceError at runtime. Please add the necessary setup.

Suggested change
const vm = require('vm')
const vm = require('vm');
const express = require('express');
const app = express();


// {fact rule=code-injection@v1.0 defects=1}
let ctrl1 = function test1(req,res) {
var input = req.query.something || ''
var sandbox = {
foo: input
}
vm.createContext(sandbox)
// ruleid:express-vm-injection
vm.runInContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
res.send('hello world')
}
app.get('/', ctrl1)
// {/fact}

// {fact rule=code-injection@v1.0 defects=1}
app.get('/', (req,res) => {
var sandbox = {
foo: req.query.userInput
}
vm.createContext(sandbox)
// ruleid:express-vm-injection
vm.runInContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
res.send('hello world')
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=0}
// ok:express-vm-injection
function testOk1(userInput) {
var sandbox = {
foo: 1
}
vm.createContext(sandbox)
vm.runInContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
}
// {/fact}

// {fact rule=code-injection@v1.0 defects=1}
var ctrl2 = null;
ctrl2 = function test2(req,res) {
var input = req.query.something || ''
var sandbox = {
foo: input
}
// ruleid:express-vm-injection
vm.runInNewContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
res.send('hello world')
}
app.get('/', ctrl2)
// {/fact}

// {fact rule=code-injection@v1.0 defects=1}
app.get('/', function (req,res) {
var sandbox = {
foo: req.query.userInput
}
// ruleid:express-vm-injection
vm.runInNewContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
res.send('hello world')
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=0}
// ok:express-vm-injection
app.get('/', function testOk1(userInput) {
var sandbox = {
foo: 1
}
vm.runInNewContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
res.send('hello world')
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=1}
app.get('/', function(req,res) {
const code = `
var x = ${req.query.userInput};
`
// ruleid:express-vm-injection
vm.runInThisContext(code)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: Passing unsanitized user data to node vm methods and context can modify the syntax or behavior of the intended code segment. Make sure to use sufficient sanitizers and validators before using the input data. For more information, see Learn more.

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix sanitizes the user input using validator.escape() to prevent code injection, and replaces vm.runInThisContext() with eval() for safer execution of the code string.

Suggested change
vm.runInThisContext(code)
// Import the validator package for input sanitization
// const validator = require('validator');
app.get('/', function(req,res) {
const userInput = validator.escape(req.query.userInput);
const code = `
var x = ${userInput};
`
// Use a safer alternative to vm.runInThisContext
const result = eval(code);
res.send('hello world');
})

res.send('hello world')
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=0}
// ok:express-vm-injection
app.get('/', function okTest3(req,res) {
const code = `
var x = 1;
`
vm.runInThisContext(code)
res.send('hello world')
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=1}
app.get('/', function test4(req,res) {
const parsingContext = vm.createContext({name: 'world'})
const code = `return 'hello ' + ${req.query.userInput}`
// ruleid:express-vm-injection
let fn = vm.compileFunction(code, [], { parsingContext })
res.send('hello world')
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=0}
// ok:express-vm-injection
app.get('/', function okTest4(req,res) {
const parsingContext = vm.createContext({name: 'world'})
const code = `return 'hello ' + name`
const fn = vm.compileFunction(code, [], { parsingContext })
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=1}
app.get('/', (req,res) => {
const context = vm.createContext({name: req.query.userInput})
let code = `return 'hello ' name`
// ruleid:express-vm-injection
const fn = vm.compileFunction(code, [], { parsingContext: context })
res.send('hello world')
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=0}
// ok:express-vm-injection
app.get('/', function okTest5(req, res) {
const parsingContext = vm.createContext({name: 'world'})
const code = `return 'hello ' + name`
const fn = vm.compileFunction(code, [], { parsingContext })
res.send('hello world')
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=1}
app.get('/', function (req,res) {
// ruleid:express-vm-injection
const script = new vm.Script(`
function add(a, b) {
return a + ${req.query.userInput};
}

const x = add(1, 2);
`);

script.runInThisContext();
res.send('hello world')
})
// {/fact}

// {fact rule=code-injection@v1.0 defects=0}
//ok:express-vm-injection
app.get('/', function okTest6(req, res) {
const script = new vm.Script(`
function add(a, b) {
return a + b;
}

const x = add(1, 2);
`);

script.runInThisContext();
res.send('hello world')
})
// {/fact}
Loading