The cookie approach seems like the more secure aproach because it ties the authentication or registration attempt to the user's browser session, but it doesn't work reliably on Chrome for Windows. Also, a simila problem pops up on Chrome for Linux if the session is used instead of a separate cookie. It looks like the browser doesn't propagate the state change through fast enough which results in some requests contaiining the new/updated cookie, and others don't, which results in sporadic failures. Since we use a signed and expiring challange we still get protection from replay attacks and tampering which enables us to omit the cookie entierly and rely on the challange's signature to prove expiration and authenticity. The only thing we lose is the ability to tie and attemp to a single browser session.
See: https://github.com/w3c/webauthn/wiki/Explainer:-WebAuthn-challengeURL which proposes to add the same challange fetching logic as part of the standard
See: https://github.com/w3c/webauthn/issues/1856 which discusses issues that arise from having expiring challanges (which the spec recommends)
See: https://github.com/OneUptime/oneuptime/security/advisories/GHSA-gjjc-pcwp-c74m which is an explot that can happen if the server isn't able to verify the authenticity of challanges that are sent outside of a cookie
This simplifies the loading and cleanup logic, while providing ubiquitous support across JS frameworks. Buttons can now be added in any way imaginable and still work without requiring additional initialization. The upside of this aproach is that it doesn't require a mutation observer nor a global click listener, and is supported by all browsers that also support passkeys.
This is a guess based on a few articles I read where Windows Hello doesn't play nice with Same-Site: Strict. I have no credible evidence to back this up.
No longer needed since the scrubber is not memoized across calls and the
text node length guard prevents the performance issue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A large comment body was causing Regexp::TimeoutError in production.
The URI regexp isn't catastrophically backtracking — it's linear — but
with a long enough string it exceeds the 1s Regexp.timeout set by
Rails.
Skip scanning text nodes over 10KB, which is well beyond any
reasonable content for auto-linking and still keeps us many orders of
magnitued under the timeout on an unloaded machine.
Fixes FIZZY-Q4
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The scrubber was memoized on the view helper in b0fa6525 so that
@regexp_timeout_reported would stop scanning remaining text nodes
after a timeout. But memoizing on the helper leaks that flag across
all format_html calls for the entire request — if one comment triggers
a timeout, every subsequent comment on the page loses auto-linking.
Use a fresh scrubber per call. The @regexp_timeout_reported flag still
works within a single document's scrub, which is the intended scope.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three changes needed to support navigating back from a card to the
activity page:
- Add root_path to the prefer_referrer allowlist on the card show page
- Switch event links from HTML target="_top" to data-turbo-frame="_top"
so Turbo handles the navigation and turbo:before-visit fires to save
the referrer
- Normalize trailing slashes in the referrer path comparison so
/account_id and /account_id/ both match
ref: https://app.fizzy.do/5986089/cards/2390
* test: update sign_in_as system test helper to be more generic
and support fixture users with more than one board
* test: backfill a test for the card "back link"
when coming from a board filter page
* Fix back navigation to filter views from cards
When navigating from a filter view to a card, the back link now shows
the filter's label and navigates back to the filter URL instead of the
card's board.
The turbo-navigation Stimulus controller stores the page title alongside
the referrer URL in sessionStorage. On the card show page, a
referrerBackLink target rewrites the back link's href and label from the
stored values. This is scoped only to the card show page to avoid
navigation loops in multi-level deep pages.
* Add referrer allowlist to prevent stale referrer rewrites
The back link is only rewritten when the stored referrer's path matches
an allowlist of paths passed via prefer_referrer. This prevents stale
referrers from unrelated pages (e.g., settings) from overriding the
card's back link.
* Move back link navigation tests to dedicated system test file
Extract back link tests from smoke_test.rb into their own file since
they test specific JavaScript behavior that needs regression coverage.
Move sign_in_as helper to ApplicationSystemTestCase for reuse.
* Move markdown paste tests to dedicated system test file
* Exclude export/import blobs and attachments from account exports
Export and import file blobs were being assigned the account's
account_id (via the Current.account default), causing subsequent
exports to include previous export/import ZIP files in the data.
This roughly doubled the export size each time and made imports
fail with an IntegrityError on unrecognized "Account::Export"
record type.
Filter out blobs attached to Export/Import records in
BlobRecordSet, FileRecordSet, and a new AttachmentRecordSet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Use STI base type for export blob exclusion filter
Rails polymorphic_name returns base_class.name for STI models,
so ActiveStorage stores record_type as "Export" rather than the
concrete subclass names. Filter on the base type accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix export filtering for mixed-use Active Storage blobs
Exclude only internal-only blobs for account export metadata/file sets, scope attachment subqueries by account, and add regression coverage for blobs shared between user records and export attachments.
* Simplify export blob filtering
Exports always create fresh blobs, so a blob can never be legitimately
shared between an Export/Import and a real account record. Replace the
internal_only/external split with a single excluded_blob_ids subquery
and drop the contrived shared-blob test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Fix multi-term SQLite FTS5 search causing a 500 error
When filtering cards by more than one term, `matching` was called
once per term via an association join. Rails did not deduplicate
association joins, resulting in two JOINs to `search_records_fts`
in a single query. SQLite FTS5 then raised "ambiguous column name"
when evaluating the MATCH condition.
Fix by using a string join instead, which Rails deduplicates so
only one JOIN to `search_records_fts` is generated regardless of
how many terms are searched.
Fixes: https://github.com/basecamp/fizzy/discussions/2354
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: verify multi-term search requires ALL terms to match
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Mike Dalessio <mike@37signals.com>