Skip to content

NOT READY FOR REVIEW - JUST CI TEST: icon-order captcha type - #3165

Open
HughParry wants to merge 19 commits into
mainfrom
feat/icon-order-captcha
Open

HughParry wants to merge 19 commits into
mainfrom
feat/icon-order-captcha

Conversation

@HughParry

Copy link
Copy Markdown
Contributor

NOT READY FOR REVIEW - JUST CI TEST

Adds CaptchaType.iconOrder — a frame of procedurally generated icons with a legend naming which of them to click, and in what order (the GeeTest-style "select in this order" challenge).

Design

The answer never leaves the provider. Icon positions and the required order are written to the challenge record before the response is sent; the widget receives a composited frame plus a legend strip and nothing else. There is no coordinate for a client to echo back. tolerance is server-side only for the same reason.

Grading is strict on order — clicking the right three icons in the wrong sequence fails — and the hit radius scales with each icon's own size, so the renderer's size jitter doesn't make small targets unfairly hard.

New packages

  • @prosopo/icon-order-assets — glyph vocabulary, non-overlapping placement, SVG compositing via sharp/librsvg, and the ordered legend. Reuses puzzle-assets' PRNG, background generator and encoders, and shares its pre-generated background buffer at runtime so mesh-gradient synthesis stays off the request path.
  • @prosopo/procaptcha-icon-order — the widget: click capture, numbered markers, reset/confirm.

Shared refactor

Extracts InteractiveCaptchaManager, the server-verify pipeline both interactive types now share (replay/recency, client-session correlation, access policies, spam rules, traffic filter, IP validation, decision machine). PuzzleCaptchaManager moves onto it — 996 lines down to 484 — with its existing 31-test suite unchanged and green.

Behaviour of existing types

Unchanged. Ranking puts icon-order above puzzle and below image wherever captcha types are ordered by harshness, and coerceToEnabledCaptchaType only reaches for icon-order when icon-order was requested — the puzzle and image fallbacks are untouched.

Test coverage

Area Tests
icon-order-assets (glyphs, placement, grading, render) 18
provider (challenge mint, verify, renderer settings) +22
procaptcha-icon-order (canvas, manager, widget, types) 131
types (settings schema, tolerance bounds, glyph budget) +7
e2e cypress.iconOrder.config.js + iconOrder.cy.ts, wired into the cypress workflow

Local gates: turbo run typecheck 52/52, build 52/52, build:cjs 51/51, lint clean. turbo run test passes except @prosopo/datasets-fs, which fails on this machine only (Node 25 removed options.recursive; CI pins Node 24) and is untouched by this branch.

Known gaps

  • The portal has no toggle for frictionlessTypes.iconOrder yet; it defaults on, so operators can't currently disable it from the UI.
  • ClientSettingsSchema.puzzle render overrides are dropped on write because the mongoose client schema has no puzzle field (pre-existing). iconOrder is declared explicitly so it doesn't inherit that bug.

Adds `CaptchaType.iconOrder`: a frame of procedurally generated icons with a
legend naming which of them to click, and in what order.

The answer never leaves the provider. Icon positions and the required order
are written to the challenge record before the response is sent; the widget
receives a composited frame and a legend strip and nothing else, so there is
no coordinate for a client to echo back. Grading is strict on order and uses
a hit radius that scales with each icon's own size.

New packages:
  - @prosopo/icon-order-assets — glyph vocabulary, placement, compositing and
    the ordered legend. Reuses puzzle-assets' PRNG, background generator and
    encoders, and shares its pre-generated background buffer at runtime.
  - @prosopo/procaptcha-icon-order — the widget: click capture, numbered
    markers, reset/confirm.

Also extracts InteractiveCaptchaManager, the server-verify pipeline both
interactive types now share (replay and recency checks, client-session
correlation, access policies, spam rules, traffic filter, IP validation and
the decision machine). PuzzleCaptchaManager moves onto it — 996 lines down to
484 — with its existing suite unchanged and green.

Ranking places icon-order above puzzle and below image wherever captcha types
are ordered by harshness. Coercion only reaches for icon-order when
icon-order was asked for: the puzzle and image fallbacks are untouched, so no
existing site's behaviour changes.
…odule commit

The lockfile was regenerated while the nested fingerprintjs submodule was
checked out at a commit other than the one this repo records, which pinned
@prosopo/config@3.3.1 against a submodule that asks for 3.3.3 and made
`npm ci` refuse the tree.
Comment thread demos/client-bundle-example/src/icon-order-explicit.html Fixed
…ages

lint:refs requires each tsconfig's `references` to mirror its package.json
workspace dependencies.
`name` and `email` come straight off the form, so interpolating them into
markup lets anything typed into those fields run as HTML. CodeQL flags it as
js/xss-through-dom.
The icon-order demo pages existed but pointed at the puzzle sitekey, and
nothing seeded a sitekey configured for the new type.

  - `getDefaultSiteKeys` seeds one, so `npm run setup` registers
    `DEV_PHRASE//iconOrder` with `captchaType: iconOrder` and writes
    `PROSOPO_SITE_KEY_ICONORDER` into the env files. It is ordered before
    `puzzle` deliberately: `updateDemoHTMLFiles` rewrites the sitekey in every
    demo HTML file once per seeded type, so appending would have silently
    repointed the android/ios webview demos from puzzle to icon-order.
  - Both demo pages, the vite `define` block and the env templates use the new
    key, and the injected code sample now names the variable that actually
    exists.
  - The nav splits camelCase type names, so the entry reads "Icon Order".

Also fixes the /frictionless short-circuit, which threw
"Unhandled configured captchaType" for an icon-order sitekey: every widget
enters through /frictionless, so a configured icon-order site could not
obtain a session at all. Adds the missing `sendIconOrderCaptcha` alongside
the image/pow/puzzle helpers.
…env files

`updateEnvFiles` only replaced variables that were already present, so the
site key for a newly added captcha type never reached a developer's existing
`.env`: `copyEnvFile` seeds from the template only when the file is absent, so
any machine set up before the type existed kept a file without the variable.
The demo page then rendered with an undefined site key and the widget fell
through to the wrong captcha type.

A variable named like a site key is now written whenever the file already
tracks site keys at all. Files that track none are still left untouched, so
this doesn't scatter keys into unrelated env files.
…radient

A smooth mesh gradient was the wrong frame for this captcha. It has almost no
edges of its own, so every icon stroke was the strongest local signal in the
picture and a single edge-detection pass found all of them.

The frame is now a collage: colour panels blocking it into regions,
concentric ripple families, and heavy opaque bars slicing across. Panel
lightness is drawn from dark / near-white / mid bands and hues jump around the
wheel rather than staying analogous, so neighbouring regions actually contrast
— which is the boundary an icon stroke can hide against. Post-rasterisation
grain raises the noise floor those edges have to clear. `haloOpacity` goes up
to keep icons findable against it.

`backgroundClutter` scales every element family at once, so one operator knob
moves the frame from clean to busy; 0 renders a plain single-colour frame as
the escape hatch.

The collage is vector work rasterised natively, cheap enough to draw per
request, so icon-order no longer borrows the puzzle type's pre-generated
background buffer — that buffer exists only because the puzzle's mesh gradient
is per-pixel JS. Dropping it also means every frame is unique without a buffer
having to guarantee consume-once.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

No updates since 2026-09-01T15:04:25Z (over 1 days), so this PR has been converted to draft. That stops it holding CI runners.

Nothing is lost — gh pr ready 3165, or the "Ready for review" button, picks it straight back up. Add the keep-ready label to exempt it permanently.

Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

No updates since 2026-09-02T18:01:07Z (over 1 days), so this PR has been converted to draft. That stops it holding CI runners.

Nothing is lost — gh pr ready 3165, or the "Ready for review" button, picks it straight back up. Add the keep-ready label to exempt it permanently.

Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

No updates since 2026-09-04T00:02:23Z (over 1 days), so this PR has been converted to draft. That stops it holding CI runners.

Nothing is lost — gh pr ready 3165, or the "Ready for review" button, picks it straight back up. Add the keep-ready label to exempt it permanently.

Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely.

Conflict resolutions of note:

- CaptchaType / ClientApiPaths / server.ts docs: both sides added members;
  kept both (iconOrder and authenticated).
- captcha-severity: main extracted the per-type severity table that this
  branch had extended in blacklistRequestInspector and checkTrafficFilter.
  Dropped both local tables in favour of the shared package and added the
  iconOrder tier there (image > iconOrder > puzzle > pow > frictionless),
  so icon-order policies keep their ranking instead of falling to 0.
- provider tsconfig{,.cjs}.json: union of both reference lists. Also fixed
  the icon-order-assets entry in the cjs config, which pointed at the ESM
  tsconfig.
- getIconOrderCaptchaChallenge: main added a required requestHeaders
  argument to getPrioritisedAccessPolicies; the icon-order handler now
  passes normalizeHeadersForMatching(req.headers) like its siblings.
- package.json: took main's dependency versions and re-added the two new
  workspace deps. The two new packages pinned pre-merge sibling versions
  (@prosopo/puzzle-assets 0.1.3, @prosopo/api 4.1.3, vitest 4.1.10, ...),
  which resolved them off the registry instead of the workspace; realigned
  to the post-merge versions.
- package-lock.json: reconciled in place from main's lockfile rather than
  regenerated, so every platform's optional deps survive.
…kage

- `verifyIconOrderCaptchaSolution` claimed the challenge's single submission
  by reading `userSubmitted` off the record it had just fetched. Concurrent
  submitters all read the same unclaimed record, so each was graded and
  handed a verdict. `claimIconOrderCaptchaSubmission` does it under a
  `userSubmitted: { $ne: true }` filter instead, so exactly one wins.
- Lower the `iconOrderTolerance` ceiling from 20 to 12, the smallest value
  that still lets the Cypress specs make any click on the frame count. The
  arithmetic is written onto the field schema so it is not raised casually.
- Drop `private: true` from `@prosopo/icon-order-assets`: the published
  `@prosopo/provider` depends on it at runtime.
- Document the icon-order flow and the shared `InteractiveCaptchaManager`
  base in architecture.mmd, which still described only puzzle.
Release v3.8.8 bumped every workspace package while this branch was open.
Took main's package.json versions and re-added the two deps this branch
introduces (`@prosopo/procaptcha-icon-order` on procaptcha-frictionless,
`@prosopo/icon-order-assets` on provider), then realigned the two new
packages' own `@prosopo/*` pins to the released versions so npm links the
workspace copies rather than resolving them from the registry.

package-lock.json reconciled in place from main's, not regenerated.
The frame bound both onClick and onTouchEnd. A tap on a touch device fires
touchend followed by a synthesised compatibility click at the same
coordinates, so each tap appended two points to the answer. Three correct
taps submitted six clicks against three targets and gradeClicks rejected
them on length before comparing a single position — icon-order could not be
solved on any phone.

Staging records confirm it: every mobile submission holds six clicks in
near-identical pairs, each pair within its target's hit radius.

Switch to onPointerUp/onPointerMove, which cover mouse, touch and pen and
fire once per interaction. touch-action: none was already set on the frame
and does not suppress the synthesised click.

The regression test dispatches the full tap sequence — pointerup, touchend,
then the synthesised click — and asserts one marker.
@github-actions

Copy link
Copy Markdown
Contributor

No updates since 2026-09-09T08:28:04Z (over 1 days), so this PR has been converted to draft. That stops it holding CI runners.

Nothing is lost — gh pr ready 3165, or the "Ready for review" button, picks it straight back up. Add the keep-ready label to exempt it permanently.

Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely.

HughParry and others added 3 commits September 15, 2026 10:12
# Conflicts:
#	package-lock.json
#	packages/procaptcha-frictionless/package.json
#	packages/provider/package.json
#	packages/provider/src/tasks/frictionless/frictionlessTasks.ts
…h the dependabot rollup

Main's rollup (#3252) moved every other package to @polkadot/util 14.0.3 and
react/react-dom 19.3.0. The icon-order package still pinned the old versions,
which left package-lock.json out of sync and failed npm ci.
@github-actions

Copy link
Copy Markdown
Contributor

No updates since 2026-09-16T08:46:46Z (over 1 days), so this PR has been converted to draft. That stops it holding CI runners.

Nothing is lost — gh pr ready 3165, or the "Ready for review" button, picks it straight back up. Add the keep-ready label to exempt it permanently.

Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely.

@github-actions

Copy link
Copy Markdown
Contributor

No updates since 2026-09-18T12:01:00Z (over 1 days), so this PR has been converted to draft. That stops it holding CI runners.

Nothing is lost — gh pr ready 3165, or the "Ready for review" button, picks it straight back up. Add the keep-ready label to exempt it permanently.

Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely.

@github-actions

Copy link
Copy Markdown
Contributor

No updates since 2026-09-21T00:01:53Z (over 1 days), so this PR has been converted to draft. That stops it holding CI runners.

Nothing is lost — gh pr ready 3165, or the "Ready for review" button, picks it straight back up. Add the keep-ready label to exempt it permanently.

Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely.

@github-actions

Copy link
Copy Markdown
Contributor

No updates since 2026-09-22T06:01:39Z (over 1 days), so this PR has been converted to draft. That stops it holding CI runners.

Nothing is lost — gh pr ready 3165, or the "Ready for review" button, picks it straight back up. Add the keep-ready label to exempt it permanently.

Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely.

@github-actions

Copy link
Copy Markdown
Contributor

No updates since 2026-09-23T18:00:59Z (over 1 days), so this PR has been converted to draft. That stops it holding CI runners.

Nothing is lost — gh pr ready 3165, or the "Ready for review" button, picks it straight back up. Add the keep-ready label to exempt it permanently.

Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely.

This branch has not been deployed

No deployments
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