Skip to content

Releases: pietrobb/BareBonesForms

v2.2.0

Choose a tag to compare

@github-actions github-actions released this 08 Oct 11:53

Added

  • Embedded mode for host applications (see README "Embedded Mode"): bbf_form.php validates and normalizes input against a form definition with no configuration, session, output or file writes (bbf_load_form(), bbf_validate() with language fallback to English, message overrides, options_resolver, $_FILES checks returning keep/remove/replace). bbf_render.php draws the same DOM as bbf.js on the server (bbf_render_html() with values, errors, hidden inputs, the stored file with a remove checkbox, id_prefix, form_attrs, before_submit, templates.control/templates.field, bbf_attrs()); a field type it cannot draw throws instead of disappearing.
  • bbf.js: BBF.render() options definition, values, errors, submitUrl (JSON or multipart with files to the host; no BBF CSRF or drafts), hiddenFields, optionsResolver; BBF.enhance() adds client validation and conditions to a server-rendered form; BBF.registerType() and bbf_register_type() add custom x-… field types (raw values, normalize after a passed validate, a second registration throws).
  • 'standalone' => false in config.php makes every public BBF PHP page answer 404 before any session or file write.
  • Field property step for number fields.

Changed

  • Breaking (only for forms with options_from): the server now checks a submitted value against the options the source returns (config options_resolver, or the source URL fetched with diagnostic_base_url). A value outside the list is refused, and when the source cannot be loaded no value is accepted (before, any value was stored). Forms without options_from are unaffected.
  • Lithuanian success message is now “Ačiū! Jūsų užklausa gauta.”.

Compatibility for existing sites

  • The standalone mode stays the default: submit.php?form=…&action=definition, action=csrf (64 hex characters) and the JSON submit response keep their shape. CI replays a real standalone form contract end to end (definition, CSRF, submit → HTTP 200, success not false, the site's own action file receives the data).
  • A site's own files that are not part of the package (for example its own files in actions/) are never touched by maintenance.php upgrade. If you deploy BBF fresh from git instead, copy such files over again.
  • No change is needed for forms without options_from.
  • Before upgrading a site whose forms use a relative options_from (for example /api/countries.php), set diagnostic_base_url (or options_resolver) in config.php. With the default config the server cannot load a relative source and refuses every value of that field. Run check.php after the upgrade: it reports every options_from field the server cannot check as an error.

v2.1.14

Choose a tag to compare

@github-actions github-actions released this 01 Oct 17:48

Fixed

  • Email templates with an unclosed, crossed or misspelled section tag no longer truncate the message silently: unmatched tags stay as visible text, and check.php and smoketest warn about them.
  • selfcheck reports a root-owned logs/ directory or access-audit.php without group/other write permission as an error instead of passing, because the web worker cannot append and management returns 503.
  • Returning with “Back” to a form using hideOnSuccess shows the recovery actions immediately after a bfcache restore.
  • Starting a new fill cancels only that form's pending redirect instead of calling window.stop(), so other forms' requests on the page continue.
  • Diagnostics keep an inherited ignored SIGINT ignored; stale fixture cleanup works for installation paths containing [; leftover root-owned fixtures appear as a selfcheck warning; the SPA fallback message no longer blames a control fixture that was served correctly.
  • The printed undo command quotes the backup path. The Windows upgrade launcher stays within the 8191-character cmd.exe limit.
  • The sandbox subject preview resolves {{field_label}} variables such as {{plan_label}}.

v2.1.13

Choose a tag to compare

@github-actions github-actions released this 01 Oct 12:29

Fixed

  • Redirect recovery actions appear only after four seconds on the same page. Starting a new fill stops pending navigation before unlocking, preserves hidden-field layout and restores focus.
  • Unidentified renderer modules fall back to the document directory with an explicit warning instead of borrowing another script's origin. data-bbf-base accepts a missing trailing slash.
  • Audit diagnostics check regular-file shape, write permission bits and the PHP guard before CLI/web ownership comparison, flag root-owned logs and accept symlinked deployment ancestors without accepting linked audit targets.
  • CLI diagnostics clean fixtures on SIGTERM/SIGINT where pcntl is available, scan matching fixture names independently of form-directory count, report unreclaimable stale fixtures, and explain redirect/SPA fallback probe failures.
  • Nested email sections render in one token pass without evaluating respondent text. Confirmation and notification subjects support authoritative system variables. Interior phone BOM is rejected consistently by client and server.
  • Upgrade children preserve literal ${...} INI values. Suggested apply/rollback commands quote POSIX arguments and use a dependency-free encoded PowerShell launcher on Windows to preserve percent/dollar paths and native argument quoting.
  • Already-published tags are checked before build attestation and release mutation.

v2.1.12

Choose a tag to compare

@github-actions github-actions released this 01 Oct 09:11

Includes all review fixes below. Tag v2.1.11 was not published: its draft was removed after ownership discovery failed. Draft creation now pins the immutable ID directly from the API response, avoiding a stale list-API result; assets are uploaded and verified by ID.

Fixed

  • Redirects that download a file, return HTTP 204 or are cancelled no longer leave the UI on “Sending…”. The success message offers a continuation link and an explicit new-submission action; pending navigation still blocks accidental duplicate POSTs. Empty same-page fragments remain usable. Recovery labels are translated in all 34 client packs.
  • Renamed module scripts and dynamic imports no longer crash when document.currentScript or a matching script is absent. data-bbf-base selects the renderer/CSS directory explicitly. Initial CSRF timeouts retain the hidden token field so the next submit can refresh and retry.
  • HTTP selfcheck tests fresh harmless ordinary fixtures in submissions/ and logs/ as well as dot-file protection. Leaks fail the check; unreachable, fallback or incomplete results remain unverified. Fixtures have finally/shutdown cleanup and bounded exact-format stale cleanup on subsequent runs after forced termination; unfamiliar, incomplete and differently owned files are preserved.
  • Audit diagnostics distinguish the current CLI identity from the web worker; a different cron UID is unverified, not a false daily unwritable-file incident. Missing-config responses in submissions and sandbox are generic HTTP 503. Authenticated proxy diagnostics warn about forwarding hints without trusting them.
  • Phone Unicode boundary whitespace is normalized before storage, exports and delivery, including repeatable rows. Email body and subject placeholders support numeric and hyphenated names in one substitution pass; respondent text is never reinterpreted as template syntax.
  • Verified upgrades support scoped-only and deliberately credential-free installations while rejecting malformed credentials and uncheckable access. Checked access-blocked dry runs include an exact quoted apply command. Documentation directs users of old launchers with disabled proc_open to the verified new external launcher.
  • Release runs serialize per tag, skip already-published tags, locate their own marked draft across paginated API results, and verify/download/publish/clean by immutable release and asset IDs, not tag lookups. Other drafts and published releases remain untouched.

v2.1.10

Choose a tag to compare

@github-actions github-actions released this 01 Oct 06:36

Fixed

  • Cross-page redirects keep the submit button and handler locked until pageshow; same-page anchors remain usable. No timer unlock permits a duplicate POST during slow navigation.
  • Shared CSRF refreshes abort and release their queue after 15 seconds, including stalled response bodies. Module scripts find their own bbf.js URL instead of an unrelated last script.
  • CLI selfcheck includes every access diagnostic (including trailing whitespace), fails for empty/all-invalid or inactive credentials, validates the existing audit file without changing it, and probes active dot-file HTTP protection with disposable fixtures. Missing/unreachable HTTP targets are explicitly unverified.
  • Missing-config viewer/editor responses are generic HTTP 503; unavailable audit responses point operators to selfcheck without leaking configuration details.
  • Automated upgrade fails closed when access preflight cannot run, preserves active error reporting, temporary-directory restrictions and UNC INI values, and distinguishes CLI failure categories (documented in README).
  • Copied Unicode whitespace at phone boundaries matches browser validation. Missing/hidden numeric email placeholders such as {{5}} are removed.
  • Failed or cancelled release runs clean only their own marked draft by immutable release ID, never a manually created draft sharing the tag.

v2.1.9

Choose a tag to compare

@github-actions github-actions released this 30 Sep 19:53

Fixes from the review of 2.1.8. Production installations are not upgraded automatically.

Security

  • Anonymous sign-in responses no longer reveal access-token names, configuration errors or administrative access state. Detailed diagnostics remain available through php maintenance.php selfcheck.
  • Token validation remains a hexadecimal format check, not an entropy estimate. Continue generating credentials with php maintenance.php new-token.

Fixed

  • Respondent session activity is renewed only after successful CSRF validation, without replacing cookies on rejected or concurrent requests.
  • CSRF creation and recovery are serialized across forms in the same installation while retaining separate form-specific tokens. Disabled drafts do not trigger a CSRF refresh.
  • Upgrade preflight reports administrative lockout before replacing files. Expected structured refusals are distinguished from child-process crashes. PHP security restrictions, resource limits and diagnostics are forwarded to child processes. Windows replacement never truncates a held maintenance.php; use the unpacked release's tools/upgrade.php if the entry point is held open.
  • Security-rule comparisons recognize safe Apache directive casing and module wrappers without discarding conditional protection.
  • Redirect success state, answer reset and hideOnSuccess are applied when submission is confirmed, before navigation; no five-second timer guesses whether a redirect has finished.
  • Renderer and stylesheet URLs use document.currentScript rather than an unrelated script following the renderer.
  • Phone validation accepts Unicode spaces such as NBSP consistently with the browser; sandbox email previews preserve empty and escaped numeric field names.
  • Failed release verification removes only the draft created by that workflow run, never an already published release. Smoke diagnostics describe the current hexadecimal token policy. Operator diagnostics identify trailing token whitespace; authenticated proxy warnings never treat forwarding headers as trusted evidence.

v2.1.8

Choose a tag to compare

@github-actions github-actions released this 30 Sep 18:11

Fixes from the review of 2.1.7.

Security

  • Breaking: access tokens now require at least 32 hexadecimal characters. This applies to api_token, scoped access_tokens and smoke_token. Passwords, UUIDs, base64 strings and shorter tokens are ignored. Generate each credential with php maintenance.php new-token: it prints 64 hex characters from 32 cryptographically random bytes. The password dictionary and entropy estimator are removed. Format validation cannot establish random origin: never invent a token by hand, use repeated characters or copy a sample value. Correct tokens still authenticate even when wrong guesses from the same address are blocked.
  • Upgrade lockout is not success: if the new policy leaves no usable administrative token, the apply result explicitly reports that code was updated but administrative access is blocked, returns ok: false and exits nonzero. Replace the token in config.php, then run php check.php or php maintenance.php selfcheck. The sign-in page explains the token requirements; diagnostics no longer mark a rejected api_token as configured correctly.
  • Release integrity: releases remain drafts until downloaded assets match the build's SHA256SUMS; only then are they published.

Fixed

  • Concurrent CSRF recovery: uploads and drafts share a pending token refresh. A rejected upload does not create a session or replace its cookie, and does not consume the upload quota. Removing a file during recovery prevents its retry.
  • Upload errors: disabled uploads and unknown forms do not flood the error log or masquerade as broken storage. Genuine storage problems remain diagnosable.
  • Numeric field names in email templates: confirmation and notification bodies preserve keys such as "1" and "5", while still escaping respondent values.
  • Upgrade/security rules: stock legacy .htaccess.dist files are recognized across published releases; rule checks preserve block context instead of treating all lines as one unordered set. Child-process exit codes survive long PHP-notice output.
  • Documentation: upgrade ZIPs include docs.html so old installations receive the current token and upgrade instructions.
  • Proxy/configuration: invalid cookie_path is reported; client-supplied forwarding headers do not create trusted-proxy warnings, and Stripe return URLs use trusted TLS-proxy detection.
  • Respondent messages: payment gateway failures use the selected language; disabled drafts are distinguished from missing drafts.

Upgrading

  • Breaking: show_if.value cannot be an object. This validation was introduced in 2.1.7 but omitted from its Breaking notes. Use a scalar or a list of scalars. Upgrade preflight validates existing forms with the new code and names incompatible definitions before replacing files.
  • Replace incompatible credentials before upgrading. Use --checksum so the package's upgrader and diagnostics evaluate the new policy. FTP uploads do not run preflight: generate tokens, validate forms, and run diagnostics yourself. No production installation is upgraded automatically.

v2.1.7

Choose a tag to compare

@github-actions github-actions released this 30 Sep 09:25

Fixes from the review of 2.1.6.

Security

  • Breaking: a token that is easy to guess is refused. Sample values (your-secret-token, change-me), words, names, years, keyboard runs (qwerty) and repeats are ignored like a too-short token. Set a random one: php -r "echo bin2hex(random_bytes(24));". After the upgrade run php check.php or php maintenance.php selfcheck: they name every ignored token. Details: the right token always signs in, even from an address blocked for wrong tokens, so the token itself must withstand fast guessing (about 1500 tries per second were measured). Each token now gets a pessimistic estimate of the guessing work, where words (also Capitalised or in leetspeak), sample-value parts, runs, years and repeats cost only a few bits; it needs at least 44 bits (2^44 guesses at 1500/s take about 370 years) and must not consist of such parts only. password12345678, adminadminadmin1, qwertyuiopasdfgh, Summer2026!Summer and my-super-secret-api-token are refused; of 1 000 000 random tokens of each kind (16 and 32 hex digits, base64, UUID, password-manager output) none was. The documentation no longer shows a sample token.
  • An expired or revoked token sent from a blocked address is answered 429 like any other wrong token, instead of 403, which told that it had once been valid.
  • A CSV/export link opened from another site (webmail, chat) explains why its ?token= is ignored instead of a bare "Access denied.".
  • Release workflow: after publishing, the release files are downloaded again from GitHub and checked against the SHA256SUMS made when they were built, not only between the build and publish jobs.

Fixed

  • Drafts and file uploads after an expired session: saving, loading or deleting a draft, uploading and removing a file refresh the CSRF token once after 403 and repeat the request, as sending the form does. Before, every draft save failed in a tab opened before the upgrade to 2.1.6 (cookie BBFSID) or more than 24 minutes ago, and the advice to reload lost the answers the draft was meant to keep.
  • A field with a numeric name ("7", "2024") is filled into redirect again ({{7}}); 2.1.6 left it empty.
  • Security-rules notice after an upgrade: an .htaccess.dist left by any earlier release is replaced by this release's, and .htaccess is compared by its rules only, so a release that changes only comments raises no daily "Security rules missing".
  • docs.html no longer shows a version in its title (a code-only upgrade does not replace it, so it showed an old one).
  • Behind a reverse proxy: new cookie_path for a proxy that serves the folder under another path (/forms/ → /bbf/), where every submission used to fail with 403. X-Forwarded-Proto: https from a proxy listed in trusted_proxies counts as HTTPS, so cookies are Secure and forms in iframes on other sites work behind a proxy that ends TLS.
  • "redirect": "#done" on a page with <base href> (single-page apps) stays on the page instead of navigating to the base address.
  • show_if with an object as value is reported by the definition check (the browser and the server compared it differently). form.schema.json accepts "value": 5, true and "op": null, which the engine has accepted since 2.1.6.
  • Restoring a backup made with a now-refused token names that token and says how to replace it, instead of "Restore access policy does not match".
  • Upgrade: the --apply result repeats the access warnings, judged by the code just installed. A token check or package upgrader that crashes reports its exit code instead of looking like success. A long Breaking note ends at a full sentence instead of being cut in the middle.
  • Upload storage not ready (folder in the web root, not writable): the respondent gets "The server cannot store uploads right now." in their language; the setup advice goes to the PHP error log (and still to the editor's sandbox).
  • E-mail confirmation box is labelled from the language pack (emailConfirm, all 34 languages) instead of an English "Confirm …" guessed from another message.

Upgrading from 2.1.6 or older

Use --checksum: then the 2.1.7 upgrader inside the package plans and applies the upgrade and names every token 2.1.7 will ignore, in the dry run and again in the --apply result. Without it the installed upgrader does all the work and judges tokens by its own, older rules. The process you start is always the installed version, so under 2.1.5 or older it can still hang on very many PHP notices (turn display_errors off for the command: php -d display_errors=0 maintenance.php upgrade …). In any case run php check.php or php maintenance.php selfcheck after the upgrade.

v2.1.6

Choose a tag to compare

@github-actions github-actions released this 30 Sep 07:11

Fixes from the review of 2.1.5.

Security

  • The right token always signs in again (as in 2.1.2). In 2.1.3–2.1.5 a blocked address got one token check per 2 seconds, and whoever came first took it: someone sharing the admin's address (NAT, IPv6 /64, a proxy or Cloudflare without trusted_proxies) could keep the admin and scripts using submissions.php out by sending wrong tokens every fraction of a second. The claim in 2.1.5 that this was no longer possible was wrong. Now only wrong tokens are limited (10 per 15 minutes per address, then 429 at once with Retry-After); a correct token is always checked and accepted. The protection against guessing is the token itself, see the next item. A blocked wrong token says "Invalid token." instead of "Not checked.".
  • Breaking: a token with an obvious pattern is refused. api_token, access_tokens and smoke_token still need at least 16 characters, and now also must not be a pattern: fewer than 5 different characters (aaaa…, abab…) or mostly consecutive/repeated characters (1234…, abcd…, 9876…). Such a token is ignored like a too-short one; check.php, selfcheck and the upgrade dry run name it. A random token (e.g. php -r "echo bin2hex(random_bytes(16));") is never affected.
  • Respondent cookie BBFSID. The CSRF session cookie is now named BBFSID and limited to the installation folder. Before, it was the shared PHPSESSID on /, and strict session IDs replaced the session of another PHP application on the same domain, signing its users out whenever a page with a form was loaded. On HTTPS it is SameSite=None; Secure, so a form embedded in an iframe on another site can be submitted. 2.1.4/2.1.5 claimed this worked with the browser default, but it did not (browsers that block third-party cookies still block it; embed with BBF.render on the page instead).
  • Nginx rules: forms/, README.md, CHANGELOG.md and .bbf-manifest.json are denied by ^~ / = locations, which win over any regex location. A static-file cache rule such as location ~* \.(css|js|json)$ placed earlier used to serve form definitions. Paste the remaining location ~ lines above your other regex locations (see the comments in .htaccess).

Fixed

  • show_if with "op": null or "field": null (written by generators and editors for "not set") is treated as a missing key again. 2.1.5 rejected such a form and every submission answered 500.
  • show_if with a number ("value": 5) now matches the field value "5" in the browser too, as on the server. The field stayed hidden in the browser while the server required it, so the form could not be sent.
  • Upgrade could hang when the smoke test or the package upgrader printed more than about 64 KB of PHP notices (e.g. deprecations from config.php): the parent read one pipe to the end while the child waited on the other. stderr now goes to a temporary file.
  • Upgrade dry run without --checksum lists access warnings again (tokens that would be ignored), judged by the installed version (access_warnings_by).
  • .htaccess.dist left by any earlier release is recognised by comparing it with this release's checksum from the manifest (also after an FTP transfer changed line endings). A release that only changes comments in .htaccess no longer reports "changed security rules".
  • Redirect that does not leave the page ("redirect": "#done", or a target answering 204 No Content) no longer leaves the submit button disabled: the success message is shown.
  • {{_id}}, {{_form}} and {{_time}} in redirect are filled in; {{_id}} used to come out empty.
  • maintenance.php deliveries-retry: an old record that cannot be read (locked, damaged) is counted as not checked and read again next time instead of being cached as delivered.
  • Server messages in the respondent's language: drafts, uploads and a cross-field rule without its own message (new text crossFieldInvalid in all 34 languages) no longer answer in English.
  • Smoke test: a minlength above 5000 characters (up to 1 MB) is met with plain text instead of always failing.
  • Alert mail: a report that only says "Update available" no longer claims the problem is repeated at most once an hour; it says "one notice per new version".

v2.1.5

Choose a tag to compare

@github-actions github-actions released this 29 Sep 21:35

Fixes from the review of 2.1.4.

Security

  • Sign-in limit can no longer lock the admin out or tie up PHP. In 2.1.3/2.1.4 a blocked address waited in PHP (up to 10 s, holding a PHP worker and the session lock) and, once that queue was full, even the right token got 429 without being checked; one address could hold every worker of a small host and stall the public forms. Now PHP never waits: a blocked address still gets one token check per 2 seconds (right or wrong), every other attempt is answered 429 at once with the exact Retry-After (1–2 s instead of 900), so the right token gets in at the next free check. A signed-in browser is not affected. A blocked HTML sign-in shows the sign-in form with the wait instead of JSON.
  • Breaking: ?token= no longer signs a browser in. viewer.php, editor.php and sandbox.php ignore a token in the URL (login CSRF through a link, and an <img src="viewer.php?token=x"> on another site spending your address's wrong-token budget); they show the sign-in form with a notice. Sign in with the form or send X-BBF-Token. The sandbox has its own sign-in form. submissions.php?token= (scripts, CSV links) keeps working, except for requests a browser marks as coming from another site (Sec-Fetch-Site: cross-site), which cannot use it.
  • Breaking (since 2.1.3): a redirect target that is only a field ("redirect": "{{return_url}}") is not followed. Field values in redirect are percent-encoded since 2.1.3 so a respondent cannot change the target (open redirect); such a template led to a relative 404 in 2.1.3/2.1.4. Now it shows the success message instead. Use a fixed target with the value as a parameter, e.g. "/thanks?ref={{ref}}".
  • Upgrade dry run no longer runs unverified package code. Since 2.1.3 a dry run started the package's upgrader and smoke test (with your config.php loaded) after checking the package only against its own manifest, which proves integrity, not origin. Now package code runs only with --checksum=<SHA-256 from SHA256SUMS> (or --trust-package for a package you built yourself). Without it the dry run checks checksums and PHP syntax only, reports check: skipped and says how to verify; --apply then uses the installed upgrader. The printed next command keeps --checksum. Takes effect from the next upgrade after 2.1.5 is installed (the running maintenance.php decides).
  • Upgrade on Windows: only maintenance.php (held open by the running upgrade) may be written in place when renaming over it fails; any other file that cannot be renamed (after 5 retries, e.g. while a web request reads it) fails the upgrade and it rolls back, so the web never loads a half-written library.
  • submit.php's daily self-check logs a warning when requests arrive through a proxy (X-Forwarded-For / CF-Connecting-IP) while trusted_proxies is empty (check.php already showed it).

Fixed

  • Upgrade with PHP notices: with display_errors=On in the CLI (XAMPP, some Windows hosts) one notice, e.g. from config.php, broke the package upgrader's JSON result: the dry run failed, and a notice during --apply reported a finished upgrade as failed. Notices now go to stderr, the result is read after a marker line, and notices are listed under php_messages. The package side of this fix already applies to the upgrade from 2.1.4 to 2.1.5.
  • smoketest --live never sends to a made-up address. When an email field has a pattern that smoke_email does not match, the smoke test used to generate a matching address (e.g. a@firma.sk, a real domain) and the confirmation email went there. Now that form fails with "smoke_email does not match the pattern of email field …" and nothing is submitted; set smoke_email to your own address that matches.
  • Smoke test data: an extreme pattern ((a{1000}){1000}) or minlength no longer exhausts memory (generated values are capped at 5000 characters); a required field never gets an empty value (^a*$, a "Choose…" option with value "" is skipped).
  • Audit log: a smoke_token shorter than 16 characters is not a credential (live smoke refuses it, check.php/selfcheck warn), so it no longer masks ordinary words such as "contact" in the audit log.
  • "Back" after a redirect no longer stores a duplicate. Returning from the thank-you page (back/forward cache) showed the form still filled in with the button enabled, and one click stored the same answers again under a new key. The restored form is now cleared (with the success message), and the next fill gets its own key.
  • Malformed show_if no longer breaks every submission. A condition such as "all": {…} (object instead of a list), a string, or a non-string field/op made submit.php answer 500 on every submission while the browser ignored it. The server now evaluates such a condition like the browser (ignored), and the definition check (editor, smoketest, selfcheck, submit.php) names it, e.g. fields[3].show_if.all: Expected a list of conditions. An unknown op still means "equals" on both sides, so working forms are not rejected.
  • Redirect with {{field}}: a checkbox value (joined with ,) or a missing value (empty) no longer leaves a literal {{tags}} in the URL.
  • \S inside a character class (e.g. [^\S\r\n], "whitespace but no line break") now matches like the browser on the server too (a no-break space counted as non-space in PHP).
  • maintenance.php deliveries-retry: skipped_old now counts only old records that still have an undelivered job; delivered ledgers are no longer reported as "not checked". The result is cached in .delivery/.old-ledgers.json, so repeated runs do not reread every old record.
  • Nginx rules: the documented location blocks use ^~ prefixes, so config/, tests/ and the bbf_*.php libraries stay denied even when the server's location ~ \.php$ block comes first (a plain regex location lost to it).
  • Server messages in the respondent's language: "too many requests" (429), "request too large" (413), save failures (500/503), "still being processed" and the payment-start/status 503 answers now come from lang/*.php instead of fixed English text.
  • Respondent session cookie (PHPSESSID, CSRF only) is now HttpOnly, Secure on HTTPS and uses strict session IDs. SameSite stays at the browser default so forms embedded in an iframe keep working.
  • Editor: creating a form with a non-text name or id answers 400 instead of a PHP TypeError 500.
  • Release workflow: the third-party setup-php action now runs only in a read-only build job; attestation and publishing run in a separate job that uses only GitHub's own actions and re-checks SHA256SUMS before signing.
  • Tests: the editor-preview security test no longer races the iframe load (it failed about 1 in 5 runs on slow machines and could block a release).
  • selfcheck / check.php: an .htaccess.dist left by an earlier release (e.g. an older FTP upgrade) is ignored and named instead of recommending its older, weaker rules; the essential rules are always checked.
  • Docs: README and docs recommend --checksum with SHA256SUMS, say that the code-only ZIP contains CHANGELOG.md, and recommend tools/upgrade.php from the new release for installations on 2.1.0–2.1.2 (their own upgrader is older).