Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e69783d
chore(deps-dev): Bump undici from 6.27.0 to 6.28.0
dependabot[bot] Aug 5, 2026
88b2c60
chore(deps-dev): Bump undici from 6.27.0 to 6.28.0 (#256)
abdulahmad307 Aug 7, 2026
c56b762
chore(deps-dev): Bump @octokit/types from 16.0.0 to 17.0.0
dependabot[bot] Aug 10, 2026
1816d68
chore(deps-dev): Bump @octokit/types from 16.0.0 to 17.0.0 (#257)
JoyceZhu Aug 10, 2026
25db39f
chore(deps): Bump rack
dependabot[bot] Aug 17, 2026
7ec4b73
chore(deps): Bump rack from 3.2.6 to 3.2.7 in /sites/site-with-errors…
kendallgassner Aug 17, 2026
e8bd96f
Overhaul plugin docs
JoyceZhu Aug 21, 2026
b9278b0
Overhaul plugin docs (#261)
JoyceZhu Aug 21, 2026
ca690ae
Address feedback on previous plugins overhaul PR
JoyceZhu Aug 21, 2026
c8c0cbb
Potential fix for pull request finding
JoyceZhu Aug 21, 2026
27bb648
Address feedback on previous plugins overhaul PR (#262)
JoyceZhu Aug 24, 2026
13faf47
Change demo video link in README
cehfisher Aug 28, 2026
6897816
Change demo video link in README (#263)
cehfisher Aug 28, 2026
7bfdfe3
Add more on NPM plugin allowlist
JoyceZhu Aug 28, 2026
91522f5
Add more on NPM plugin allowlist (#265)
JoyceZhu Aug 31, 2026
dcd232e
chore(deps-dev): Bump postcss from 8.5.15 to 8.5.28
dependabot[bot] Sep 6, 2026
b548e25
chore(deps-dev): Bump postcss from 8.5.15 to 8.5.28 (#266)
kendallgassner Sep 8, 2026
310bb9b
chore(deps-dev): Bump undici from 6.28.0 to 6.28.1 (#267)
dependabot[bot] Sep 8, 2026
fdc4f8f
chore(deps-dev): Bump @octokit/types from 17.0.0 to 18.0.0 (#268)
dependabot[bot] Sep 8, 2026
d78e663
chore(deps-dev): Bump vitest from 4.1.6 to 5.0.0 (#269)
dependabot[bot] Sep 8, 2026
e18c636
Pin GitHub Actions to commit SHAs (#270)
github-security-bot Sep 11, 2026
a2c23bd
chore(deps): Bump ruby/setup-ruby
dependabot[bot] Sep 25, 2026
c5a85a6
chore(deps): Bump ruby/setup-ruby from 1.321.0 to 1.324.0 in the gith…
JoyceZhu Sep 25, 2026
ee18f95
chore(deps): Bump ruby/setup-ruby
dependabot[bot] Oct 2, 2026
8f8670e
Port URL selector wait config from PR 223
Copilot Oct 5, 2026
b13eadf
Ensure Playwright cleanup after selector wait failures
Copilot Oct 5, 2026
cb2eb45
Wait for per-URL selectors before accessibility scans (#273)
abdulahmad307 Oct 5, 2026
cf17456
chore(deps): Bump ruby/setup-ruby from 1.324.0 to 1.327.0 in the gith…
abdulahmad307 Oct 6, 2026
bc35176
chore(deps-dev): Bump flatted from 3.3.3 to 3.4.4
dependabot[bot] Oct 6, 2026
9a4b187
chore(deps-dev): Bump brace-expansion from 5.0.7 to 5.0.12
dependabot[bot] Oct 6, 2026
0592777
chore(deps-dev): Bump source-map-js from 1.2.1 to 1.2.2
dependabot[bot] Oct 6, 2026
e01538c
chore(deps-dev): Bump undici from 6.28.1 to 6.29.0
dependabot[bot] Oct 6, 2026
de79e4c
chore(deps-dev): Bump brace-expansion from 5.0.7 to 5.0.12 (#276)
abdulahmad307 Oct 6, 2026
98a353a
chore(deps-dev): Bump source-map-js from 1.2.1 to 1.2.2 (#277)
abdulahmad307 Oct 6, 2026
7e1f8d1
chore(deps-dev): Bump flatted from 3.3.3 to 3.4.4 (#275)
abdulahmad307 Oct 6, 2026
0110080
chore(deps-dev): Bump undici from 6.28.1 to 6.29.0 (#278)
abdulahmad307 Oct 6, 2026
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
23 changes: 23 additions & 0 deletions .github/ISSUE_TEMPLATE/allowlist-npm-plugin-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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
id: npm-package
attributes:
label: Link to your plugin on NPM
validations:
required: true
- type: input
id: github-repo-link
attributes:
label: Link to the source code for your plugin on GitHub
validations:
required: true
- type: textarea
id: plugin-description
attributes:
label: What does your plugin do?
Comment thread
abdulahmad307 marked this conversation as resolved.
Comment thread
abdulahmad307 marked this conversation as resolved.
Comment thread
abdulahmad307 marked this conversation as resolved.
description: Please provide a brief description of your plugin's functionality.
validations:
required: true
4 changes: 4 additions & 0 deletions .github/actions/find/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ https://primer.style
https://primer.style/octicons/
```

#### `url_configs`

**Optional** Stringified JSON array of per-URL configuration objects. Each object must include a `url` and may include `excludeSelectors` (selectors to exclude from Axe) and `waitForSelectors` (selectors that must become visible within 30 seconds before scanning). When provided, this input takes precedence over `urls`.

#### `auth_context`

**Optional** Stringified JSON object containing `username`, `password`, `cookies`, and/or `localStorage` from an authenticated session. For example: `{"username":"some-user","password":"correct-horse-battery-staple","cookies":[{"name":"theme-preference","value":"light","domain":"primer.style","path":"/"}],"localStorage":{"https://primer.style":{"theme-preference":"light"}}}`
Expand Down
3 changes: 1 addition & 2 deletions .github/actions/find/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ inputs:
required: false
multiline: true
url_configs:
description: "Stringified JSON array of URL config objects, each with a 'url' field and an optional 'excludeSelectors' field (array of CSS selectors to exclude from the Axe scan for that URL). When provided, takes precedence over the 'urls' input."
description: "Stringified JSON array of URL config objects, each with a 'url' field and optional 'excludeSelectors' (selectors to exclude from Axe) and 'waitForSelectors' (selectors that must be visible before scanning) fields. When provided, takes precedence over the 'urls' input."
required: false
auth_context:
description: "Stringified JSON object containing 'username', 'password', 'cookies', and/or 'localStorage' from an authenticated session"
Expand All @@ -25,7 +25,6 @@ inputs:
color_scheme:
description: 'Playwright colorScheme setting: https://playwright.dev/docs/api/class-browser#browser-new-context-option-color-scheme'
required: false

outputs:
findings_file:
description: 'Path to a JSON file containing the list of potential accessibility gaps'
Expand Down
86 changes: 50 additions & 36 deletions .github/actions/find/src/findForUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import {loadPlugins, invokePlugin} from './pluginManager/index.js'
import {getScansContext} from './scansContextProvider.js'
import * as core from '@actions/core'

const SELECTOR_WAIT_TIMEOUT = 30000

export async function findForUrl(
urlConfig: UrlConfig,
authContext?: AuthContext,
includeScreenshots: boolean = false,
reducedMotion?: ReducedMotionPreference,
colorScheme?: ColorSchemePreference,
): Promise<Finding[]> {
const {url, excludeSelectors} = urlConfig
const {url, excludeSelectors, waitForSelectors} = urlConfig
const browser = await playwright.chromium.launch({
headless: true,
executablePath: process.env.CI ? '/usr/bin/google-chrome' : undefined,
Expand All @@ -25,51 +27,63 @@ export async function findForUrl(
...(reducedMotion ? {reducedMotion} : {}),
...(colorScheme ? {colorScheme} : {}),
}
const context = await browser.newContext(contextOptions)
const page = await context.newPage()
await page.goto(url)
let context: playwright.BrowserContext | undefined
try {
context = await browser.newContext(contextOptions)
const page = await context.newPage()
await page.goto(url)
await Promise.all(
(waitForSelectors ?? []).map(selector =>
page.locator(selector).waitFor({state: 'visible', timeout: SELECTOR_WAIT_TIMEOUT}),
),
)

const findings: Finding[] = []
const addFinding = async (findingData: Finding) => {
let screenshotId
if (includeScreenshots) {
screenshotId = await generateScreenshots(page)
const findings: Finding[] = []
const addFinding = async (findingData: Finding) => {
let screenshotId
if (includeScreenshots) {
screenshotId = await generateScreenshots(page)
}
findings.push({...findingData, screenshotId})
}
findings.push({...findingData, screenshotId})
}

try {
const scansContext = getScansContext()
try {
const scansContext = getScansContext()

if (scansContext.shouldRunPlugins) {
const plugins = await loadPlugins()
for (const plugin of plugins) {
if (scansContext.scansToPerform.includes(plugin.name)) {
core.info(`Running plugin: ${plugin.name}`)
await invokePlugin({
plugin,
page,
addFinding,
})
} else {
core.info(`Skipping plugin ${plugin.name} because it is not included in the 'scans' input`)
if (scansContext.shouldRunPlugins) {
const plugins = await loadPlugins()
for (const plugin of plugins) {
if (scansContext.scansToPerform.includes(plugin.name)) {
core.info(`Running plugin: ${plugin.name}`)
await invokePlugin({
plugin,
page,
addFinding,
})
} else {
core.info(`Skipping plugin ${plugin.name} because it is not included in the 'scans' input`)
}
}
}
}

if (scansContext.shouldPerformAxeScan) {
await runAxeScan({page, addFinding, excludeSelectors})
}
if (scansContext.shouldPerformAxeScan) {
await runAxeScan({page, addFinding, excludeSelectors})
}

if (scansContext.shouldPerformAccesslintScan) {
await runAccesslintScan({page, addFinding})
if (scansContext.shouldPerformAccesslintScan) {
await runAccesslintScan({page, addFinding})
}
} catch (e) {
core.error(`Error during accessibility scan: ${e}`)
}
return findings
} finally {
try {
await context?.close()
} finally {
await browser.close()
}
} catch (e) {
core.error(`Error during accessibility scan: ${e}`)
}
await context.close()
await browser.close()
return findings
}

async function runAxeScan({
Expand Down
7 changes: 7 additions & 0 deletions .github/actions/find/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ function loadUrlConfigs() {
if (typeof item !== 'object' || item === null || typeof item.url !== 'string') {
throw new Error("Each entry in 'url_configs' must be an object with a 'url' string field.")
}
if (
item.waitForSelectors !== undefined &&
(!Array.isArray(item.waitForSelectors) ||
item.waitForSelectors.some((selector: unknown) => typeof selector !== 'string'))
) {
throw new Error("Each 'waitForSelectors' field in 'url_configs' must be an array of CSS selector strings.")
}
}

return parsed as UrlConfig[]
Expand Down
1 change: 1 addition & 0 deletions .github/actions/find/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ export type ColorSchemePreference = 'light' | 'dark' | 'no-preference' | null
export type UrlConfig = {
url: string
excludeSelectors?: string[]
waitForSelectors?: string[]
}
Loading
Loading