Conversation
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.
…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.
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
|
No updates since Nothing is lost — 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.
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
# 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.
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
|
No updates since Nothing is lost — Converting to draft disables auto-merge, so PRs with auto-merge enabled are excluded from this entirely. |
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.
toleranceis 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. Reusespuzzle-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).PuzzleCaptchaManagermoves 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
coerceToEnabledCaptchaTypeonly reaches for icon-order when icon-order was requested — the puzzle and image fallbacks are untouched.Test coverage
icon-order-assets(glyphs, placement, grading, render)provider(challenge mint, verify, renderer settings)procaptcha-icon-order(canvas, manager, widget, types)types(settings schema, tolerance bounds, glyph budget)cypress.iconOrder.config.js+iconOrder.cy.ts, wired into the cypress workflowLocal gates:
turbo run typecheck52/52,build52/52,build:cjs51/51, lint clean.turbo run testpasses except@prosopo/datasets-fs, which fails on this machine only (Node 25 removedoptions.recursive; CI pins Node 24) and is untouched by this branch.Known gaps
frictionlessTypes.iconOrderyet; it defaults on, so operators can't currently disable it from the UI.ClientSettingsSchema.puzzlerender overrides are dropped on write because the mongoose client schema has nopuzzlefield (pre-existing).iconOrderis declared explicitly so it doesn't inherit that bug.