Skip to content

Latest commit

 

History

History
193 lines (146 loc) · 14 KB

File metadata and controls

193 lines (146 loc) · 14 KB

CLA Bot — Product & Behavior Spec

This document is the authoritative behavior contract for the CLA Bot application. It covers product requirements, page routes, scenario catalog, webhook/PR flows, and test coverage expectations.

Product Requirements

  • If a contributor has signed a non-current CLA version, they must re-sign before being considered compliant.
  • Contributor compliance status is evaluated per org using the contributor's latest signed version for that org.
  • Admins can define org-scoped bypass lists for both GitHub users and GitHub Apps/system bots that should always receive a passing CLA check.
  • If a contributor has open pull requests and their signature becomes outdated after a CLA update, checks may need to be re-opened/re-evaluated and set to failing until re-signing is completed.
  • After a contributor signs/re-signs the latest CLA, the app schedules an async workflow that updates their open PR CLA checks to success and removes stale CLA prompt comments.
  • When an org is activated/deactivated, the app schedules an async workflow to re-check open PRs for that org so checks converge to the new enforcement mode.
  • GitHub is the user-management source of truth for the app.
  • The app has no local signup/password user-management system; DB user rows are GitHub-linked identity mirrors only.
  • Authentication/session management is stateless JWT-based (HTTP-only cookie + signed JWT with jti).
  • Users can only log in via GitHub OAuth.
  • Contributors can view and download every CLA version they have signed.
  • Admins can download both current and archived CLA versions for managed orgs.

Page Spec

This section is the behavior contract for UI routes.

Route Purpose Signed-out behavior Signed-in behavior Key actions
/ Marketing landing page Public page Same CTA to /auth/signin, example CLA link to /sign/fiveonefour
/auth/signin Start sign-in flow Shows GitHub sign-in CTA Same Sends user to /api/auth/github?returnTo=...; returnTo is sanitized to internal paths only
/dashboard Mode selector page Public page Same + session shown in header Navigate to /admin or /contributor
/admin Org admin overview Shows "Sign in required" card Lists organizations user can administer; shows install CTA when none are authorized Install app (/api/github/install), open org manage pages
/admin/[orgSlug] Org CLA management If data unavailable, shows "Organization not found" UI Shows org details, CLA version, signers, archives, bypass list, branch-protection reminder Edit/save CLA text with live markdown preview modes (Edit, Split, Preview), activate/deactivate bot, copy signing link, inspect signers/archives, manage bypass users and app/bot slugs, download current/archived CLA text, share tab links via ?tab=cla|signers|archives|bypass
/contributor Contributor agreement dashboard Shows "Sign in required" card Lists signed CLA history grouped by org status Re-sign prompts for outdated orgs, links to /sign/[orgSlug], download previously signed CLA records
/sign/[orgSlug] CLA read/sign page Shows sign-in required (or org not found) Shows signed state, or sign/re-sign workflow Requires scroll-to-bottom before sign button enables; handles inactive org warning
/terms Legal terms page Public page Same Documents signing/enforcement terms and branch-protection requirement
/privacy Privacy policy page Public page Same Documents collected data, retention, and rights workflow

Scenario Catalog

1) First login with the app

  • Users can only authenticate via GitHub OAuth.
  • OAuth flow validates state and redirects back to a sanitized internal returnTo.
  • Session is maintained with an HTTP-only cookie containing a JWT payload (userId, githubUsername, role, jti).
  • GitHub remains the source of truth for identity. The DB stores app-side profile/session linkage metadata, not standalone account management.

2) User selects Admin

  • If signed out: user sees auth-required state and can start GitHub login.
  • If signed in and authorized on at least one installed account: user sees the account list and install button.
  • If signed in but authorized on zero installed accounts: user sees install CTA for GitHub App flow.
  • Newly installed accounts start with no CLA text. Maintainers must publish their own CLA before external contributors can sign.
  • Org manage tabs are URL-synced (/admin/[orgSlug]?tab=...) so state is shareable and works with browser back/forward navigation.

3) User selects Contributor

  • User sees signed CLA records including org, version label/hash prefix, and signed timestamp.
  • User can view full signing history and download each signed record they own.
  • Org compliance status uses the latest signature per org; older historical rows do not keep an org in a warning state once the latest version is signed.
  • User can open a CLA from the list and view full language on /sign/[orgSlug].
  • Data detail: full SHA-256 hash is persisted in DB; UI currently shows the short 7-character version label.

4) Admin creates a new CLA version

  • Saving CLA updates current text/hash.
  • While editing, maintainers can preview rendering before save (Edit, Split, Preview modes; desktop defaults to split view).
  • Existing signatures remain historical; users on previous hash are treated as outdated and must re-sign.
  • If no one has signed prior versions, only current hash/text changes.
  • If prior versions were signed, historical signed versions remain in archives/signatures.
  • Saving CLA schedules an async workflow recheck of open pull requests; non-member contributors with missing/outdated signatures receive failing checks and updated signing comments once the workflow run completes.

5) Contributor opens a PR

  • Org member: check passes, no CLA comment.
  • Personal-account repository owner: check passes, no CLA comment.
  • User or app/bot on org bypass list: check passes, no CLA comment.
  • Non-member + current signature: check passes, no CLA comment.
  • Non-member + outdated signature: check fails, re-sign comment posted.
  • Non-member + never signed: check fails, sign prompt comment posted.

6) Contributor signs/re-signs CLA

  • Signature is stored with org, user, full CLA hash, accepted hash, assent metadata, immutable GitHub ID at signing time, timestamp, email provenance, and session evidence fields.
  • If repo + pr is provided, the signer must match that PR author before targeted PR sync is applied.
  • After signing/re-signing, the app schedules an async workflow to sync open PRs authored by that contributor in the org: latest CLA check runs are updated to success and stale CLA prompt comments are deleted.

7) Signed CLA versions cannot be deleted

  • There is no route to delete signed CLA archives/signature history.
  • Signed version history behaves as append-only.

8) CLA downloads

  • Contributors can download CLA versions from their own signing history.
  • Admins can download both current and archived CLA versions for orgs they administer.
  • Download endpoints enforce ownership/authorization and do not expose records across users/orgs.

9) Merge queue support

  • When a repository uses GitHub merge queues, the CLA bot auto-passes the check on the merge queue head SHA because CLA compliance was already verified on the original pull request before it entered the queue.
  • The bot handles merge queue events via two webhook paths:
    • check_suite with action: "requested" and head_branch matching gh-readonly-queue/ prefix: GitHub automatically delivers these to apps with checks permission. The bot creates a passing check run on the check suite's head SHA. Non-merge-queue branches and non-requested actions are ignored.
    • merge_group with action: "checks_requested": handled if the app is subscribed to merge_group events. The bot creates a passing check run on the merge group's head SHA. Non-checks_requested actions are ignored.
  • Missing required payload fields return 400.

10) Additional scenarios commonly missed

  • Org deactivated/uninstalled: signing blocked; webhook events set passing CLA checks and remove managed CLA prompts so PRs are not blocked by CLA while inactive.
  • Activating or deactivating an org schedules an async open-PR recheck workflow so existing PR checks/comments converge automatically.
  • Updating either bypass section (users or app/bots) schedules async open-PR recheck so existing PRs converge to the latest policy.
  • /recheck authorization: allowed for PR author, org member, or maintainer; unauthorized users are blocked.
  • OAuth and install redirects sanitize returnTo to prevent open redirects.
  • Webhook hardening: production signature verification and delivery de-duplication.
  • Standard error paths: unauthorized, forbidden, missing org, invalid payload combinations.

11) OAuth/session lifecycle edge cases

  • OAuth callback state mismatch/expired state cookie: sign-in fails safely and redirects back to /auth/signin?error=....
  • GitHub token exchange or profile fetch failure: sign-in fails safely and no session cookie is issued.
  • Explicit logout clears JWT cookie; expired/invalid JWT is treated as signed-out.

12) /recheck command behavior edge cases

  • /recheck is only processed on PR issue comments; non-PR issue comments are ignored.
  • Non-command comments (or non-created comment events) are ignored.
  • If PR head SHA cannot be resolved in production, /recheck fails with an error instead of guessing.

13) Webhook delivery/idempotency scenarios

  • Duplicate x-github-delivery IDs are ignored via persistent DB-backed delivery tracking to reduce duplicate check/comment churn across process restarts.
  • Missing/invalid webhook signature is rejected in production when GITHUB_WEBHOOK_SECRET is configured.
  • Missing required payload fields return 400 and do not mutate DB/check state.

14) Installation lifecycle scenarios

  • Installation created or unsuspend: account row is created/reactivated, installation ID refreshed, and installation target metadata (organization vs user) is persisted.
  • New installations are initialized with empty CLA text and cla_text_sha256 = null (no built-in agreement/template is auto-published).
  • Installation deleted or suspend: account is deactivated and installation ID cleared.
  • Installation repository-change events refresh installation linkage.

15) Access-control scenarios

  • In production, org installs require live GitHub org-admin verification.
  • In production, personal-account installs are authorized when the signed-in GitHub user matches the installation target account.
  • In local dev/test, org-admin verification is relaxed to keep tests deterministic.
  • /admin/[orgSlug] and /sign/[orgSlug] handle unknown orgs with explicit not-found states.

System Behavior Spec (PR/Webhook Flow)

  • Pull request webhook checks whether PR author is an org member or has signed current CLA.
  • PR signature resolution is keyed by immutable GitHub user ID when available (username is fallback only).
  • Contributor dashboard status uses the latest stored signature per org to determine current/outdated state in UI.
  • Outcomes:
    • Org member: passing check, no CLA comment.
    • Bypass-listed user/app/bot: passing check, no CLA comment.
    • Signed current CLA: passing check, no CLA comment.
    • Unsigned/outdated signature: failing check + bot comment with signing URL.
  • App/bot bypass matching is slug-based and treats <slug> and <slug>[bot] as equivalent actor forms.
  • When CLA text changes, contributors on older signatures are marked as requiring re-sign; open PRs may require check re-evaluation and failure until re-signing.
  • After signing/re-signing, an async workflow updates signer-authored open PR CLA checks to success and removes stale CLA prompt comments.
  • Activating/deactivating CLA enforcement schedules async open-PR rechecks; inactive mode converges CLA checks to success and clears managed CLA prompt comments.
  • CLA bot comment updates/deletions are restricted to CLA-managed comments tagged with an internal signature marker, preventing edits to third-party bot comments.
  • Merge queue support: the CLA bot auto-passes checks for merge queue commits. It responds to both check_suite.requested events on gh-readonly-queue/ branches (delivered automatically to apps with checks permission) and merge_group.checks_requested events (if subscribed). CLA compliance was already enforced on the individual pull request.
  • Repository maintainers must require CLA Bot / Contributor License Agreement in GitHub branch protection/rulesets for merge blocking to be enforced.
  • Markdown ordered lists preserve explicit authored numbering (for example 1., 2., 7. stays 1, 2, 7), and legal alpha markers (a. / a)) render as ordered sub-clauses with indentation.

Test Coverage

Reference UI coverage:

  • tests/e2e/pages-reference.spec.ts
    • home and dashboard pages render core navigation
    • sign-in page sanitizes external returnTo
    • admin page shows auth-gated state when signed out
    • admin list and org detail pages render for signed-in admin
    • contributor page shows auth-gated state when signed out
    • contributor page shows signed agreements when signed in
    • sign page handles auth-gated and not-found states
    • sign page supports re-sign flow after CLA update

Reference API/flow coverage:

  • tests/integration/api-suite.test.ts
    • Broad integration flow coverage for auth/session APIs, org management, signing/re-signing, webhook checks/comments, install/uninstall/suspend lifecycle, and /recheck authorization.
    • Includes edge cases like stale-signature detection after CLA updates, /recheck handling/authorization on open PRs, malformed webhook payload rejection, and duplicate webhook delivery de-duplication.

Keeping Spec and Tests in Sync

When behavior changes:

  1. Update the relevant sections in this file.
  2. Update or add coverage in tests/integration/api-suite.test.ts and/or tests/e2e/pages-reference.spec.ts as appropriate.
  3. Run pnpm test and pnpm build before merging. Run pnpm test:all when UI/browser behavior changes.