Skip to content

v5.1.0

Choose a tag to compare

@github-actions github-actions released this 21 Jul 07:30
v5.1.0
0364542

Send v5.1.0

37 commits since v5.0.0.
This release works through a pre-release audit of the whole app: the user-facing
failure modes it found, the accessibility gaps, the dependency backlog, and the
supply chain.

This is the build that powers send.anoni.net.

Nothing about how Send works has changed

The wire protocol, the URL format and the encryption are untouched. Existing
share links keep working. ffsend and other third-party clients keep working.
/__version__ still reports the API generation in version (v3.4.27) and our
release in release (now v5.1.0).

Fixed, and you would have felt these

A blocked WebSocket connection hung the upload forever. new WebSocket()
does not throw when a connection is refused or filtered, so the promise never
settled: the upload tile sat at 0% and the Cancel button did nothing. It now
fails, cancels, or times out, and says which. For anyone on a network that
filters WebSocket upgrades, this is the difference between "your network is
blocking this" and an app that just looks frozen.

Rate limiting had no interface. A 429, whether on upload or download, showed
the generic "Something went wrong" page whose only button invited an immediate
retry that burned another slot. It now explains that you are being asked to wait
and reads the server's Retry-After. The streaming download path also stopped
swallowing every error as a silent reload.

"This link has expired" was shown for a file that had reached its download
limit.
Once the record is gone the server genuinely cannot tell the two apart,
so the message no longer claims a cause it does not know.

Unsupported-browser pages pointed people at Firefox, which contradicted the
footer's own "not affiliated with Mozilla or Firefox" and was poor advice for a
Tor Browser user. They are browser-neutral now. An instance served over plain
HTTP also explains that the encryption APIs need a secure origin, rather than
blaming the visitor's browser.

Copy-link no longer reports success it never checked; the JavaScript-disabled
page is styled again; several dialog and copy interactions were fixed.

Accessibility

Dialogs are real dialogs now: a dialog role, a focus trap, Escape to close, and
focus returned where it was. The wrong-password message announces itself to a
screen reader instead of the field silently refocusing. The expiry and
download-count dropdowns, which decide how long a file stays on the server, have
accessible names.

Smaller, and more current

The application bundle is 53 KB gzipped, down from 71 KB, after dropping the
core-js polyfills a WebCrypto-capable browser never uses.

Behind that, a dependency backlog cleared with each dist/-changing step
verified against a real browser: @babel 7 → 8, tailwindcss 2 → 3 (its
built-in engine replaces the separate purge step), stylelint 14 → 17,
@fluent/*, prettier 1 → 3, and the css/postcss loader chain. The Tailwind
upgrade was checked with a 20-shot pixel comparison across screens, light and
dark, desktop and mobile. ws 7 → 8 was verified through a real WebSocket
round-trip.

The production tree keeps shrinking: cldr-core (1.2 MB) is down to the one
52 KB file the app actually reads, and nanohtml's Babel-transform dependencies,
including acorn, are stripped from the image because server-side rendering
never loads them. npm audit --omit=dev reports zero.

Localization

The five fork-added footer strings, previously English-only outside zh-TW and
zh-CN, now cover 33 major languages. Locales without a translation fall back to
English rather than showing a blank.

te and ast were dropped from the served set: below 70% coverage, they showed
the home page and the download-page explanation in English inside an otherwise
translated shell. A node-based locale check runs in CI now, so a broken .ftl or
a served locale with no directory fails the build.

Supply chain

  • SHA256SUMS.txt is itself signed, so the list that is supposed to catch a
    tampered server is no longer protected only by a release permission.
  • Trivy scans a saved tarball instead of a socket-mounted :latest image; every
    GitHub Action and the base image are pinned by commit digest, with Dependabot
    moving the pins.
  • Reproducibility is a CI gate: two Node versions build the same commit and the
    hashes are compared, instead of the claim resting on a one-time manual check.
  • The image that is tested, scanned and checksummed is now the exact image that
    is signed. This release is the first to run that path.

Verify this release

docker pull ghcr.io/anoni-net/send:5.1.0

cosign verify ghcr.io/anoni-net/send:5.1.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github.com/anoni-net/send/.github/workflows/publish.yml@.*$'

SHA256SUMS.txt below lists the sha256 of every file the server serves to a
browser, and it is signed:

cosign verify-blob SHA256SUMS.txt \
  --bundle SHA256SUMS.txt.cosign.bundle \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github.com/anoni-net/send/.github/workflows/publish.yml@.*$'

VERIFYING.md walks
through checking what an instance served you, the image signature, and rebuilding
from source.

Pin by digest in production so the tag cannot move under you:
ghcr.io/anoni-net/send@sha256:39daa1bb1d6e7a0a59441f4ff03529b174dc17cacd41125c78639259480629a6

Known gaps

Tailwind is on 3, not yet 4 (v4's CSS-first rewrite is a separate migration).
Password-protected links still derive their key from the full share URL, so the
same link opened on a different hostname reports the password as wrong: this is
inherited from upstream and matters for anyone running a .onion mirror
alongside a clearnet address. PBKDF2 still uses 100 iterations, bounded because
its salt already contains the URL secret.

Credit

Send was built by Mozilla and kept alive by
Tim Visée after Firefox Send shut down. This
release stands on both. We are not affiliated with, nor endorsed by, either.


Send v5.1.0(正體中文)

v5.0.0 起累積 37 個
commit。這個版本把整個 app 走了一遍發布前稽核:找出使用者會遇到的失敗、無障礙
的缺口、相依套件的積欠,以及供應鏈。

這就是 send.anoni.net 所使用的版本。

Send 的運作方式完全沒有改變

傳輸協定、URL 格式與加密都沒有動過。既有的分享連結繼續可用,ffsend 與其他
第三方客戶端也繼續可用。/__version__ 仍然分開回報 API 世代(version
v3.4.27)與我們自己的版號(release,現在是 v5.1.0)。

修正,而且你會有感

被阻擋的 WebSocket 連線會讓上傳永遠卡住。 new WebSocket() 在連線被拒或被
過濾時不會丟例外,所以那個 promise 永遠不會 settle:上傳磚停在 0%,取消鈕也
沒有反應。現在它會失敗、取消、或逾時,並說出是哪一種。對在會過濾 WebSocket
升級的網路上的人,這是「你的網路擋住了這個」與「這個網站看起來壞了」的差別。

速率限制沒有介面。 一個 429,不論在上傳或下載,都顯示通用的「Something
went wrong」頁面,而那一頁唯一的按鈕是邀請你立刻重試,重試會再吃掉一個額度。
現在它會說明你被要求稍候,並讀取伺服器的 Retry-After。串流下載路徑也不再把
每一個錯誤吞成無聲的重新載入。

「此連結已過期」被顯示在一個其實是達到下載次數上限的檔案上。 記錄一旦消失,
伺服器真的無法分辨這兩者,所以這個訊息不再宣稱它並不知道的原因。

不支援瀏覽器的頁面把人導向 Firefox,這與頁尾自己的「與 Mozilla 或 Firefox
沒有從屬關係」矛盾,對 Tor Browser 使用者也是錯誤的建議。現在它們是瀏覽器中性
的。以純 HTTP 提供的實例也會說明加密 API 需要安全來源,而不是怪罪訪客的瀏覽器。

複製連結不再回報它從未檢查過的成功;關閉 JavaScript 的頁面重新有了樣式;數個
對話框與複製的互動都修好了。

無障礙

對話框現在是真正的對話框:有 dialog role、焦點困陷、Escape 關閉,以及關閉後把
焦點還回原處。密碼錯誤的訊息會向螢幕報讀軟體播報,而不是欄位無聲地重新取得
焦點。決定檔案在伺服器上保留多久的到期與下載次數下拉,也有了可及名稱。

更小,也更新

應用程式的 bundle 是 53 KB gzip,從 71 KB 降下來,在移除 WebCrypto 瀏覽器
從不使用的 core-js polyfill 之後。

在這背後,一批積欠的相依升級清掉了,每一個會改變 dist/ 的步驟都對真瀏覽器
驗證過:@babel 7 → 8、tailwindcss 2 → 3(內建引擎取代獨立的 purge 步驟)、
stylelint 14 → 17、@fluent/*prettier 1 → 3,以及 css/postcss 的 loader
鏈。Tailwind 升級用了跨畫面、淺色與深色、桌面與手機的 20 張截圖像素比對來把關。
ws 7 → 8 則透過真實的 WebSocket 往返驗證。

正式相依樹持續縮小:cldr-core(1.2 MB)縮到 app 真正讀取的那一個 52 KB 檔案,
而 nanohtml 的 Babel transform 相依(包含 acorn)從映像中移除,因為伺服器端
算繪從不載入它們。npm audit --omit=dev 回報零。

在地化

五個 fork 新增的頁尾字串,原本在 zh-TW 與 zh-CN 之外只有英文,現在涵蓋 33 個
主要語言。沒有翻譯的語系會退回英文,而不是顯示空白。

teast 從被服務的語系中移除:覆蓋率低於 70%,它們在其餘已翻譯的外殼裡
把首頁與下載頁的說明顯示成英文。CI 現在會跑一個 node 版的語系檢查,所以一個
壞掉的 .ftl,或一個被服務卻沒有目錄的語系,都會讓建置失敗。

供應鏈

  • SHA256SUMS.txt 本身也被簽章,所以這份「用來抓出被竄改伺服器」的清單,不再
    只靠一個 release 權限保護。
  • Trivy 改掃一個存下來的 tarball,而不是掛載 socket 的 :latest 映像;每一個
    GitHub Action 與 base image 都用 commit digest 釘住,由 Dependabot 推進。
  • 可重現性是一道 CI 閘門:兩個 Node 版本建置同一個 commit,再比對雜湊,而不是
    讓這個宣稱只靠一次手動檢查。
  • 被測試、被掃描、被 checksum 的映像,現在就是被簽章的那一個。這個版本是第一次
    走這條路徑。

驗證這個版本

docker pull ghcr.io/anoni-net/send:5.1.0

cosign verify ghcr.io/anoni-net/send:5.1.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github.com/anoni-net/send/.github/workflows/publish.yml@.*$'

下方的 SHA256SUMS.txt 列出伺服器會送給瀏覽器的每一個檔案的 sha256,而且它有
簽章:

cosign verify-blob SHA256SUMS.txt \
  --bundle SHA256SUMS.txt.cosign.bundle \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github.com/anoni-net/send/.github/workflows/publish.yml@.*$'

VERIFYING.md 說明如何
檢查某個實例送給你的東西、映像簽章,以及從原始碼重新建置。

正式環境請用 digest 固定版本,這樣 tag 就不會在你不知情的情況下被移動:
ghcr.io/anoni-net/send@sha256:39daa1bb1d6e7a0a59441f4ff03529b174dc17cacd41125c78639259480629a6

已知的不足

Tailwind 在 3,還不是 4(v4 的 CSS-first 重寫是另一個獨立遷移)。密碼保護的
連結仍然從完整分享網址推導金鑰,所以同一個連結在不同主機名稱下開啟會顯示密碼
錯誤:這是從上游繼承的,對同時經營 .onion 鏡像與 clearnet 位址的人有影響。
PBKDF2 仍使用 100 次迭代,影響有限,因為它的 salt 已經包含網址裡的 secret。

致謝

Send 由 Mozilla 打造,並在 Firefox Send 關閉之後由
Tim Visée 維持存續。這個版本站在兩者之上。
我們與他們沒有從屬關係,也未獲得他們背書。