contrib.nodejsscan.ssrf_playwright.playwright_ssrf

profile photo of returntocorpreturntocorp
Author
99
Download Count*
License

If unverified user data can reach the puppeteer methods it can result in Server-Side Request Forgery vulnerabilities.

Run Locally

Run in CI

Defintion

rules:
  - id: playwright_ssrf
    patterns:
      - pattern-inside: |
          require('playwright');
          ...
      - pattern-either:
          - pattern-inside: function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: $PAGE.goto(<... $REQ.$QUERY.$FOO ...>, ...)
          - pattern: $PAGE.goto(<... $REQ.$BODY ...>, ...)
          - pattern: $PAGE.setContent(<... $REQ.$QUERY.$FOO ...>, ...)
          - pattern: $PAGE.setContent(<... $REQ.$BODY ...>, ...)
          - pattern: $PAGE.evaluate(<... $REQ.$QUERY.$FOO ...>, ...)
          - pattern: $PAGE.evaluate(<... $REQ.$BODY ...>, ...)
          - pattern: $PAGE.evaluate($CODE,..., <... $REQ.$QUERY.$FOO ...>, ...)
          - pattern: $PAGE.evaluate($CODE,..., <... $REQ.$BODY ...>, ...)
          - pattern: $PAGE.evaluateHandle(<... $REQ.$QUERY.$FOO ...>, ...)
          - pattern: $PAGE.evaluateHandle(<... $REQ.$BODY ...>, ...)
          - pattern: $PAGE.evaluateHandle($CODE,..., <... $REQ.$QUERY.$FOO ...>, ...)
          - pattern: $PAGE.evaluateHandle($CODE,..., <... $REQ.$BODY ...>, ...)
          - pattern: $PAGE.evaluateOnNewDocument(<... $REQ.$BODY ...>, ...)
          - pattern: $PAGE.evaluateOnNewDocument(<... $REQ.$BODY.$FOO ...>, ...)
          - pattern: $CONTEXT.addInitScript(<... $REQ.$BODY ...>,...)
          - pattern: $CONTEXT.addInitScript(<... $REQ.$BODY.$FOO ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.goto(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.goto(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluate($CODE,..., <... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluate($CODE,..., <... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluate(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluate(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateHandle(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateHandle(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateHandle($CODE,..., <... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateHandle($CODE,..., <... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateOnNewDocument(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateOnNewDocument(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $CONTEXT.addInitScript($INPUT,...);
    message: If unverified user data can reach the `puppeteer` methods it can result
      in Server-Side Request Forgery vulnerabilities.
    metadata:
      owasp: A01:2017 - Injection
      cwe: "CWE-918: Server-Side Request Forgery (SSRF)"
      category: security
      technology:
        - node.js
        - express
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    severity: ERROR
    languages:
      - javascript

Examples

ssrf_playwright.js

const { chromium } = require('playwright');
const express = require('express')
const app = express()
const port = 3000

app.post('/goto', async (req, res) => {
    const browser = await chromium.launch();
    const page = await browser.newPage();
    let url = 'https://hardcoded.url.com'

    // ruleid:playwright_ssrf
    await page.goto(req.foo);

    // ruleid:playwright_ssrf
    const newUrl = req.foo.bar;
    await page.goto(newUrl);

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

app.post('/setContent', async (req, res) => {
    const browser = await chromium.launch();
    const page = await browser.newPage();


    // ruleid:playwright_ssrf
    await page.setContent(req.foo['bar']);

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

app.post('/evaluate', async (req, res) => {

    const browser = await chromium.launch();
    const page = await browser.newPage();


    // ruleid:playwright_ssrf
    await page.evaluate(`fetch(${req.foo})`);

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

app.post('/evaluate', async (req, res) => {

    const browser = await chromium.launch();
    const page = await browser.newPage();


    // ruleid:playwright_ssrf
    await page.evaluate(x => fetch(x), req.foo.bar);

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