Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/ISSUE_TEMPLATE/allowlist-npm-plugin-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Allowlist Third Party NPM Plugin Request
description: Fill out the details to request allowlisting your third party plugin hosted on NPM in the scanner.
labels: 'allowlist-plugin-request'
body:
- type: input
attributes:
label: Link to your plugin on NPM
validations:
required: true
- type: textarea
attributes:
label: What does your plugin do?
description: Please provide a brief description of your plugin's functionality.
validations:
required: true
52 changes: 33 additions & 19 deletions PLUGINS.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Plugins

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](https://github.com/github/accessibility-scanner/tree/main/.github/scanner-plugins) for examples of plugin structure.
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.

Some plugins come built-in with the scanner and can be enabled via [actions inputs](https://github.com/github/accessibility-scanner/tree/main/action.yml#L48-L50).
Some first-party plugins come built-in with the scanner repository and can be enabled via [actions inputs](https://github.com/github/accessibility-scanner/tree/main/action.yml#L48-L50).
There are also some [allowlisted](https://github.com/github/accessibility-scanner/blob/7ec4b73ca2fdb471ad6f71a48a88e7b1a84ab3ad/.github/actions/find/src/pluginManager/index.ts#L92) first-party plugins hosted in a different repository; we also have the ability to support loading approved third-party plugins [from NPM](./loading-plugins-from-npm-packages).

## How plugins work
## How to create plugins

### Plugin folder structure for all plugins

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.
- Each plugin should have one `index.ts` OR `index.js` file inside its folder.
- The `index.ts/index.js` file must export a `name` field. This is the name used to pass to the `scans` input. So if the plugin exports a name value of `my-custom-plugin` and we pass the following to the scanner action inputs: `scans: ['my-custom-plugin']`, it would cause the scanner to only run that plugin.
Comment on lines +10 to +13
- The `index.ts/index.js` file must export a default function (see [below](./default-function-api)). This is the function that the scanner uses to run the plugin. This can be an async function.

### Default function API

Expand All @@ -22,15 +27,23 @@ A async function (you must use `await` or `.then` when invoking this function) t

- An object that should match the [`Finding` type](https://github.com/github/accessibility-scanner/blob/main/.github/actions/find/src/types.d.ts#L1-L9).

## How to create plugins
### NPM-hosted plugins

As mentioned above, plugins need to exist under `./.github/scanner-plugins`. For a plugin to work, it needs to meet the following criteria:
Create a repository containing the required files/functions mentioned above, then publish it as an NPM package (with a public URL).

- Each separate plugin should be contained in it's own directory in `./.github/scanner-plugins`. For example, `./.github/scanner-plugins/plugin-1` would be 1 plugin loaded by the scanner.
- Each plugin should have one `index.ts` OR `index.js` file inside its folder.
- The `index.ts/index.js` file must export a `name` field. This is the name used to pass to the `scans` input. So if the plugin exports a name value of `my-custom-plugin` and 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.js` file 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:
Once the public URL is live, please fill out [this issue template](./.github/ISSUE_TEMPLATE/allowlist-npm-plugin-request.yml) so we can allowlist your plugin in future versions of the scanner.

To then enable an NPM plugin in the `scans` input, follow the instructions in the [loading plugins from NPM](./loading-plugins-from-npm-packages) section.

An example of a plugin which can be loaded from NPM is our [alt text scanning plugin](https://github.com/github/accessibility-scanner-alt-text-plugin).

### Local plugins (located in your repository)

If you wish, you can create _local plugins_ by creating a folder containing plugin code in `./.github/scanner-plugins/`. Each separate plugin should be contained in its own directory. For example, `./.github/scanner-plugins/plugin-1` would be 1 plugin loaded by the scanner.

Enabled local plugins (i.e. those listed in the `scans` Action input) are dynamically loaded by the scanner when it runs. The scanner will search for a `./.github/scanner-plugins` folder in your repo (where you run the workflow from). If it finds one, 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.

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:

```yaml
jobs:
Expand All @@ -45,16 +58,18 @@ jobs:

## Loading plugins from NPM packages

In 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.
> [!IMPORTANT]
> NPM package loading requires scanner v3.4.1 or later.

The scanner can install and load plugins published as NPM packages. This avoids having to vendor a plugin's source into your repo.

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 against `scans`, 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](.github/actions/find/src/pluginManager/index.ts#L91) may be loaded from NPM. Any other package is skipped with a warning.
Only the set of [approved packages](.github/actions/find/src/pluginManager/index.ts#L91) may be loaded from NPM. Any other package is skipped with a warning.

```yaml
jobs:
Expand All @@ -68,11 +83,10 @@ jobs:
["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 a `version` to 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.json` in your repo (the plugin reads its config relative to the repo you run the workflow from).
> [!NOTE]
> - Packages are installed at runtime with `npm install --ignore-scripts`, so install/postinstall scripts in the package will not run. Pin a `version` to 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.json` in your repo (the plugin reads its config relative to the repo you run the workflow from).

## Things to look out for

Expand Down
Loading