The plugin system allows teams to create custom scans/tests to run on their pages. An example of this is Axe interaction tests. In some cases, it might be desirable to perform specific interactions on elements of a given page before doing an Axe scan. These interactions are usually unique to each page that is scanned, so it would require the owning team to write a custom plugin that can interact with the page and run the Axe scan when ready. See the existing plugins under .github/scanner-plugins for examples of plugin structure.
Some plugins come built-in with the scanner and can be enabled via actions inputs.
Plugins are dynamically loaded by the scanner when it runs. The scanner will look into the ./.github folder in your repo (where you run the workflow from) and search for a scanner-plugins folder. If it finds it, it will assume each folder under that is a plugin, and attempt to load the index.ts (first) or index.js (second) file inside it. Once loaded, the scanner will invoke the exported default function from the index.js/index.ts file.
When the default function is invoked, the following arguments are passed to the function:
This is the playwright page instance.
A async function (you must use await or .then when invoking this function) that will add a finding to the list (findings are used to generate and file issues). It will also generate a screenshot and add the screenshotId to the finding data if includeScreenshots is true in the scanner action input. It has the following arguments:
- An object that should match the
Findingtype.
As mentioned above, plugins need to exist under ./.github/scanner-plugins. For a plugin to work, it needs to meet the following criteria:
- Each separate plugin should be contained in it's own directory in
./.github/scanner-plugins. For example,./.github/scanner-plugins/plugin-1would be 1 plugin loaded by the scanner. - Each plugin should have one
index.tsORindex.jsfile inside its folder. - The
index.ts/index.jsfile must export anamefield. This is the name used to pass to thescansinput. So if the plugin exports a name value ofmy-custom-pluginand we pass the following to the scanner action inputs:scans: ['my-custom-plugin'], it would cause the scanner to only run that plugin. - The
index.ts/index.jsfile must export a default function. This is the function that the scanner uses to run the plugin. This can be an async function. - In your workflow file, before the scanner step, add
- uses: actions/checkout@v6(or whatever the current version is). This allows the current repository's files (where your custom plugin's file exists) to be read:
jobs:
accessibility_scanner:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: github/accessibility-scanner@v3
with:
# ... the rest of the workflow setupIn addition to local plugins under ./.github/scanner-plugins, the scanner can install and load plugins published as NPM packages. This avoids having to vendor a plugin's source into your repo.
NPM package loading requires scanner v3.4.1 or later.
To use an NPM plugin, pass an object (instead of a plain string) in the scans input with the following fields:
name— the plugin name exported by the package (used to match againstscans, same as local plugins).package— the NPM package name to install.version— (optional) a version or dist-tag to pin. If omitted, the latest version is installed.
Only the set of first-party packages may be loaded from NPM. Any other package is skipped with a warning.
jobs:
accessibility_scanner:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: github/accessibility-scanner@v3
with:
scans: |
["axe", {"name": "alt-text-scan", "package": "@github/accessibility-scanner-alt-text-plugin", "version": "1.1.0"}]Notes:
- Packages are installed at runtime with
npm install --ignore-scripts, so install/postinstall scripts in the package will not run. Pin aversionto avoid silently picking up future releases. - If an NPM plugin shares a name with a built-in or local plugin, the built-in/local plugin wins and the NPM one is skipped.
- Plugin configuration works the same as local plugins: place a config-only folder at
./.github/scanner-plugins/<name>/config.jsonin your repo (the plugin reads its config relative to the repo you run the workflow from).
- Plugin names should be unique. If multiple plugins have the same name, and the
scansinput array contains this name, all the plugins with that name will run. However, this is not advised because if you want to turn off one plugin, you'll have to go back and change that plugin name.