feat(captcha): settings screen for the captcha, with guardrails (#1053 PR4) - #1066
Merged
Merged
Conversation
Adds Settings → Captcha: the mode chooser, the widget options, and the two bounded numbers. Nothing changes for an existing install — the default stays the math challenge, which is the only strategy that runs without JavaScript and without a secure context, so it is the choice that cannot lock anyone out of a form they could use before. Each mode's consequence is stated beside the option rather than in a paragraph above the group, because they are consequences and not preferences: the math challenge is the weakest, the composite mode is reach rather than strength (the server accepts either proof, so an attacker takes the cheaper one), and ALTCHA-only cannot be submitted without JavaScript. The ALTCHA-only mode is refused at save time without HTTPS, keeping whatever was configured before. This is not caution: the widget throws "Secure context (HTTPS) required" rather than degrading, so accepting it would hand every visitor a form they cannot submit. The composite mode is allowed there — its <noscript> half is exactly what it is for. `CaptchaSettings` is the one place that knows what a captcha setting may be, and it bounds the work factor on READ as well as on save: a value written before a bound moved, or by anything other than this form, still has to land somewhere the widget can cope with. The bounds are real on both sides — below the floor the proof costs nothing, above the ceiling a slow phone grinds into the widget's own 90-second timeout, which is a failure with no visible cause. `humanInteractionSignature` and `setCookie` are forced off and deliberately not settings. These are public-sector forms under the LGPD; the proof of work already carries the anti-automation load, and an administrator toggling pointer and keyboard collection back on would change what the site has to disclose without being told so. Three deviations from the plan, each deliberate. `algorithm` is not exposed: only SHA-256 ships in the bundle, and a one-item selector is noise. The HTTPS refusal uses `add_settings_error()` rather than `WP_Error`, which is the idiom the rest of this handler uses for save feedback. And the typed reader is `CaptchaSettings` rather than accessors on `SettingsReader`: these keys carry bounds, and bounds have to live somewhere regardless — spreading `SettingsReader::get_int()` would leave the clamp outside, for every reader to repeat. `ActivityLogActionLabelsTest` caught the fallback-usage action arriving with no translated label, which would have rendered as untranslated English in every locale across the table, the summary and the CSV export. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU
rpgmem
marked this pull request as ready for review
September 5, 2026 18:48
rpgmem
enabled auto-merge (squash)
September 5, 2026 18:48
Coverage Report for CI Build 33985124700Warning No base build found for commit Coverage: 89.856%Details
Uncovered Changes
Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
84 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR4 do épico #1053, e a última — a PR5 foi cortada (ver Sobre a PR5 abaixo). Entrega a tela que torna o ALTCHA da PR3 selecionável.
Nada muda para instalações existentes: o padrão segue o desafio matemático, a única estratégia que roda sem JavaScript e sem contexto seguro — a escolha que não tranca ninguém fora de um formulário que usava antes.
Summary
Secure context (HTTPS) requiredem vez de degradar, então aceitar entregaria a todo visitante um formulário que ele não consegue enviar. O modo composto é permitido ali — sua metade em<noscript>é exatamente para isso.Core\Captcha\CaptchaSettings— o único lugar que sabe o que uma configuração de captcha pode ser. Valores aceitos por atributo, e os dois números com limite.Duas decisões que valem a revisão
O fator de trabalho é limitado na leitura, não só no salvamento. Um valor gravado antes de um limite mudar — ou por qualquer coisa que não seja este formulário — ainda precisa cair onde o widget consegue operar. Os limites são reais dos dois lados: abaixo do piso a prova não custa nada, acima do teto um celular lento mói até o widget desistir aos 90 segundos, que é uma falha sem causa visível para o visitante.
humanInteractionSignatureesetCookieficam desligados e deliberadamente não são configuráveis. São formulários de serviço público sob a LGPD; o proof-of-work já carrega a carga anti-automação, e um administrador religando a coleta de temporização de ponteiro e teclado mudaria o que o site tem de declarar sem ser avisado disso.Três desvios do plano, todos deliberados
algorithmnão foi exposto. Só SHA-256 vem no bundle; SHA-384/512 exigiriam vendorizar workers à parte, que o épico já pôs fora de escopo. Um seletor de um item só é ruído.add_settings_error(), nãoWP_Error. É o idioma que todo o resto doSettingsSaveHandlerusa para retorno de salvamento; umWP_Errorprecisaria de plumbing próprio para chegar à tela e não chegaria mais claro.CaptchaSettings, não acessores emSettingsReader. Estas chaves carregam limites, e limites precisam morar em algum lugar de qualquer forma — espalharSettingsReader::get_int()deixaria o clamp do lado de fora, para cada leitor repetir. É a forma que o Declarative settings registry: declare eachffc_settingskey once #993 defende, construída onde já se pagava por ela.Sobre a PR5
A PR5 previa forçar o modo só-ALTCHA como padrão numa release posterior. Cortada. A premissa era que ele é estritamente melhor, e não é: exige HTTPS e JavaScript. Trocar o padrão mudaria o comportamento justamente das instalações que nunca abriram esta aba — as menos propensas a perceber que um formulário parou de aceitar envios.
O que esta PR entrega torna o flip desnecessário: a escolha fica visível com a consequência ao lado, o save recusa o modo onde ele não funciona, e o aviso empurra para ele só onde é possível. Escolha informada em vez de mudança silenciosa — mesmo raciocínio que estacionou o flip de IP do cliente no #902. Registrado no épico com o gatilho para revisitar.
Type of change
Test plan
composer test— 7584 testes / 21778 asserções, verdecomposer lint(PHPStan) — nível 8 limpo nos arquivos novos e tocadosphpcs-tests.xml.dist, ESLint e Stylelint limposCobertura nova.
CaptchaSettingsTestcobre os limites nas duas pontas, entradas não numéricas, valores de atributo desconhecidos caindo para o seguro, e que as escolhas de privacidade não são alcançáveis por configuração. EmSettingsSaveHandlerTest, oito casos: a seção intocada quando outra aba é salva, modo desconhecido caindo para matemático, a recusa sem HTTPS preservando o valor anterior e registrando o erro, o modo composto permitido sem HTTPS, clamp no salvamento, e caixas desmarcadas lendo como zero.Um guarda pegou algo
ActivityLogActionLabelsTestfalhou porque a ação nova do log de fallback chegou sem rótulo traduzido — sem ele renderizaria como inglês não traduzido em todos os locales, na tabela, no resumo por ação e no export CSV. Corrigido.Checklist
npm run build) and the resulting*.min.*files are committedCHANGELOG.mdupdated —[Unreleased] → Added; sem bump deFFC_VERSIONuninstall.phpdeclara a option nova do aviso dispensável, mantendo-o um manifesto honesto para o gate de fresh-install.CLAUDE.mdganhou uma seção sobre a arquitetura de captcha — o contrato, a regra "conferir ≠ gastar", os cinco sites que emitem desafio, e as especificidades do ALTCHA que custam tempo para redescobrir (os nove atributos, omaxnumberinexistente no 3.x, o contexto seguro obrigatório, o build UMD). A justificativa doPublicCsvExporterfoi emendada: "funciona com JS desabilitado" passa a ser propriedade dos modos matemático e composto apenas.Nota sobre o PHPStan local: rodo o PHAR, que não carrega
szepeviktor/phpstan-wordpress, então ele reporta 3 erros em arquivos que esta PR não toca. Artefato do meu ambiente; a CI é a fonte de verdade.🤖 Generated with Claude Code
https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU
Generated by Claude Code