跳轉到

vendor

第三方程式庫,原封不動放進來,不做任何修改。改過就失去「這是上游那一份」的可審性, 讀者要驗的時候只能相信我們的說法。

檔案 來源 版本 授權
qrcode-generator.js kazuhikoarase/qrcode-generator 1.4.4 MIT
jsQR.js cozmo/jsQR 1.4.0 Apache-2.0,授權全文見 jsQR-LICENSE.txt
pdf-lib.min.js Hopding/pdf-lib 1.17.1 MIT,授權全文見 pdf-lib-LICENSE.txt
pdfjs/pdf.min.mjspdfjs/pdf.worker.min.mjs mozilla/pdf.js(npm pdfjs-dist 的 legacy build) 6.3.289 Apache-2.0,授權全文見 pdfjs/pdfjs-LICENSE.txt
age/age-encryption/ FiloSottile/typage(npm age-encryption 0.3.1 BSD-3-Clause,授權全文見 age/age-encryption/LICENSE
age/noble-ciphers/ paulmillr/noble-ciphers 2.1.1 MIT,age/noble-ciphers/LICENSE
age/noble-curves/ paulmillr/noble-curves 2.0.1 MIT,age/noble-curves/LICENSE
age/noble-hashes/ paulmillr/noble-hashes 2.0.1 MIT,age/noble-hashes/LICENSE
age/noble-post-quantum/ paulmillr/noble-post-quantum 0.5.3 MIT,age/noble-post-quantum/LICENSE
age/scure-base/ paulmillr/scure-base 2.0.0 MIT,age/scure-base/LICENSE
pico/pico.js nenadmarkus/picojs 2022-08-25 的 master(無版本號) MIT
pico/facefinder nenadmarkus/pico commit c2e81f9 MIT

為什麼不自己寫

QR 編碼寫錯的典型後果是產生一個「掃得出來但內容錯」的碼,比壞掉更糟。Reed-Solomon 與遮罩選擇那兩段特別容易出錯,而錯了不會有任何徵兆。這一份用了十幾年、被大量專案 採用,比重寫一份可靠。

tools/test_qrcode.mjs 另外寫了一個獨立的解碼器,把產生出來的矩陣讀回字串再比對, 所以「我們呼叫的方式對不對」有測試守著,不是純粹相信上游。

jsQR.js 走的是相反方向:把影像解回文字。它要處理定位、透視校正與容錯還原,那是比編碼 更大的工程,自己寫不切實際。tools/test_qrread.mjsqrcode-generator 產生已知內容 的碼、算成像素、再交給 jsQR 讀回來比對,兩個各自獨立的函式庫互相驗證。

pdf-lib.min.js 處理的是 PDF。那個格式的每個物件在交叉索引表裡都記著位元組位置, 拿掉一段東西之後整張表要重算,而 PDF 1.5 之後常見的物件流還會把好幾個物件壓進同一段 壓縮資料裡。自己寫一個能正確處理這些的解析器不切實際,寫半套的後果是「宣告清乾淨、 內容還在」,那比不清更危險。

Apache-2.0 與 MIT 都要求散布時附上授權副本,所以 jsQR-LICENSE.txtpdf-lib-LICENSE.txt 也在這個目錄裡,不要刪。

pico/ 底下兩個檔案的授權都是 MIT,但上游兩個 repo 都沒有放 LICENSE 檔,也沒有 版權人那一行,所以這裡沒有對應的授權全文可以附。授權的依據寫在兩個地方: pico.js 第一行的註解(This library is released under the MIT license,那一行 原封不動留在檔案裡),以及 picojs 的 readme 的 License 一節寫著 MIT。要重新確認 的時候看那兩處。

pico.js 沒有發行版本號,取的是 2022-08-25 那次 push 的 master。級聯檔取自同一位 作者的 pico repo,網址上釘死 commit c2e81f9,那是 picojs 的範例自己指向的 版本。兩個檔案的 SHA-256:

785b981cc79e5fa3f7557dc3fa7773629d7529994d7627de41b77d8687649309  pico/pico.js
d8014993e7298c7b1865d1f8b855d6dbf4ec5c808bf879e2091ab6837abf90cd  pico/facefinder

為什麼臉部偵測用 pico 而不是 TensorFlow.js

截圖遮蔽的「自動找出人臉」需要一個偵測器。主流做法是 TensorFlow.js 加 BlazeFace, 量過實際要載入的東西是 tf-core.min.js 287 KB、tf-converter.min.js 315 KB、 tf-backend-cpu.min.js 129 KB、模型權重 455 KB,合計約 1.2 MB。而且 @tensorflow-models/blazeface 的 repo 已經掛上棄用公告。

pico 這條是 240 KB,純 JavaScript,不需要 WebGL 也不需要 WASM,Tor Browser 只要 JavaScript 開著就能執行。代價是它只抓得到正面、直立、夠大的臉。

那個代價在這一頁的設計裡被吸收掉了:偵測只提候選框,讀者看得到、刪得掉、補得上, 按下產生的仍然是人。漏抓由人補,多抓一鍵刪掉。另外兩百行的 JavaScript 讀者自己 讀得完,那比較接近這一區「看得懂的人可以自己驗」的立場。

pdfjs/ 為什麼取最新版而不是小一點的舊版

PDF 頁面整理的「看裡面有什麼」要把文字層抽出來,pdf-lib 做不到,那需要一個完整的 解析器。v5 量到約 1.4 MB,v6 是 1.8 MB,差 0.4 MB。

還是取最新的 6.3.289。這支解析的是別人給的檔案,而 pdf.js 出過從字型那條路做到 任意程式碼執行的 CVE(CVE-2024-4367),版本落後的風險比 0.4 MB 重要。

呼叫的時候另外關掉 isEvalSupportedenableXfa,這一頁只要文字與縮圖, 不需要那些能力。tools/test_pdfpages.mjs 盯著這兩個選項。

兩個檔案的 SHA-256:

f401927e692efc7735e0cd528c490d0dd31b7f0972c122b7040df805be45cce4  pdfjs/pdf.min.mjs
a33cfe728c584fdba4fcc1fd54bcdc2f9f2f13889ddbb5b2bd1d0f8cbe49b84e  pdfjs/pdf.worker.min.mjs

age/ 底下的 ES module

typage 與它相依的五個套件沒有單一檔案的發行版,所以照 npm tarball 裡的目錄結構原封不動放進 age/,每個套件自己的 LICENSEpackage.json 跟著放。只放 age-encryption/dist/index.js 從 import 一路連到的 38 支 .js(typage 的 .d.ts 與套件裡沒有被 import 的其他模組不放), 清單由 tools/test_agecrypt.mjs 從 import 重算一次比對,多一個或少一個都會紅。

頁面用 <script type="importmap">age-encryption@noble/... 等名稱接到 age/ 底下的檔案, js/agecrypt.jsimport("age-encryption")。要驗跟上游是不是同一份,拿下面的 tarball 雜湊去對:

tarball sha256
age-encryption-0.3.1.tgz 5a318d61f29bafc02810feef65dcf9d82b17a44d9c777eed658d2abea5f74bc5
noble-ciphers-2.1.1.tgz 1704cc39be04737a17f17c37b8556b195e2b6ed442ecc2b7be6230b2e26f0d82
noble-curves-2.0.1.tgz 1271ac0dfa27c93e464e64b65ff2a255eea52c449ca86c149cfb0b9786dd9029
noble-hashes-2.0.1.tgz 638ffb3053a7e7478c9e54a6e297f3601299ee570a41112e501af7050d086a0a
noble-post-quantum-0.5.3.tgz 30efd5f906fb181552a0ade106bb6d004725303360f6c041346fd578fff3438e
scure-base-2.0.0.tgz 758d9a74d504922c21f12afbb832a8da13634657fa842c1aee1ec3eac82c8902

換版時重新執行 tools/test_agecrypt.mjs,它用 Node 內建的 crypto 獨立實作了 age 的密語模式, typage 的輸出要能被它解開、它的輸出也要能被 typage 解開,兩邊互驗才算通過。