Skip to content

feat(DEVA11Y-657): composite GitHub Action (v1 scan+report)#1

Open
Crash0v3rrid3 wants to merge 4 commits into
mainfrom
feat/DEVA11Y-463-action-v1
Open

feat(DEVA11Y-657): composite GitHub Action (v1 scan+report)#1
Crash0v3rrid3 wants to merge 4 commits into
mainfrom
feat/DEVA11Y-463-action-v1

Conversation

@Crash0v3rrid3

Copy link
Copy Markdown
Collaborator

DEVA11Y-657 — Composite GitHub Action (v1: scan + report)

Ships the public composite Action for the CI/CD accessibility flow (Epic DEVA11Y-463, ADR-0035/0036).

A developer comments @AccessibilityDevTools on a PR → this Action runs the published @browserstack/accessibility-devtools CLI in PR-scan mode, authenticated by a BrowserStack Service Account key (zero GitHub credentials on the runner) → the CLI sends findings to linter-server, which normalizes + HMAC-signs them (scanProof) → the runner forwards {report, scanProof} (plus a best-effort Actions OIDC token) to the ci-review-service, which verifies and posts as the GitHub App.

What's in this PR

  • action.yml — composite Action with the ADR-0035 Appendix A input surface (username, access-key, comment, check-gate, fail-on-severity, inline-suggestions, sarif, comment-mode, remediation, ai-agent) and outputs (result, error-count, warning-count, findings-count, comment-url, sarif-file).
  • examples/browserstack-a11y.yml — reference consumer workflow (on: issue_comment, permissions: contents/pull-requests read + id-token write).
  • .github/workflows/release.yml — versioned release/bundle pipeline (SHA-pinned CLI, tagged dist).
  • README.md, LICENSE, .gitignore.

Fork-PR safety

"Diff, don't build" — no npm install/postinstall of untrusted head code; degrades to skip when OIDC/secrets are restricted.

Status / dependencies

  • Draft — depends on the linter-server + CLI (a11y-linter #406, story DEVA11Y-655) and ci-review-service (a11y-linter #407, story DEVA11Y-656) landing, plus the real GitHub App credentials + Marketplace listing.
  • CLI version pin / Marketplace publish to be finalized before GA.

🤖 Generated with Claude Code

Crash0v3rrid3 and others added 2 commits July 8, 2026 13:02
Scaffold the public browserstack-accessibility-devtools-action repo per
ADR-0035 Appendix A (GitHub App + Service Account model).

- action.yml: composite action. Inputs username/access-key (required, secret),
  comment, check-gate, fail-on-severity, inline-suggestions, sarif, comment-mode,
  remediation, ai-agent (validated ^[A-Za-z0-9-]+(\[bot\])?$). Outputs result,
  error-count, warning-count, findings-count, comment-url, sarif-file. Hardcoded:
  @AccessibilityDevTools trigger, diff scope, deva11y.config.* inference, commenter
  write/maintain/admin permission gate. Steps implement fork-PR "diff, don't build"
  safety (no install/postinstall of head code; skip when OIDC/secrets restricted),
  pinned npx CLI (placeholder 1.3.0), Actions OIDC mint (aud placeholder
  browserstack-accessibility-devtools), and POST to ci-review-service /v1/findings
  with {platform,repo,prNumber,headSha,findings,scanProof,oidcToken} + X-CI-Platform
  header. The Action holds no GitHub write credential; the App posts server-side.
- examples/browserstack-a11y.yml: reference consumer workflow (issue_comment,
  read-only token + id-token: write, concurrency, if-guard).
- .github/workflows/release.yml: release stub (esbuild dist build guard, action.yml
  validation, manual org-admin Marketplace publish note).
- README.md: public customer README (v1 = scan + report; agent hand-off preview
  off by default; homepage browserstack.com).
- LICENSE (MIT), .gitignore.

Placeholders pending provisioning: CLI version 1.3.0, OIDC audience and service URL
(https://devtools-a11y-linter.browserstack.com), GitHub App. Draft PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…y token

The commenter permission pre-check called GET /collaborators/{user}/permission,
which requires push access. The documented least-privilege workflow token
(contents: read) cannot call it, so the request 403s, the `|| echo none`
fallback yielded "none", and the action refused to scan for EVERY commenter —
non-functional under its own recommended setup.

Make the client-side check best-effort: fast-refuse only on a DEFINITIVE
non-privileged role; on an inconclusive call (the read-only-token case) proceed
and rely on the authoritative server-side fail-closed permission gate in the
ci-review-service (ADR-0035 §5). The runner is the customer's and was never the
security boundary.

Also correct the preflight fork-safety comments: issue_comment workflows run in
base-repo context with secrets available even for fork PRs, so cred-absence is
not what protects fork PRs — the permission gate + "diff, don't build" are.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Crash0v3rrid3

Copy link
Copy Markdown
Collaborator Author

Code review

Reviewed the composite action (bugs + security; no CLAUDE.md in this repo). Found 1 functional issue — now fixed in 222bd37:

  1. Commenter permission gate hard-failed under the documented read-only token (P1). GET /repos/{owner}/{repo}/collaborators/{username}/permission requires push access; the recommended least-privilege workflow token (contents: read, pull-requests: read) cannot call it, so it 403'd, the || echo none fallback resolved to none, and the action refused to scan for every commenter — non-functional under its own recommended setup.

    Fix: the client-side check is now best-effort — it fast-refuses only on a definitive non-privileged role and, on an inconclusive call (the read-only-token case), proceeds and defers to the authoritative server-side fail-closed permission gate in the ci-review-service (ADR-0035 §5). The runner is the customer's and was never the security boundary.

    # read-only token the call fails (gh exits non-zero) and we must NOT refuse.
    if perm="$(gh api "repos/${repo}/collaborators/${actor}/permission" --jq '.permission' 2>/dev/null)"; then
    echo "Commenter ${actor} has permission: ${perm}"
    case "$perm" in
    admin|maintain|write)
    echo "allowed=true" >> "$GITHUB_OUTPUT" ;;
    *)
    echo "::warning::${actor} lacks write/maintain/admin permission; refusing to scan."
    echo "allowed=false" >> "$GITHUB_OUTPUT" ;;
    esac
    else
    # Could not read the commenter's permission (expected with the documented
    # read-only GITHUB_TOKEN, which cannot call the collaborators API). Do not

Also corrected the preflight fork-safety comments: issue_comment workflows run in base-repo context with secrets available even for fork PRs, so cred-absence is not what protects fork PRs — the permission gate + "diff, don't build" (no PR-head-code execution) are.

Verified clean: OIDC request URL separator (&audience= onto GitHub's already-?-bearing request URL), jq --argjson prNumber body assembly, composite-step failure propagation (scan failure skips oidc/submit; the always() report step fails closed), and no ${{ github.event.* }} interpolated into any run: block (all untrusted input routed via env + jq over GITHUB_EVENT_PATH). All YAML parses; the validate-action structural checks pass.

No remaining high-confidence issues.

Verdict: PASS (draft — placeholders for CLI version, service URL/audience, and the pinned action SHA remain, as expected pre-GA).

🤖 Generated with Claude Code

@Crash0v3rrid3 Crash0v3rrid3 marked this pull request as ready for review July 10, 2026 05:00
Crash0v3rrid3 and others added 2 commits July 10, 2026 10:45
- example workflow + README now reference @v1 (with a note to SHA-pin for
  production) instead of the @<commit-sha> placeholder
- add .github/workflows/ci.yml: PR gate validating action.yml structure +
  YAML well-formedness (the repo previously had no PR checks)

CLI version pin (action.yml) stays a documented release-time value — no
pr-scan CLI version is published to npm yet; the release step pins the real
version. GitHub App id / service URL remain provisioning placeholders.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-URL comments

@browserstack/accessibility-devtools 1.5.0 is published (npmjs `latest`) and
includes PR-scan mode, so pin CLI_VERSION 1.3.0 -> 1.5.0. The OIDC audience
(browserstack-accessibility-devtools) already matches the deployed
ci-review-service verifier config, and SERVICE_URL is the real global ingress;
drop the stale PLACEHOLDER comments accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread README.md
Comment on lines +12 to +15
agent you already use, which then acts under _your_ credentials. BrowserStack runs no
AI model and never handles your AI keys. **v1 is scan + report**; whether the agent
reviews or fixes is up to that agent (see "Optional: agent hand-off" below for what
works today).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the parts "Browserstack runs no...what works today"

Comment thread README.md
reviews or fixes is up to that agent (see "Optional: agent hand-off" below for what
works today).

Learn more: <https://browserstack.com>

@aveek-bs aveek-bs Jul 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread README.md
@@ -1,2 +1,166 @@
# browserstack-accessibility-devtools-action
A package to support Accessibility Devtools Action
# BrowserStack Accessibility for GitHub

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BrowserStack Accessibility DevTools for GitHub

Comment thread README.md
Comment on lines +30 to +31
A Service Account is a non‑personal, admin‑managed key built for CI — it uses
no user license and can be rotated or revoked independently. (Enterprise plan.)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove these lines.

Comment thread README.md
Add `.github/workflows/browserstack-a11y.yml`:

```yaml
name: BrowserStack Accessibility

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name: BrowserStack Accessibility DevTools

steps:
# Use the v1 major tag for automatic patch/minor updates, or pin to a
# full commit SHA for supply-chain hardening (recommended for production).
- uses: browserstack/browserstack-accessibility-devtools-action@v1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid mentioning v1 here? So that it always uses the latest version of the action

Comment thread README.md

Add `.github/workflows/browserstack-a11y.yml`:

```yaml

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Crash0v3rrid3 Can we add the ready to copy version here instead of a separate link?

Comment thread README.md

## Inputs

| Input | Default | Description |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Crash0v3rrid3 Are we supporting all of these inputs?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which input toggles line-by-line commenting?

Comment thread README.md
| `comment` | `true` | Post a summary findings comment on the PR. |
| `check-gate` | `true` | Publish a Check Run / commit status for the PR. |
| `fail-on-severity` | `error` | Fail the check at this severity and above: `error`, `warning`, or `none`. |
| `inline-suggestions` | `false` | Add one‑click suggestion blocks on offending lines. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Crash0v3rrid3 What do the suggestion blocks contain?

Comment thread README.md
ai-agent: coderabbitai # BrowserStack posts "@coderabbitai"
```

What works today (honest status — this is a preview, off by default):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads like an internal note. Instead of this section, let's just add a generic disclaimer that this feature will be supported if the AI agent accept triggers from a bot/App.

"- If your agent ignores bot authors by default, allow‑list the BrowserStack
Accessibility
bot in its config." -> Add this as a pre-requisite under agent hand-off section.

Comment thread README.md
> **v1 is scan + report.** A guaranteed AI-_fix_ path is not part of v1; the hand-off
> above is a preview and is a **silent no-op** if the agent isn't configured to accept it.

## Privacy & security

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this section.

Comment thread README.md
your runner.
- The scan reads only the pull request's changed files.

## Support

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just add one link https://www.browserstack.com/support. Remove the others.

Comment thread README.md
- Product & docs: <https://browserstack.com>
- Accessibility API reference (including rate limits): <https://www.browserstack.com/docs/accessibility/api>

## License

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove section

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants