Giter Club home page Giter Club logo

eslump's Introduction

eslump Build Status

Fuzz testing JavaScript parsers and suchlike programs.

es : short for ECMAScript (the JavaScript standard)
lump : a piece or mass of indefinite size and shape
slump : the Swedish word for “chance”

Inspired by esfuzz. Powered by shift-fuzzer and shift-codegen.

Contents

Installation

eslump is primarily intended to be used as a CLI tool.

npm install --global eslump

You can also use parts of it as a Node.js module.

npm install eslump

CLI

eslump --help
Usage: eslump [options]
   or: eslump TEST_FILE OUTPUT_DIR [options]

Options:

  --max-depth Number    The maximum depth of the random JavaScript. - default: 7
  --source-type String  Parsing mode. - either: module or script - default: module
  --whitespace          Randomize the whitespace in the random JavaScript.
  --comments            Insert random comments into the random JavaScript.
  -r, --reproduce       Reproduce a previous error using files in OUTPUT_DIR.
  -h, --help            Show help
  -v, --version         Show version

When no arguments are provided, random JavaScript is printed to stdout.
Otherwise, TEST_FILE is executed until an error occurs, or you kill the
program. When an error occurs, the error is printed to stdout and files
are written to OUTPUT_DIR:

  - random.js contains the random JavaScript that caused the error.
  - random.backup.js is a backup of random.js.
  - reproductionData.json contains additional data defined by TEST_FILE
    needed to reproduce the error caused by random.js, if any.
  - Other files, if any, are defined by TEST_FILE.

OUTPUT_DIR is created as with `mkdir -p` if non-existent.

For information on how to write a TEST_FILE, see:
https://github.com/lydell/eslump#test-files

Examples:

  # See how "prettier" pretty-prints random JavaScript.
  $ eslump | prettier --parser babel

  # Run test.js and save the results in output/.
  $ eslump test.js output/

  # Narrow down the needed JavaScript to produce the error.
  # output/random.backup.js is handy if you go too far.
  $ vim output/random.js

  # Reproduce the narrowed down case.
  $ eslump test.js output/ --reproduce

Module

const { generateRandomJS } = require("eslump");

const randomJSString = generateRandomJS({
  sourceType: "module",
  maxDepth: 7,
  comments: false,
  whitespace: false,
});

generateRandomJS(options = {})

Returns a string of random JavaScript code.

If you want, you can pass some options:

Option Type Default Description
sourceType "module" or "script" "module" The type of code to generate.
maxDepth integer 7 How deeply nested AST:s to generate.
comments boolean false Whether or not to generate random comments.
whitespace boolean false Whether or not to generate random whitespace.

Disclaimer

eslump was created from the need of finding edge cases in Prettier. It started out as a bare-bones little script in a branch on my fork of that repo. As I wanted more and more features, I extracted it and fleshed it out in its own repo. Then I realized that it might be useful to others, so I put it on GitHub and made the CLI installable from npm.

Initially, eslump basically just strung together shift-fuzzer and shift-codegen. Then, I realized that no random comments were generated, so I hacked that in (along with random whitespace) since comments are very difficult to get right in Prettier. Then, random parentheses and semicolons where requested, so I hacked that in as well.

eslump has successfully found lots of little edge cases in Prettier, so it evidently works. But there aren’t many tests. (I’ve mostly gone meta and fuzz-tested it using itself basically.)

From the beginning eslump was only ever intended to be a CLI tool, but other people have started to want to use eslump’s code generation as an npm module, so these days it can also be used as a module. If you know what you’re doing.

Here are some features I’d like to see from a proper random JS library:

  • No hacks.
  • Seeded randomness, so things can be reproduced.
  • JSX and Flow support.
  • Ability to generate code without any early errors.
  • Possibly ways to prevent certain syntax constructs from being generated.

Examples

There are several examples in the examples directory.

To run the Acorn example, for instance, follow these steps:

  1. Clone this repository.
  2. npm ci
  3. eslump examples/acorn.js output

Test files

$ eslump test.js output/

Test files, test.js in the above example, must follow this pattern:

module.exports = ({
  code, // String.
  sourceType, // String, either "module" or "script".
  reproductionData = {}, // undefined or anything that `JSON.parse` can return.
}) => {
  if (testFailedSomehow) {
    return {
      error, // Caught Error object.
      reproductionData, // Optional. Anything that `JSON.stringify` can handle.
      artifacts, // Optional. Object mapping file names to string contents.
    };
  }
  // If the test passed, return nothing.
};
  • The main export is a function, called the test function.

  • The test function accepts a single argument, an object with the following properties:

    • code: String. Randomly generated JavaScript, or the contents of OUTPUT_DIR/random.js if using the --reproduce flag.

    • sourceType: String. Either "module" or "script". ES2015 can be parsed in one of these modes, and parsers usually have an option for choosing between the two.

    • reproductionData: undefined or anything that JSON.parse can return. Normally, it is undefined. When using the --reproduce flag, this property contains the result of running JSON.parse on the contents of OUTPUT_DIR/reproductionData.json. This is used when the test function itself generates random data, such as random options for a parser.

      • If the test function is completely deterministic, ignore this property.
      • Otherwise, generate random options if it is undefined.
      • In all other cases, use its data to be able to reproduce a previous error.
  • The test function returns nothing if the test succeeded. Then, eslump will run it again with new random JavaScript code. If the --reproduce flag is used, the test function will only be run once (and if nothing fails in that run something is wrong).

  • The test function returns an object with the following properties if the test fails:

    • error: Error. The caught error. (Technically, this property can have any value, since anything can be thrown.)

    • reproductionData: Anything that JSON.stringify can handle. Optional. If the test function isn’t completely deterministic, such as when generating random options for a parser, the data needed to reproduce the error in the future must be set here. eslump will write this data to OUTPUT_DIR/reproductionData.json. That file will be read, parsed and passed to the test function when using the --reproduce flag.

    • artifacts. Object. Optional. Sometimes it can be useful to see intermediate values in addition to just the random JavaScript when a test fails, such as the AST from a parser. Each key-value pair describes a file to write:

      • The object keys are file paths relative to OUTPUT_DIR. The file will be written at OUTPUT_DIR/key.
      • The object values are the contents of the file. (The values will be passed trough the String function before writing.)

      Example:

        {
          artifacts: {
            "ast.json": JSON.stringify(ast, null, 2)
          }
        }
  • The test function must not throw errors, so be sure to wrap everything in try-catch. (eslump will catch uncaught errors, but it will not have a chance to write OUTPUT_DIR/reproductionData.json or any artifacts.)

License

MIT.

eslump's People

Contributors

aladdin-add avatar dependabot[bot] avatar lydell avatar not-an-aardvark avatar stephenwade avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

eslump's Issues

npm install error with new released node v16

env:
node: v16.0.0
npm: v7.11.1
os: macos Big Sur

➜  eslump git:(main) npm ci
npm WARN deprecated [email protected]: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated [email protected]: this library is no longer supported
npm WARN deprecated [email protected]: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated [email protected]: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated [email protected]: Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future
npm ERR! code 1
npm ERR! path /Users/weiran/repo/github/eslump/node_modules/canvas
npm ERR! command failed
npm ERR! command sh -c node-pre-gyp install --fallback-to-build
npm ERR! Failed to execute '/usr/local/bin/node /Users/weiran/.npm-global/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --module=/Users/weiran/repo/github/eslump/node_modules/canvas/build/Release/canvas.node --module_name=canvas --module_path=/Users/weiran/repo/github/eslump/node_modules/canvas/build/Release --napi_version=8 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v93' (1)
npm ERR! node-pre-gyp info it worked if it ends with ok
npm ERR! node-pre-gyp info using [email protected]
npm ERR! node-pre-gyp info using [email protected] | darwin | x64
npm ERR! node-pre-gyp WARN Using request for node-pre-gyp https download 
npm ERR! node-pre-gyp info check checked for "/Users/weiran/repo/github/eslump/node_modules/canvas/build/Release/canvas.node" (not found)
npm ERR! node-pre-gyp http GET https://github.com/Automattic/node-canvas/releases/download/v2.7.0/canvas-v2.7.0-node-v93-darwin-unknown-x64.tar.gz
npm ERR! node-pre-gyp WARN Pre-built binaries not installable for [email protected] and [email protected] (node-v93 ABI, unknown) (falling back to source compile with node-gyp) 
npm ERR! node-pre-gyp WARN Hit error tunneling socket could not be established, cause=read ECONNRESET 
npm ERR! gyp info it worked if it ends with ok
npm ERR! gyp info using [email protected]
npm ERR! gyp info using [email protected] | darwin | x64
npm ERR! gyp info ok 
npm ERR! gyp info it worked if it ends with ok
npm ERR! gyp info using [email protected]
npm ERR! gyp info using [email protected] | darwin | x64
npm ERR! gyp info find Python using Python version 3.8.2 found at "/Applications/Xcode.app/Contents/Developer/usr/bin/python3"
npm ERR! (node:43011) [DEP0150] DeprecationWarning: Setting process.config is deprecated. In the future the property will be read-only.
npm ERR! (Use `node --trace-deprecation ...` to show where the warning was created)
npm ERR! gyp info spawn /Applications/Xcode.app/Contents/Developer/usr/bin/python3
npm ERR! gyp info spawn args [
npm ERR! gyp info spawn args   '/Users/weiran/.npm-global/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py',
npm ERR! gyp info spawn args   'binding.gyp',
npm ERR! gyp info spawn args   '-f',
npm ERR! gyp info spawn args   'make',
npm ERR! gyp info spawn args   '-I',
npm ERR! gyp info spawn args   '/Users/weiran/repo/github/eslump/node_modules/canvas/build/config.gypi',
npm ERR! gyp info spawn args   '-I',
npm ERR! gyp info spawn args   '/Users/weiran/.npm-global/lib/node_modules/npm/node_modules/node-gyp/addon.gypi',
npm ERR! gyp info spawn args   '-I',
npm ERR! gyp info spawn args   '/Users/weiran/Library/Caches/node-gyp/16.0.0/include/node/common.gypi',
npm ERR! gyp info spawn args   '-Dlibrary=shared_library',
npm ERR! gyp info spawn args   '-Dvisibility=default',
npm ERR! gyp info spawn args   '-Dnode_root_dir=/Users/weiran/Library/Caches/node-gyp/16.0.0',
npm ERR! gyp info spawn args   '-Dnode_gyp_dir=/Users/weiran/.npm-global/lib/node_modules/npm/node_modules/node-gyp',
npm ERR! gyp info spawn args   '-Dnode_lib_file=/Users/weiran/Library/Caches/node-gyp/16.0.0/<(target_arch)/node.lib',
npm ERR! gyp info spawn args   '-Dmodule_root_dir=/Users/weiran/repo/github/eslump/node_modules/canvas',
npm ERR! gyp info spawn args   '-Dnode_engine=v8',
npm ERR! gyp info spawn args   '--depth=.',
npm ERR! gyp info spawn args   '--no-parallel',
npm ERR! gyp info spawn args   '--generator-output',
npm ERR! gyp info spawn args   'build',
npm ERR! gyp info spawn args   '-Goutput_dir=.'
npm ERR! gyp info spawn args ]
npm ERR! /bin/sh: pkg-config: command not found
npm ERR! gyp: Call to 'pkg-config pixman-1 --libs' returned exit status 127 while in binding.gyp. while trying to load binding.gyp
npm ERR! gyp ERR! configure error 
npm ERR! gyp ERR! stack Error: `gyp` failed with exit code: 1
npm ERR! gyp ERR! stack     at ChildProcess.onCpExit (/Users/weiran/.npm-global/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:351:16)
npm ERR! gyp ERR! stack     at ChildProcess.emit (node:events:365:28)
npm ERR! gyp ERR! stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12)
npm ERR! gyp ERR! System Darwin 20.4.0
npm ERR! gyp ERR! command "/usr/local/bin/node" "/Users/weiran/.npm-global/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "configure" "--fallback-to-build" "--module=/Users/weiran/repo/github/eslump/node_modules/canvas/build/Release/canvas.node" "--module_name=canvas" "--module_path=/Users/weiran/repo/github/eslump/node_modules/canvas/build/Release" "--napi_version=8" "--node_abi_napi=napi" "--napi_build_version=0" "--node_napi_label=node-v93"
npm ERR! gyp ERR! cwd /Users/weiran/repo/github/eslump/node_modules/canvas
npm ERR! gyp ERR! node -v v16.0.0
npm ERR! gyp ERR! node-gyp -v v7.1.2
npm ERR! gyp ERR! not ok 
npm ERR! node-pre-gyp ERR! build error 
npm ERR! node-pre-gyp ERR! stack Error: Failed to execute '/usr/local/bin/node /Users/weiran/.npm-global/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --module=/Users/weiran/repo/github/eslump/node_modules/canvas/build/Release/canvas.node --module_name=canvas --module_path=/Users/weiran/repo/github/eslump/node_modules/canvas/build/Release --napi_version=8 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v93' (1)
npm ERR! node-pre-gyp ERR! stack     at ChildProcess.<anonymous> (/Users/weiran/repo/github/eslump/node_modules/node-pre-gyp/lib/util/compile.js:83:29)
npm ERR! node-pre-gyp ERR! stack     at ChildProcess.emit (node:events:365:28)
npm ERR! node-pre-gyp ERR! stack     at maybeClose (node:internal/child_process:1067:16)
npm ERR! node-pre-gyp ERR! stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:301:5)
npm ERR! node-pre-gyp ERR! System Darwin 20.4.0
npm ERR! node-pre-gyp ERR! command "/usr/local/bin/node" "/Users/weiran/repo/github/eslump/node_modules/.bin/node-pre-gyp" "install" "--fallback-to-build"
npm ERR! node-pre-gyp ERR! cwd /Users/weiran/repo/github/eslump/node_modules/canvas
npm ERR! node-pre-gyp ERR! node -v v16.0.0
npm ERR! node-pre-gyp ERR! node-pre-gyp -v v0.15.0
npm ERR! node-pre-gyp ERR! not ok

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/weiran/.npm/_logs/2021-04-26T11_42_19_471Z-debug.log

Cannot find module './test.js'

$ eslump ./test.js output

it reports Cannot find module './test.js' -- since./test.js is a file, not a package.

I can make a repo to reproduce later(if needed).

background: I was trying to use it in cherow.

Generate more `real-world` js code

This package is generating very random code that can be very synthetic.
It will be nice to have an option that will produce more real-world js code.

For example:

  • the variable should be declared before usage
  • valid strings and regexps
  • valid code structures: no use strict in the middle of code, only common patterns to organize the code

I need this for fuzz testing of my package, that is working with the AST-tree, but currently, most of the code that is generated by eslump just break my package at the parse (espree)

Expose codegen

I need access to both AST and code for validation (and to avoid reparsing same input with shift when fuzzer already produced an AST), and also would like to reuse own fuzzer state.

The easiest way to achieve this would be to expose the codegen class from the library, so that it could be used directly by consumer.

Suggestion: use shift-validator to avoid early errors

I saw in the readme

Ability to generate code without any early errors.

The fact that shift-fuzzer doesn't do this already is a bug, but not one I'm likely to get around to fixing for a while yet. In the mean time, though, you can use shift-validator to confirm that the generated ASTs represent real programs. Its default export tells you if an AST is valid. If you generate an invalid one, you can just re-roll.

Depending on the setup this might be slower than just letting the consumer choke the invalid programs, but it's an option to consider.

Dependencies out of date

Hi there! I've noticed that most of the dependencies of this project are out of date, including one that is causing a deprecation warning in projects that depend on this one.

npm WARN deprecated [email protected]: babel-eslint is now @babel/eslint-parser.
This package will no longer receive updates.

Would you be open to pull requests to update the dependencies?

Randomize code formatting

I've been looking into using eslump for fuzz-testing ESLint. So far, it's been very useful and has already found a few bugs in rules. Thanks!

As far as I can tell, this module randomly generates an AST, but does not randomize how the AST is formatted. For example here's some code generated with eslump.generateRandomJS(). The code is gibberish, but its formatting is very consistent.

for (const fffomhowjo in this) try {
  class mnrvq {}
  debugger;
} catch (ilfbunnhxnk) {
  while (class {}) r: ;
  throw arguments;
}
export class a {}
export {} from "ä";

This makes eslump useful for testing rules that check the AST, but it's not as useful for testing rules that deal with formatting. It would be nice if eslump could randomize the AST printing as well.

Potential formatting-related randomizations:

  • Insert random linebreaks/whitespace (when the syntax allows it)
  • Parenthesize random expressions
  • Remove semicolons sometimes

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.