Commit Graph
438 Commits
Author SHA1 Message Date
dependabot[bot] 254b7c75a7 Bump ruby/setup-ruby
Bumps the github-actions group with 1 update in the / directory: [ruby/setup-ruby](https://github.com/ruby/setup-ruby).


Updates `ruby/setup-ruby` from 1.324.0 to 1.328.0
- [Release notes](https://github.com/ruby/setup-ruby/releases)
- [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb)
- [Commits](https://github.com/ruby/setup-ruby/compare/v1.324.0...e81a8fa391b11a595c1b7ed84177cc0fe02faf83)

---
updated-dependencies:
- dependency-name: ruby/setup-ruby
  dependency-version: 1.327.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-16 19:34:33 +00:00
Jeremy Daer 11d3fb9ce0 Merge pull request #365 from basecamp/enforce-script-policy
Enforce a script Content-Security-Policy
2026-10-15 19:20:35 -07:00
Jeremy Daer 2ba940c683 Allow an asset host only up to its own asset directory
An asset host's path can contain the asset directory too, as in
https://cdn.example.com/assets, and cutting each URL at the first one would
allow everything the host serves under it. Cut at the last one instead.
2026-10-15 19:13:26 -07:00
Jeremy Daer 0a494a10cb Leave the scheme off this origin's script sources
Behind a proxy that terminates TLS without X-Forwarded-Proto, Rails sees
http while the page is https, and an http source on a non-default port
doesn't match. A source without a scheme takes the page's own.
2026-10-15 19:01:12 -07:00
Jeremy Daer 2be2227f40 Allow scripts only from the asset directory, not the whole origin
Uploads are served from this origin too, so script-src 'self' would run one
that a cache still holds under a script type from before the upload guard.
Application pages now allow each importmap URL up to its asset directory
(relative root and asset host included), the inline importmap by hash, and
the service worker by worker-src.
2026-10-15 19:01:12 -07:00
Jeremy Daer 2a449401ec Leave a request without a policy when the policy is turned off
With DISABLE_CONTENT_SECURITY_POLICY=true there's no application policy,
and the hash callback started an empty one, so pages sent an empty
Content-Security-Policy header. Skip the callback then.
2026-10-15 19:01:12 -07:00
Jeremy Daer 9c37f41ff6 Keep a multi-range upload response's multipart type
A request for several byte ranges gets a 206 multipart/byteranges whose
boundary the client needs to split the body. Rewriting it to
octet-stream lost the boundary. No browser runs that type as a script,
so it passes through.
2026-10-15 19:01:12 -07:00
Jeremy Daer d713f2631d Say how long a pre-upgrade cached upload can last
Disk URLs are cached for an hour, but blob proxy URLs are cached
publicly and as immutable for a hundred years, so a copy taken that way
before the upgrade doesn't expire.
2026-10-15 19:01:12 -07:00
Jeremy Daer 562888251e Enforce a script policy that allows the importmap by hash
script-src 'self', object-src 'none' and base-uri 'self', enforced. The
two inline scripts the importmap helper emits are allowed by their
SHA-256, and the origins its URLs point to (a configured asset host, in
any form Rails accepts) are allowed by name. There is no per-request
nonce, so cached pages stay valid.

DISABLE_CONTENT_SECURITY_POLICY=true leaves the policy off, for an
install whose proxy or customization injects scripts.
2026-10-15 19:01:12 -07:00
Jeremy Daer cad2d7211d Serve uploads that aren't media as downloads
Uploaded files come from this origin, so one served with a JavaScript
type could run as one of the app's own scripts. Browsers accept sixteen
spellings of that type, and Disk URLs sign the type they were minted
with, so refusing a list of types at upload can't cover links already
out there. Instead, a middleware rewrites the type of anything Active
Storage reads out that isn't plainly media, a PDF or plain text to
application/octet-stream, whatever the URL says.
2026-10-15 19:01:12 -07:00
Jeremy Daer 821ff92dda Merge pull request #364 from basecamp/autolink-text-only
Link URLs only in a message's text
2026-10-15 18:59:57 -07:00
Jeremy Daer 9b271d0e19 Hand auto_link text escaped the way the serializer writes it
That's exactly what it was given before, with quotes and apostrophes
left as they are, rather than relying on its own sanitize pass to undo
h's &#39;. Covers an email address with an apostrophe.
2026-10-15 18:42:06 -07:00
Jeremy Daer 4e29c6e32b Keep a code block's leading newline when linking a message's text
A <pre> whose text starts with a newline needs one more written out to
survive being parsed again, as the html5 serializer does with
preserve_newline. Without it, linking text dropped one more of a code
block's leading blank lines than rendering did before.
2026-10-15 18:42:06 -07:00
Jeremy Daer 6052b123d5 Quote a mention as text when replying
Replying swaps each mention in the quoted copy for its text. Assigning
that text to outerHTML parsed it as html; replace the mention with a
text node instead.
2026-10-15 18:42:06 -07:00
Jeremy Daer 7ea05e18f8 Link URLs only in a message's text
auto_link finds URLs and email addresses with regular expressions over
the rendered html, which can't tell an attribute value from text, so it
could put a link inside an attribute. Hand it each text node on its own,
escaped, after the final sanitize, so links only ever land between tags.

Bump the presentation cache versions so nothing rendered the old way is
served again.
2026-10-15 18:42:06 -07:00
Jeremy Daer 1ac6dfde84 Merge pull request #366 from basecamp/bump-brakeman
Update Brakeman to 8.1.0
2026-10-15 18:41:55 -07:00
Jeremy Daer b2140cf2aa Update Brakeman to 8.1.0 2026-10-15 18:33:17 -07:00
Sam RubyandClaude Opus 5.5 edbc779f4d Test the pooled-connection class check on its own, and name the class
The rejection cases built plain Net::HTTP connections that were also
unpinned or proxied, so they failed the guard either way. They're built
as WebPush::Connections::HTTP now, so each fails only for its stated
reason, and a pinned, direct, TLS plain Net::HTTP is refused for being
one. The error says which class is required.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:41:45 +02:00
Sam RubyandClaude Opus 5.5 2daa01e26b Open pooled push connections as a Net::HTTP subclass that includes Stages
WebPush::Connections mixed Stages into each new connection with
`http.extend Stages` just before starting it. The connection is built in
one place, WebPush::PersistentRequest, so it can be built from a class
that already includes Stages: the same method lookup, without giving
every pooled connection its own singleton class. Connections#request
now also refuses a connection that isn't one, so a caller that builds a
plain Net::HTTP gets the ArgumentError rather than a connection whose
failures can't be told apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:41:45 +02:00
Rosa GutierrezandClaude Opus 5.5 2d4b05a74a Clear no_proxy in the link preview proxy test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:45:43 +02:00
Rosa GutierrezandClaude Opus 5.5 a7e9ea53dd Don't let an http_proxy environment variable bypass the pinned address in link previews
Opengraph::Fetch connects to the IP it checked, not to a fresh lookup of
the host. With http_proxy set, Net::HTTP would instead send the hostname
to the proxy, which looks it up again, so the connection could end up
somewhere other than the checked address. Pass nil as the proxy address,
as web push delivery already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:45:43 +02:00
GPT on behalf of DHH b52b244f38 Include executable template code in application KLOC 2026-10-09 12:52:12 +02:00
GPT on behalf of DHH 05c5a2c0d7 Count only backend application code in benchmark KLOC 2026-10-08 22:13:01 +02:00
GPT on behalf of DHH 81973c564c Document application KLOC alongside validated benchmarks 2026-10-08 22:07:01 +02:00
GPT on behalf of DHH 5796e5c837 Update verified Rails and Django benchmark figures 2026-10-08 21:08:12 +02:00
GPT on behalf of DHH 19c9e6b6f9 Merge Rails empty-mentions optimization by GPT on behalf of DHH 2026-10-08 21:05:15 +02:00
GPT on behalf of DHH 0aa339d81e Avoid room queries for messages without mentions 2026-10-08 20:46:01 +02:00
GPT on behalf of DHH b042182791 Use final published Rails benchmark results 2026-10-08 15:55:51 +02:00
GPT on behalf of DHH 6944a4a796 Update verified Campfire benchmark comparison 2026-10-08 15:38:48 +02:00
GPT on behalf of DHH 4bcc745f7a Interrupt WAL checkpoint shutdown and retain exclusive lock ownership 2026-10-08 15:28:42 +02:00
GPT on behalf of DHH 2e89e08003 Merge #303: retain Sam Ruby’s previously incorporated Herb changes 2026-10-08 13:27:39 +02:00
GPT on behalf of DHH 7331d3a946 Create messages, search entries and unread state atomically
Apply the Laravel implementation’s MessageWriter architecture to Rails creation: save native Action Text and attachment metadata, insert the search entry and update unread memberships in the message transaction. Keep notification jobs after commit and preserve Room#receive and existing serialized jobs.

Cover native HTML/mentions, attachment-only search, derived-write failures, outer rollback and SQL commit ordering. Updates and destruction retain their current indexing behavior.
2026-10-08 13:22:14 +02:00
GPT on behalf of DHH e9f8b7117e Merge #344: upgrade the production and exporter runtimes to Ruby 4.0.7 2026-10-08 13:15:30 +02:00
GPT on behalf of DHH 59eb690f23 Merge #343: upgrade Puma to 8.0.2 2026-10-08 13:15:30 +02:00
GPT on behalf of DHH 9f1b73a75f Merge #342: update the RuboCop toolchain 2026-10-08 13:15:30 +02:00
GPT on behalf of DHH da44351c5f Merge #341: exercise actual libvips loader blocks 2026-10-08 13:15:30 +02:00
Thomas KlemmandAmp 24042e6b4c Upgrade runtime to Ruby 4.0.7
Amp-Thread-ID: https://ampcode.com/threads/T-01a11acc-6153-7316-a06c-fcf2d713cd64
Co-authored-by: Amp <amp@ampcode.com>
2026-10-08 10:54:35 +00:00
Thomas KlemmandAmp 66883b6fb1 Update RuboCop toolchain
Amp-Thread-ID: https://ampcode.com/threads/T-01a11acc-6153-7316-a06c-fcf2d713cd64
Co-authored-by: Amp <amp@ampcode.com>
2026-10-08 10:54:35 +00:00
Thomas KlemmandAmp bb338a8c74 Test blocked libvips loaders directly
Amp-Thread-ID: https://ampcode.com/threads/T-01a11acc-6153-7316-a06c-fcf2d713cd64
Co-authored-by: Amp <amp@ampcode.com>
2026-10-08 10:54:35 +00:00
GPT on behalf of DHH 9b8a1683a7 Reuse native plain text conversion for attachment-free content 2026-10-08 12:41:14 +02:00
GPT on behalf of DHH a0d72938f3 Keep transaction guards temporary and simplify quick boost forms 2026-10-08 12:40:31 +02:00
GPT on behalf of DHH a9007010c1 Check cache transactions without leasing a database connection 2026-10-08 12:34:18 +02:00
GPT on behalf of DHH bbee420097 Reuse epoch-verified record snapshots and avoid empty push payloads 2026-10-08 12:26:50 +02:00
GPT on behalf of DHH 74381e8b58 Retain content-validated message fragments across unrelated commits 2026-10-08 12:00:45 +02:00
Thomas KlemmandAmp 9ad45892ed Update RuboCop toolchain
Amp-Thread-ID: https://ampcode.com/threads/T-01a11acc-6153-7316-a06c-fcf2d713cd64
Co-authored-by: Amp <amp@ampcode.com>
2026-10-08 09:48:51 +00:00
GPT on behalf of DHH 59e1c6d3d7 Merge #335: reject invalid QR code requests before rendering 2026-10-08 11:43:27 +02:00
GPT on behalf of DHH 2c53c46767 Reuse token-free message controls and render posting deliveries once 2026-10-08 11:43:27 +02:00
Thomas KlemmandAmp 5940ea068f Test blocked libvips loaders directly
Amp-Thread-ID: https://ampcode.com/threads/T-01a11acc-6153-7316-a06c-fcf2d713cd64
Co-authored-by: Amp <amp@ampcode.com>
2026-10-08 09:35:53 +00:00
GPT on behalf of DHH e55b6dc3e5 Merge #339: checkpoint SQLite WAL outside request threads 2026-10-08 11:33:18 +02:00
GPT on behalf of DHH f36b5a2fae Merge #336: avoid rewriting already-unread room memberships 2026-10-08 11:33:18 +02:00