javascript.chrome-remote-interface.security.audit.chrome-remote-interface-compilescript-injection.chrome-remote-interface-compilescript-injection

profile photo of semgrepsemgrep
Author
2,176
Download Count*

If unverified user data can reach the compileScript method it can result in Server-Side Request Forgery vulnerabilities

Run Locally

Run in CI

Defintion

rules:
  - id: chrome-remote-interface-compilescript-injection
    message: If unverified user data can reach the `compileScript` method it can
      result in Server-Side Request Forgery vulnerabilities
    metadata:
      owasp:
        - A10:2021 - Server-Side Request Forgery (SSRF)
      cwe:
        - "CWE-918: Server-Side Request Forgery (SSRF)"
      category: security
      technology:
        - chrome-remote-interface
      references:
        - https://github.com/cyrus-and/chrome-remote-interface
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: MEDIUM
      impact: MEDIUM
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Server-Side Request Forgery (SSRF)
    languages:
      - javascript
      - typescript
    severity: WARNING
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: function ... (..., $ARG,...) {...}
          - focus-metavariable: $ARG
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  require('chrome-remote-interface');
                  ...
              - pattern-inside: |
                  import 'chrome-remote-interface';
                  ...
          - pattern-either:
              - pattern: |
                  $RUNTIME.compileScript({expression: $SINK},...)
              - pattern: |
                  $RUNTIME.evaluate({expression: $SINK},...)
              - pattern: |
                  $PAGE.navigate({url: $SINK},...)
              - pattern: |
                  $RUNTIME.printToPDF({headerTemplate: $SINK},...)
              - pattern: |
                  $RUNTIME.printToPDF({footerTemplate: $SINK},...)
              - pattern: |
                  $PAGE.setDocumentContent({html: $SINK},...)
          - focus-metavariable: $SINK

Examples

chrome-remote-interface-compilescript-injection.js

const CDP = require('chrome-remote-interface');

async function example(userInput) {
    let client;
    try {
        client = await CDP();
        const {Runtime} = client;
        const script1 = "document.querySelector('p').textContent"
        // ok:chrome-remote-interface-compilescript-injection
        const result = await Runtime.compileScript({expression: script1, sourceURL:"", persistScript:false, executionContextId:1});
        // ruleid:chrome-remote-interface-compilescript-injection
        const result2 = await Runtime.compileScript({expression: userInput, sourceURL:"", persistScript:false, executionContextId:1});
        // ruleid:chrome-remote-interface-compilescript-injection
        const result3 = await Runtime.compileScript({expression: 'var x = 123;' + userInput, sourceURL:"", persistScript:false, executionContextId:1});
    } catch (err) {
        console.error(err);
    } finally {
        if (client) {
            await client.close();
        }
    }
}

async function example2(userInput) {
    let client;
    try {
        client = await CDP();
        const {Runtime} = client;
        const script1 = "document.querySelector('p').textContent"
        // ok:chrome-remote-interface-compilescript-injection
        const result = await Runtime.evaluate({expression: script1});
        // ruleid:chrome-remote-interface-compilescript-injection
        const result2 = await Runtime.evaluate({expression: userInput});
        // ruleid:chrome-remote-interface-compilescript-injection
        const result3 = await Runtime.evaluate({expression: 'var x = 123;' + userInput});
    } catch (err) {
        console.error(err);
    } finally {
        if (client) {
            await client.close();
        }
    }
}

async function example3(userInput) {
    let client;
    try {
        client = await CDP();
        const {Network, Page} = client;
        Network.requestWillBeSent((params) => {
            console.log(params.request.url);
        });
        await Network.enable();
        await Page.enable();
        // ok:chrome-remote-interface-compilescript-injection
        await Page.navigate({url: 'https://github.com'});
        // ruleid:chrome-remote-interface-compilescript-injection
        await Page.navigate({url: userInput});
        await Page.loadEventFired();
    } catch (err) {
        console.error(err);
    } finally {
        if (client) {
            await client.close();
        }
    }
}

function example4(userInput) {

    CDP(async (client) => {
        const {Page} = client;
        try {
            await Page.enable();
            await Page.navigate({url: 'https://github.com'});
            await Page.loadEventFired();
            // ok:chrome-remote-interface-compilescript-injection
            const result = await Page.printToPDF({landscape: true, printBackground: true, headerTemplate: '<h1>Title</h1>'});
            // ruleid:chrome-remote-interface-compilescript-injection
            const result2 = await Page.printToPDF({landscape: true, printBackground: true, footerTemplate: userInput});
            // ruleid:chrome-remote-interface-compilescript-injection
            const result3 = await Page.printToPDF({landscape: true, printBackground: true, headerTemplate: '<h1>' + userInput + '</h1>'});
            fs.writeFileSync('page.pdf', Buffer.from(data, 'base64'));
        } catch (err) {
            console.error(err);
        } finally {
            await client.close();
        }
    }).on('error', (err) => {
        console.error(err);
    });

}

function example5(userInput) {
    CDP(async (client) => {
        const {Page} = client;
        try {
            const {frameId} = await Page.navigate({url: 'about:blank'});
            const html = '<html>test</html>';
            // ok:chrome-remote-interface-compilescript-injection
            await Page.setDocumentContent({frameId, html});
            // ruleid:chrome-remote-interface-compilescript-injection
            await Page.setDocumentContent({frameId, html: userInput});
        } catch (err) {
            console.error(err);
            client.close();
        }
    }).on('error', (err) => {
        console.error(err);
    });
}