Electronegativity

repository·master·Indexed 21 days ago

https://github.com/doyensec/electronegativity

A security tool for identifying misconfigurations and security anti-patterns in Electron-based applications using AST and DOM parsing. It provides a CLI for scanning directories, .js files, .html files, and .asar archives, as well as a programmatic Node.js API via the Finder and Parser classes. The tool supports filtering results by severity and confidence, outputting to CSV or SARIF formats, and detecting Electron versions across package manifests.

Tokens
5K
Snippets
14
Records
16
Agent score
77%

What's inside @doyensec/electronegativity

  1. Disable specific checks using eng-disable comments

    master

    You can suppress specific security findings by adding eng-disable comments to your code. This works for both single lines and entire files.

    Inline (Single Line)

    Use // eng-disable, /* eng-disable */, or <!-- eng-disable --> followed by the check ID (snake_case) or construct name (camelCase).

    File-wide

    Place the eng-disable directive at the top of a .js or .html file to disable the specified checks for the entire file.

    Note: Global checks (like CSP_GLOBAL_CHECK) may not be compatible with annotations. For those, use the -x CLI flag to exclude them.

    // Disable by snake_case ID or camelCase construct name
    const res = eval(safeVariable); /* eng-disable DANGEROUS_FUNCTIONS_JS_CHECK */
    
    // Multiple checks
    shell.openExternal(eval(safeVar)); /* eng-disable OPEN_EXTERNAL_JS_CHECK DANGEROUS_FUNCTIONS_JS_CHECK */
    <!-- Disable in HTML -->
    <webview src="https://doyensec.com/" enableblinkfeatures="DangerousFeature"></webview> <!-- eng-disable BLINK_FEATURES_HTML_CHECK -->
  2. Generate the Electronegativity User Manual PDF

    master

    To generate the official Electronegativity User Manual as a PDF, you must first install the required conversion tools and clone the project wiki.

    Prerequisites

    1. Install github-wikito-converter globally via npm.
    2. Install wkhtmltopdf via your system package manager (e.g., apt-get). Note: If PDF rendering issues occur, install the latest stable static release from the official wkhtmltopdf website instead of using the apt version.
    3. Clone the Electronegativity wiki repository.

    Conversion Steps

    Navigate to the docs/resources folder within the project and execute the gwtc command with the appropriate flags to convert the wiki files into a formatted PDF manual.

    # 1. Install dependencies
    sudo npm install -g github-wikito-converter
    sudo apt-get install wkhtmltopdf
    
    # 2. Clone the wiki
    git clone https://github.com/doyensec/electronegativity.wiki.git
    
    # 3. Run conversion (from docs/resources folder)
    gwtc -f pdf -n ../manual/ElectronegativityUserManual_v1.x.x -t "<b>Electronegativity<b><br><p class="manual">User Manual - March 2019</p>" --logo-img ./img/logo.svg --footer "Electronegativity © 2017-2019 Doyensec LLC" --toc ../../electronegativity.wiki/Home.md --css ./electronegativitywiki.css --pdf-page-count ../../electronegativity.wiki/
  3. Use Electronegativity programmatically

    master

    Import @doyensec/electronegativity to integrate security scanning directly into your Node.js applications or custom tooling. The run() function accepts an options object similar to the CLI flags.

    const run = require('@doyensec/electronegativity')
    // or: import run from '@doyensec/electronegativity';
    
    run({
      input: '/path/to/electron/app',
      output: '/path/for/output/file',
      isSarif: false,
      customScan: ['dangerousfunctionsjscheck', 'remotemodulejscheck'],
      severitySet: 'high',
      confidenceSet: 'certain',
      isRelative: false,
      electronUpgrade: '7..8',
      electronVersion: '5.0.0',
      parserPlugins: ['optionalChaining']
    })
    .then(result => console.log(result))
    .catch(err => console.error(err));
  4. Understand the Electronegativity scan result object

    master

    When running programmatically, the run() function returns a Promise that resolves to an object containing scan metadata and an array of findings. Each finding includes details like file path, code sample, location, severity, and confidence.

    {
      globalChecks: 6,
      atomicChecks: 36,
      errors: [
        {
          file: 'ts/main/main.ts',
          sample: 'shell.openExternal(url);',
          location: { line: 328, column: 4 },
          id: 'OPEN_EXTERNAL_JS_CHECK',
          description: 'Review the use of openExternal',
          properties: undefined,
          severity: { value: 2, name: 'MEDIUM', format: [Function: format] },
          confidence: { value: 0, name: 'TENTATIVE', format: [Function: format] },
          manualReview: true,
          shortenedURL: 'https://git.io/JeuMC'
        }
        // ... more issues
      ]
    }
  5. Reference: Electronegativity CLI Options

    master

    Available command-line flags for the electronegativity tool:

    |    Option    |                 Description                       |
    |:------------:|:-------------------------------------------------:|
    | -V           | output the version number                         |
    | -i, --input  | input (directory, .js, .html, .asar)               |
    | -l, --checks | only run the specified checks, passed in csv format |
    | -x, --exclude-checks <excludedCheckNames> | skip the specified checks list, passed in csv format |
    | -s, --severity | only return findings with the specified level of severity or above |
    | -c, --confidence | only return findings with the specified level of confidence or above |
    | -o, --output <filename[.csv or .sarif]> | save the results to a file in csv or sarif format |
    | -r, --relative | show relative path for files |
    | -v, --verbose <bool> | show the description for the findings, defaults to true |
    | -u, --upgrade <current version..target version> | run Electron upgrade checks, eg -u 7..8 to check upgrade from Electron 7 to 8 |
    | -e, --electron-version <version> | assume the set Electron version, overriding the detected one, eg -e 7.0.0 to treat as using Electron 7 |
    | -p, --parser-plugins <plugins> | specify additional parser plugins to use separated by commas, e.g. -p optionalChaining |
    | -h, --help   | output usage information                          |
  6. Use the Electronegativity CLI

    master

    The Electronegativity CLI allows you to scan directories, .js files, .html files, or .asar archives for security issues. You can filter results by severity, confidence, or specific checks, and output results to CSV or SARIF formats.

    # Scan a directory
    $ electronegativity -i /path/to/electron/app
    
    # Scan an asar archive and save results to CSV
    $ electronegativity -i /path/to/asar/archive -o result.csv
    
    # Run Electron upgrade checks (e.g., from version 7 to 8)
    $ electronegativity -i /path/to/electron/app -v -u 7..8
    
    # Troubleshooting: If you encounter 'JavaScript heap out of memory'
    $ node --max-old-space-size=4096 electronegativity -i /path/to/asar/archive -o result.csv
  7. Perform a scan using Finder.find()

    master

    The find method executes the actual security analysis on a specific file. It supports different file types (JavaScript, HTML, JSON) and applies the enabled checks to the provided content.

    Method Signature

    async find(file, data, type, content, use_only_checks = null, electronVersion = null)

    Parameters

    • file (string): The path or identifier of the file being scanned.
    • data (object): The parsed data structure (e.g., an AST for JavaScript or a parsed object for JSON).
    • type (string): The source type of the file (e.g., sourceTypes.JAVASCRIPT, sourceTypes.HTML, or sourceTypes.JSON).
    • content (string|Buffer): The raw content of the file.
    • use_only_checks (Array<string>, optional): An array of specific check IDs to filter the scan. If provided, only checks matching these IDs will be run.
    • electronVersion (string, optional): The version of Electron being used. If not provided, it defaults to '0.1.0'. Providing the correct version ensures that security defaults are applied correctly according to that version's context.

    Returns

    Returns a Promise that resolves to an array of issue objects. Each issue contains details such as id, description, severity, location (line and column), and a sample of the offending code.

    const issues = await finder.find(
      'path/to/file.js',
      parsedData,
      sourceTypes.JAVASCRIPT,
      rawContent,
      ['check-id-123'], // optional: only run this specific check
      '12.0.0'          // optional: target Electron version
    );
  8. Detect the oldest Electron version using findOldestElectronVersion()

    master

    The findOldestElectronVersion function is the primary entry point for programmatically determining the Electron version used in a project. Because a project might have multiple Electron versions specified across different manifests, this utility assumes the oldest version found is the one actually in use to ensure conservative security analysis.

    You can provide an object containing various sources to scan for Electron versions. The function will aggregate all found versions and return the oldest one as a string.

    import { findOldestElectronVersion } from './path/to/electron_version.js';
    
    const oldestVersion = await findOldestElectronVersion({
      pjsonData: packageJsonContent, // Object from package.json
      rootPath: '/path/to/project',   // Scans installed packages via read-package-tree
      plockData: packageLockContent, // Object from package-lock.json
      yarnLockData: yarnLockString   // Raw string content of yarn.lock
    });
    
    console.log(`Oldest Electron version: ${oldestVersion}`);
  9. Add custom Babel or TypeScript plugins

    master

    You can extend the parser's capabilities by adding custom plugins to the internal Babel and TypeScript plugin lists using the addPlugin(plugin) method. This plugin will be applied to both Babel-based and TypeScript-based parsing attempts.

    const parser = new Parser();
    parser.addPlugin('my-custom-plugin');
  10. Parse files using the parse() method

    master

    The parse(filename, content) method is the primary entry point for converting source content into a structured format. It automatically detects the source type based on the file extension.

    Supported Source Types:

    • JavaScript/TypeScript: Returns an AST object. For TS/TSX, it uses either Babel or typescript-estree based on constructor settings.
    • HTML: Returns a cheerio loaded object (using xmlMode: true).
    • JSON: Returns an object containing the parsed json and the original text.

    Return Value: Returns an array in the format: [sourceType, data, content, errors]

    • sourceType: The detected type (e.g., sourceTypes.JAVASCRIPT).
    • data: The parsed AST, DOM, or JSON object.
    • content: The original string content.
    • errors: An optional array of errors if available in the parsed data.
    const parser = new Parser(true, true);
    const [type, data, originalContent, errors] = parser.parse('script.ts', 'const x = 10;');
  11. Initialize the Parser class

    master

    The Parser class is used to convert source code (JavaScript, TypeScript, HTML, or JSON) into an Abstract Syntax Tree (AST) or a traversable data structure.

    When initializing, you can specify the preference for parsing engines via the constructor:

    • babelFirst: If true, the parser will attempt to use @babel/parser before falling back to esprima for JavaScript files.
    • typescriptBabelFirst: If true, the parser will attempt to use @babel/parser before falling back to @typescript-eslint/typescript-estree for TypeScript files (.ts or .tsx).
    import { Parser } from './path/to/parser';
    
    // Example: Prefer Babel for both JS and TS parsing
    const parser = new Parser(true, true);