npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

frontend-fuzzer

v0.0.2

Published

A headless-chrome UI fuzzing setup

Downloads

3

Readme

Frontend Fuzzer

UI fuzz testing is like handing your computer to a monkey, and watching carefully to see what damage they manage to do. It is not targeted, or surgical, but if your app is already in good shape, it can give you confidence that even extremely unexpected user behavior won't cause it to break down.

This is NOT a good fuzz tester. Good fuzz testers are fast, good at guessing interesting test cases, and can reduce complicated failures to the minimum number of steps. This package does none of those things.

However, I have been unable to find any usable web UI fuzz testing tools at all -- so on the doubtful principle that something is better than nothing, here's something.

Design

Frontend Fuzzer is based on three kinds of action: fuzz actions, tests, and guards.

  • A fuzz action is a randomly selected UI fuzzing behavior, like clicking a random link, or focusing a random form element.
  • A test checks some expected invariant -- for instance "no errors were printed to the console" or "No 500s were returned by the server".
  • A guard is a guard-rail for the testing-monkey. For instance, a guard makes sure you're fuzz testing your website, not sneaking off and doing damage on the rest of the internet.

Any of these actions can either succeed or fail.

When an action fails, that failure is recorded, and the fuzzer is reset to start again from the beginning.

The fuzzing loop is as follows:

  • Reset the test (visit the site, log in, etc)
  • [a] Pick a (weighted) random fuzz action
  • Perform the action
  • If the action fails, report the failure, take a screenshot and restart
  • Otherwise, perform all the tests, in order
  • If a test fails, report the failure, take a screenshot, and restart
  • Otherwise, perform all the guards in order
  • If a guard fails, print a warning and restart
  • Otherwise, loop from [a]

Usage

npm install --save-dev frontend-fuzzer
const { fuzz, ClickAClickable, FocusAFocusable, PressAKey, UrlGuard, PageErrorTest, NetworkErrorTest } = require('frontend-fuzzer');

let options = {
  actionCount: 10000,                             // How many total fuzzing actions to take before exiting the loop
  actions: [                                      // Which fuzz actions to take, and how often
    {action: ClickAClickable, frequency: 1},
    {action: FocusAFocusable, frequency: .1},
    {action: PressAKey, frequency: 1},
  ],
  guards: [                                       // Which guards to run.
    {                                             // Some actions take extra arguments; the url guard takes a regex to match urls against.
      action: UrlGuard,
      args: [/localhost:3000\/.*/]
    }
  ],
  tests: [                                        // Which tests to run
    PageErrorTest,
    NetworkErrorTest                              // NetworkErrorTest takes arguments optionally, but we're sticking with the defaults.
  ],
  async setup(instance) {                         // Setup runs before each testing cycle; it should be idempotent.
    await instance.page.goto("localhost:3000/login")

    // Be sure to use a disposable account if testing on a live site
    // it will be filled with garbage data by the time this is over.
    await instance.page.type("input.email", process.env.LOGIN)
    await instance.page.type("input.password", process.env.PASSWORD)
    await instance.page.click(".action-submit")
    await instance.page.waitForNavigation();
    await instance.page.goto("localhost:3000/start-fuzzing-here")
  },

  // If, like me, you're running on an unconventional setup, this points puppeteer to the right chromium.
  // This is optional.
  chromiumExecutablePath: process.env.CHROME_PATH
}

// Start fuzzing!
fuzz(options);

Available Fuzz Actions

  • ClickAClickable Picks a random DOM object based on a selector, and triggers its click event. The default selector is 'a, button, [role="button"]'. This will be replaced by the action's first argument: {action: ClickAClickable, frequency: 1, args: ['a.navLink']}

  • FocusAFocusable Picks a random DOM object based on a selector, and focuses it. The default selector is 'a, button, input:not([type="hidden"]), select, textarea, [tabindex]'. This will be replaced by the action's first argument: {action: FocusAFocusable, frequency: 1, args: ['input.formField']}

  • PressAKey Triggers a random keypress. Selected by default from lowercase a-z and 0-9, but this list can be replaced by the action's first argument: {action: FocusAFocusable, frequency: 1, args: ['ArrowLeft', 'ArrowDown']}

Available Tests

  • PageErrorTest Triggers on any non-network console error.

  • NetworkErrorTest Triggers on console errors of the form "the server responded with a status of XXX". By default, it only fails for 500 statuses, but the list of failing statuses can be replaced by the test's first argument: {action: NetworkErrorTest, args: [500, 401, 422]}

Available Guards

  • UrlGuard Resets the test when the url stops matching the given regex. One argument, the url regex, is required. {action: UrlGuard, args: [/localhost:3000\/.*/]}

Writing your own actions

An action is an async function that takes a PuppeteerInstance and returns either a Failure or null.

interface PuppeteerInstance {
  browser: puppeteer.Browser
  page: puppeteer.Page
  history: BrowserEvent[]  // A list of console log errors
  options: FuzzOptions
  errorCount: number       // The number of times a fuzz action or test has failed
}

export interface Failure {
  message: string
  backtrace?: string[]
  payload?: any
}

export type Action = (instance: PuppeteerInstance, ...args: any[]) => Promise<void|Failure>

Features desperately needed

  • The actual failure reporting is inexcusably bad. Track the events, give better stack trackes, etc. Group screenshots with relevant failure data.
  • More fuzz actions: touch input, drag-n-drop, key combos, right clicks, etc.