mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: remove automatic third-party egress of user data + optional strict offline mode (OSM tiles, Scalar fonts, editor fonts, AI model downloads) (#422)
* fix: remove all automatic third-party egress (OSM tiles, Scalar fonts, editor Google Fonts, AI model download fallbacks) Phone-home audit follow-up. The product no longer makes any automatic third-party request; user-initiated click-outs stay, and production now fails closed on missing AI models. 1. GPS leak via OSM tiles: the strip-metadata panel auto-loaded tile.openstreetmap.org tiles encoding the photo's GPS position. The Leaflet mini-map is gone; coordinates render as text plus an explicit View on map link (openstreetmap.org, opens on click only). Removed tile.openstreetmap.org from the CSP img-src, dropped the leaflet dependency, added the viewOnMap i18n key to all 21 locales. 2. Scalar docs fonts: /api/docs loaded Inter and JetBrains Mono from fonts.scalar.com. Scalar now renders with withDefaultFonts: false and both --scalar-font and --scalar-font-code pinned to system stacks; fonts.scalar.com removed from the docs CSP font-src. Verified by injecting GET /api/docs/: config carries withDefaultFonts false and the served page has no fonts.scalar.com reference. 3. Editor Google Fonts: the editor font picker built fonts.googleapis.com stylesheet URLs for 25 web fonts the served CSP already blocked. The remote loading path is deleted; the picker now offers system fonts only, with a SELF_HOSTED_FONTS seam (FontFace API, same origin) for bundling fonts later. Unknown families saved in old documents fall back to the browser default. 4. Python sidecar fails closed on model downloads: new packages/ai/python/offline_guard.py gates every runtime download fallback (inpaint, outpaint, restore, noise_removal, detect_faces, enhance_faces, face_landmarks, red_eye_removal, remove_bg, ocr, transcribe, upscale) behind SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1 with an actionable error. Bundled models keep working untouched. 5. OCR and transcription library-internal downloads: unbundled PaddleOCR language and detection fallbacks now raise the guard error naming the language instead of resolving models over the network; faster-whisper gets local_files_only when downloads are off. 6. GFPGAN and CodeFormer cwd-relative weights: facexlib and codeformer-pip resolve helper weights relative to the process cwd and fetch them from GitHub when absent. They are now symlinked from the installed bundle files under MODELS_PATH/gfpgan/facelib before the libraries load, failing closed when unresolvable. Defense in depth: HF_HUB_OFFLINE=1 and TRANSFORMERS_OFFLINE=1 are set in the runtime image and in the sidecar spawn env; install_feature.py lifts them for user-initiated bundle installs and restores them afterwards (it can run in-process inside the dispatcher). SNAPOTTER_ALLOW_MODEL_DOWNLOAD is documented in .env.example, default off. Validation: typecheck 9/9 workspaces, Biome clean on touched files, 5178 unit tests pass, py_compile on all touched scripts, guard behavior exercised in both dispatcher exec and per-request import modes, zero remaining runtime references to the three hosts. Docker build and live AI inference need post-merge verification on the GPU host. Claude-Session: https://claude.ai/code/session_01XGB4pGvTvb7sUX4JN745U7 * fix: allow AI model downloads by default, make strict offline mode opt-in Product call: ease of use first. The download gating from the previous commit inverts its default: runtime model fetches (public model weights only, never user data) are allowed out of the box so AI tools self-heal, and SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0 becomes the explicit strict offline mode for airgapped deployments, where every fallback raises the actionable error instead of fetching. Changes: offline_guard blocks only on an explicit 0/false; the unconditional HF_HUB_OFFLINE/TRANSFORMERS_OFFLINE image ENV is removed and bridge.ts sets those flags for the sidecar only in strict mode; .env.example documents the new default; install_feature's lift/restore stays. All bundled-path preferences, pre-existence checks, and symlink pre-placement remain, so installed bundles never trigger a download. The OSM, Scalar font, and editor font fixes are unchanged. Validation rerun: typecheck 9/9, Biome clean on touched files, 5178 unit tests pass, py_compile on touched scripts, guard behavior verified for unset/1 (allowed) and 0/false (blocked with the new message). Claude-Session: https://claude.ai/code/session_01XGB4pGvTvb7sUX4JN745U7
This commit is contained in:
@@ -1252,6 +1252,7 @@ export const ar: TranslationKeys = {
|
||||
stripIcc: "إزالة ICC (ملف تعريف الألوان)",
|
||||
stripXmp: "إزالة XMP (البيانات الوصفية الموسعة)",
|
||||
locationFound: "تم العثور على الموقع",
|
||||
viewOnMap: "عرض على الخريطة",
|
||||
locationWarning: "تحتوي هذه الصورة على موقعك الدقيق. يُنصح بإزالة بيانات GPS قبل المشاركة.",
|
||||
metadataSections: "تم العثور على {count} قسم بيانات وصفية",
|
||||
metadataSectionsPlural: "تم العثور على {count} أقسام بيانات وصفية",
|
||||
|
||||
@@ -1262,6 +1262,7 @@ export const de: TranslationKeys = {
|
||||
stripIcc: "ICC entfernen (Farbprofil)",
|
||||
stripXmp: "XMP entfernen (erweiterte Metadaten)",
|
||||
locationFound: "Standort gefunden",
|
||||
viewOnMap: "Auf Karte anzeigen",
|
||||
locationWarning:
|
||||
"Dieses Bild enthält Ihren genauen Standort. Erwägen Sie, GPS-Daten vor dem Teilen zu entfernen.",
|
||||
metadataSections: "{count} Metadaten-Abschnitt gefunden",
|
||||
|
||||
@@ -1215,6 +1215,7 @@ export const en = {
|
||||
stripIcc: "Strip ICC (color profile)",
|
||||
stripXmp: "Strip XMP (extensible metadata)",
|
||||
locationFound: "location found",
|
||||
viewOnMap: "View on map",
|
||||
locationWarning:
|
||||
"This image contains your precise location. Consider removing GPS data before sharing.",
|
||||
metadataSections: "{count} metadata section found",
|
||||
|
||||
@@ -1247,6 +1247,7 @@ export const es: TranslationKeys = {
|
||||
stripIcc: "Eliminar ICC (perfil de color)",
|
||||
stripXmp: "Eliminar XMP (metadatos extensibles)",
|
||||
locationFound: "ubicación encontrada",
|
||||
viewOnMap: "Ver en el mapa",
|
||||
locationWarning:
|
||||
"Esta imagen contiene tu ubicación precisa. Considera eliminar los datos GPS antes de compartirla.",
|
||||
metadataSections: "{count} sección de metadatos encontrada",
|
||||
|
||||
@@ -1268,6 +1268,7 @@ export const fr: TranslationKeys = {
|
||||
stripIcc: "Supprimer ICC (profil colorimétrique)",
|
||||
stripXmp: "Supprimer XMP (métadonnées extensibles)",
|
||||
locationFound: "localisation trouvée",
|
||||
viewOnMap: "Voir sur la carte",
|
||||
locationWarning:
|
||||
"Cette image contient votre localisation précise. Pensez à supprimer les données GPS avant de la partager.",
|
||||
metadataSections: "{count} section de métadonnées trouvée",
|
||||
|
||||
@@ -1083,6 +1083,7 @@ export const hi: TranslationKeys = {
|
||||
stripIcc: "ICC हटाएं (कलर प्रोफाइल)",
|
||||
stripXmp: "XMP हटाएं (विस्तारित मेटाडेटा)",
|
||||
locationFound: "लोकेशन मिली",
|
||||
viewOnMap: "मानचित्र पर देखें",
|
||||
locationWarning: "इस इमेज में आपकी सटीक लोकेशन है। शेयर करने से पहले GPS डेटा हटाने पर विचार करें।",
|
||||
metadataSections: "{count} मेटाडेटा सेक्शन मिला",
|
||||
metadataSectionsPlural: "{count} मेटाडेटा सेक्शन मिले",
|
||||
|
||||
@@ -1259,6 +1259,7 @@ export const id: TranslationKeys = {
|
||||
stripIcc: "Hapus ICC (profil warna)",
|
||||
stripXmp: "Hapus XMP (metadata yang dapat diperluas)",
|
||||
locationFound: "lokasi ditemukan",
|
||||
viewOnMap: "Lihat di peta",
|
||||
locationWarning:
|
||||
"Gambar ini berisi lokasi presisi Anda. Pertimbangkan untuk menghapus data GPS sebelum membagikan.",
|
||||
metadataSections: "{count} bagian metadata ditemukan",
|
||||
|
||||
@@ -1263,6 +1263,7 @@ export const it: TranslationKeys = {
|
||||
stripIcc: "Rimuovi ICC (profilo colore)",
|
||||
stripXmp: "Rimuovi XMP (metadati estensibili)",
|
||||
locationFound: "posizione trovata",
|
||||
viewOnMap: "Vedi sulla mappa",
|
||||
locationWarning:
|
||||
"Questa immagine contiene la tua posizione precisa. Considera di rimuovere i dati GPS prima di condividerla.",
|
||||
metadataSections: "{count} sezione di metadati trovata",
|
||||
|
||||
@@ -1219,6 +1219,7 @@ export const ja: TranslationKeys = {
|
||||
stripIcc: "ICC削除(カラープロファイル)",
|
||||
stripXmp: "XMP削除(拡張メタデータ)",
|
||||
locationFound: "位置情報あり",
|
||||
viewOnMap: "地図で表示",
|
||||
locationWarning:
|
||||
"この画像には正確な位置情報が含まれています。共有前にGPSデータの削除をご検討ください。",
|
||||
metadataSections: "{count}個のメタデータセクションが見つかりました",
|
||||
|
||||
@@ -1204,6 +1204,7 @@ export const ko: TranslationKeys = {
|
||||
stripIcc: "ICC 제거 (색상 프로파일)",
|
||||
stripXmp: "XMP 제거 (확장 메타데이터)",
|
||||
locationFound: "위치 정보 발견",
|
||||
viewOnMap: "지도에서 보기",
|
||||
locationWarning:
|
||||
"이 이미지에 정확한 위치 정보가 포함되어 있습니다. 공유 전에 GPS 데이터 제거를 권장합니다.",
|
||||
metadataSections: "메타데이터 섹션 {count}개 발견",
|
||||
|
||||
@@ -1263,6 +1263,7 @@ export const nl: TranslationKeys = {
|
||||
stripIcc: "ICC verwijderen (kleurprofiel)",
|
||||
stripXmp: "XMP verwijderen (uitgebreide metadata)",
|
||||
locationFound: "locatie gevonden",
|
||||
viewOnMap: "Op kaart bekijken",
|
||||
locationWarning:
|
||||
"Deze afbeelding bevat je precieze locatie. Overweeg GPS-data te verwijderen voor het delen.",
|
||||
metadataSections: "{count} metadata-sectie gevonden",
|
||||
|
||||
@@ -1262,6 +1262,7 @@ export const pl: TranslationKeys = {
|
||||
stripIcc: "Usuń ICC (profil kolorów)",
|
||||
stripXmp: "Usuń XMP (rozszerzalne metadane)",
|
||||
locationFound: "znaleziono lokalizację",
|
||||
viewOnMap: "Zobacz na mapie",
|
||||
locationWarning:
|
||||
"Ten obraz zawiera Państwa dokładną lokalizację. Zalecamy usunięcie danych GPS przed udostępnieniem.",
|
||||
metadataSections: "Znaleziono {count} sekcję metadanych",
|
||||
|
||||
@@ -1261,6 +1261,7 @@ export const ptBR: TranslationKeys = {
|
||||
stripIcc: "Remover ICC (perfil de cor)",
|
||||
stripXmp: "Remover XMP (metadados extensíveis)",
|
||||
locationFound: "localização encontrada",
|
||||
viewOnMap: "Ver no mapa",
|
||||
locationWarning:
|
||||
"Esta imagem contém sua localização precisa. Considere remover os dados GPS antes de compartilhá-la.",
|
||||
metadataSections: "{count} seção de metadados encontrada",
|
||||
|
||||
@@ -1258,6 +1258,7 @@ export const ru: TranslationKeys = {
|
||||
stripIcc: "Удалить ICC (цветовой профиль)",
|
||||
stripXmp: "Удалить XMP (расширяемые метаданные)",
|
||||
locationFound: "местоположение найдено",
|
||||
viewOnMap: "Показать на карте",
|
||||
locationWarning:
|
||||
"Это изображение содержит Ваше точное местоположение. Рекомендуем удалить GPS-данные перед публикацией.",
|
||||
metadataSections: "Найден {count} раздел метаданных",
|
||||
|
||||
@@ -1258,6 +1258,7 @@ export const sv: TranslationKeys = {
|
||||
stripIcc: "Ta bort ICC (färgprofil)",
|
||||
stripXmp: "Ta bort XMP (utökad metadata)",
|
||||
locationFound: "plats hittad",
|
||||
viewOnMap: "Visa på karta",
|
||||
locationWarning:
|
||||
"Denna bild innehåller din exakta plats. Överväg att ta bort GPS-data innan du delar.",
|
||||
metadataSections: "{count} metadatasektion hittad",
|
||||
|
||||
@@ -1245,6 +1245,7 @@ export const th: TranslationKeys = {
|
||||
stripIcc: "ลบ ICC (โปรไฟล์สี)",
|
||||
stripXmp: "ลบ XMP (ข้อมูลเมตาขยาย)",
|
||||
locationFound: "พบตำแหน่ง",
|
||||
viewOnMap: "ดูบนแผนที่",
|
||||
locationWarning: "ภาพนี้มีตำแหน่งที่ตั้งแม่นยำของคุณ ควรพิจารณาลบข้อมูล GPS ก่อนแชร์",
|
||||
metadataSections: "พบ {count} ส่วนข้อมูลเมตา",
|
||||
metadataSectionsPlural: "พบ {count} ส่วนข้อมูลเมตา",
|
||||
|
||||
@@ -1261,6 +1261,7 @@ export const tr: TranslationKeys = {
|
||||
stripIcc: "ICC kaldır (renk profili)",
|
||||
stripXmp: "XMP kaldır (genişletilmiş meta veri)",
|
||||
locationFound: "konum bulundu",
|
||||
viewOnMap: "Haritada görüntüle",
|
||||
locationWarning:
|
||||
"Bu görüntü kesin konumunuzu içeriyor. Paylaşmadan önce GPS verilerini kaldırmanız önerilir.",
|
||||
metadataSections: "{count} meta veri bölümü bulundu",
|
||||
|
||||
@@ -1261,6 +1261,7 @@ export const uk: TranslationKeys = {
|
||||
stripIcc: "Видалити ICC (колірний профіль)",
|
||||
stripXmp: "Видалити XMP (розширювані метадані)",
|
||||
locationFound: "місцезнаходження знайдено",
|
||||
viewOnMap: "Переглянути на карті",
|
||||
locationWarning:
|
||||
"Це зображення містить Ваше точне місцезнаходження. Рекомендуємо видалити GPS-дані перед публікацією.",
|
||||
metadataSections: "Знайдено {count} розділ метаданих",
|
||||
|
||||
@@ -1261,6 +1261,7 @@ export const vi: TranslationKeys = {
|
||||
stripIcc: "Xóa ICC (cấu hình màu)",
|
||||
stripXmp: "Xóa XMP (siêu dữ liệu mở rộng)",
|
||||
locationFound: "đã tìm thấy vị trí",
|
||||
viewOnMap: "Xem trên bản đồ",
|
||||
locationWarning:
|
||||
"Hình ảnh này chứa vị trí chính xác của bạn. Hãy cân nhắc xóa dữ liệu GPS trước khi chia sẻ.",
|
||||
metadataSections: "Đã tìm thấy {count} phần siêu dữ liệu",
|
||||
|
||||
@@ -1035,6 +1035,7 @@ export const zhCN: TranslationKeys = {
|
||||
stripIcc: "移除 ICC(颜色配置文件)",
|
||||
stripXmp: "移除 XMP(扩展元数据)",
|
||||
locationFound: "已发现位置信息",
|
||||
viewOnMap: "在地图上查看",
|
||||
locationWarning: "此图片包含精确的位置信息。建议在分享前移除 GPS 数据。",
|
||||
metadataSections: "发现 {count} 个元数据部分",
|
||||
metadataSectionsPlural: "发现 {count} 个元数据部分",
|
||||
|
||||
@@ -1034,6 +1034,7 @@ export const zhTW: TranslationKeys = {
|
||||
stripIcc: "移除ICC(色彩描述檔)",
|
||||
stripXmp: "移除XMP(延伸中繼資料)",
|
||||
locationFound: "發現位置資訊",
|
||||
viewOnMap: "在地圖上查看",
|
||||
locationWarning: "此影像包含您的精確位置資訊。建議在分享前移除GPS資料。",
|
||||
metadataSections: "找到{count}個中繼資料區段",
|
||||
metadataSectionsPlural: "找到{count}個中繼資料區段",
|
||||
|
||||
Reference in New Issue
Block a user