mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: release QA hardening across processing, media, security, and CI gates (#649)
A release-readiness QA pass over the whole product. The commits split into defects a user would hit and gates that were reporting green while measuring nothing. ## Fixes that change behaviour Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so request.ip came from a client-set header and a forged X-Forwarded-For got past the login limiter. The default is now a private-network trust list. A transient Postgres outage stranded in-flight jobs, leaving finished output on disk with no row pointing at it. A reconciler now resolves those rows and adopts the bytes rather than dropping the work. A Redis connection that moved to a new address wedged every read-blocked consumer, so completions stopped signalling while health still answered 200. Socket timeouts plus subscriber pings recover it. Installing more than one AI bundle left the shared venv multi-versioned and silently broke three tools. The installer now reconciles distributions to one version each. Converting an image to JXL at quality 1 through 4 returned a 500, because libjxl 0.7 rejects the distance those values compute. The quality is floored at what the encoder honours. A missing ffmpeg was also reported to the user as a corrupt upload; it now says the engine is unavailable. RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at 0.22.2, and the release scan was split so it can fail on an unfixed critical instead of hiding it behind ignore-unfixed. ## Gates that could not fail Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs build; coverage discarded its whole report on any failing test; the lint gate skipped root tests, scripts, and two workspaces; and several generated matrices counted a host missing ffmpeg as a passing tool. Each now measures what it claims. Full evidence and the outstanding release items are tracked locally and are not part of this branch.
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "SnapOtter 的安全強化指南。容器安全、網路隔離、Docker secrets、Kubernetes 部署與合規產出物。"
|
||||
i18n_source_hash: 986f7658430c
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: c7ac676acc59
|
||||
i18n_source_hash: 9ff337fa0417
|
||||
i18n_provenance: machine
|
||||
i18n_output_hash: 0ec192d6b1ef
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# 安全與強化 {#security-hardening}
|
||||
@@ -11,133 +12,42 @@ SnapOtter 完全在你的基礎架構上處理檔案。它預設會傳送匿名
|
||||
|
||||
容器以專屬的非 root 使用者(`snapotter`)執行,並卸除除最低必需集之外的所有 Linux capabilities。完整的漏洞揭露政策與安全架構,請參閱 GitHub 上的 [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md)。
|
||||
|
||||
## 容器強化 {#container-hardening}
|
||||
## 容器硬化 {#container-hardening}
|
||||
|
||||
[預設的 docker-compose.yml](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) 包含正式環境安全強化。以下逐一說明每個選項及其重要性:
|
||||
規範的 [CPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) 和 [GPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose-gpu.yml) Compose 檔案是事實來源。不要將縮寫範例複製到生產中;從您驗證的發布標籤部署檔案。
|
||||
|
||||
```yaml
|
||||
services:
|
||||
SnapOtter:
|
||||
image: snapotter/snapotter:latest
|
||||
ports:
|
||||
# Bind to localhost only for internet-facing deployments:
|
||||
- "127.0.0.1:1349:1349"
|
||||
volumes:
|
||||
- SnapOtter-data:/data
|
||||
- SnapOtter-workspace:/tmp/workspace
|
||||
environment:
|
||||
- AUTH_ENABLED=true
|
||||
- DEFAULT_PASSWORD=change-me-immediately
|
||||
- RATE_LIMIT_PER_MIN=1000
|
||||
- DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter
|
||||
- REDIS_URL=redis://redis:6379
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
兩個堆疊都應用以下控制:
|
||||
|
||||
# --- Resource limits ---
|
||||
mem_limit: 6g # Prevents runaway memory from crashing the host
|
||||
memswap_limit: 6g # No swap - fail fast instead of degrading the host
|
||||
cpus: 4 # Cap CPU usage to 4 cores
|
||||
pids_limit: 512 # Prevents fork bombs
|
||||
- 記憶體、交換、CPU 和 PID 限制包含失控的本機處理。
|
||||
- 每個服務都會放棄所有 Linux 功能。該應用程式僅添加回 `CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILL` 來實現卷所有權、單向 `gosu` 身份刪除以及優雅的信號轉發。 PostgreSQL 和 Redis 僅接收其官方入口點所需的子集。
|
||||
- `security_opt: [no-new-privileges:true]` 防止應用程式、PostgreSQL 和 Redis 容器中的進程獲得額外權限。這仍然與 `gosu` 相容:入口點以 root 身份開始,準備卷,並且僅下降到專用的 `snapotter` 用戶。
|
||||
- PostgreSQL 和 Redis 影像輸入由摘要固定。應用程式同樣應該固定到經過驗證的發布標籤或摘要,而不是 `latest`。
|
||||
- 健康檢查、有界 JSON 日誌輪替、持久的 Redis AOF 和重啟策略在規範文件中集中定義。
|
||||
|
||||
# --- Capability restrictions ---
|
||||
cap_drop:
|
||||
- ALL # Drop ALL Linux capabilities first
|
||||
cap_add:
|
||||
- CHOWN # Needed for volume permission setup
|
||||
- SETUID # Needed for gosu privilege drop (root -> snapotter)
|
||||
- SETGID # Needed for gosu privilege drop
|
||||
- DAC_OVERRIDE # Needed for volume permission setup
|
||||
- FOWNER # Needed for volume permission setup
|
||||
對於面向網際網路的部署,將連接埠 1349 綁定到環回並在維護的反向代理處終止 TLS。產生唯一的 PostgreSQL 和 Redis 憑證,將機密儲存在受保護的檔案或機密管理器中,並立即變更初始管理員密碼。
|
||||
|
||||
# --- Logging ---
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "50m" # Rotate logs at 50 MB
|
||||
max-file: "5" # Keep 5 rotated log files
|
||||
### 為什麼 `read_only` 沒有設定 {#why-read-only-is-not-set}
|
||||
|
||||
# --- Health check ---
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-sf", "--max-time", "5", "http://localhost:1349/api/v1/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 60s
|
||||
retries: 3
|
||||
未設定 `read_only: true`,因為 PUID/PGID 重新映射在啟動時寫入 `/etc/passwd` 和 `/etc/group`。如果您使用 Docker 的 `--user` 標誌或 Kubernetes `runAsUser` 而不是 PUID/PGID,則可以安全地啟用只讀根檔案系統。
|
||||
|
||||
shm_size: "2gb" # Required for Python ML shared memory
|
||||
restart: unless-stopped
|
||||
## 網路隔離{#network-isolation}
|
||||
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
environment:
|
||||
POSTGRES_USER: snapotter
|
||||
POSTGRES_PASSWORD: snapotter
|
||||
POSTGRES_DB: snapotter
|
||||
volumes:
|
||||
- SnapOtter-pgdata:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 15s
|
||||
文件處理是本地的,但預設安裝**不是無出口系統**。啟用遙測功能時,匿名產品分析使用 PostHog,崩潰報告使用 Sentry。設定 `SNAPOTTER_TELEMETRY=0`(或在「設定」>「系統」>「隱私權」下停用分析)以關閉兩者。 SnapOtter 絕不會在這些事件中包含上傳的檔案、檔案名稱、OCR 輸出、文件文字或其他文件內容。
|
||||
|
||||
redis:
|
||||
image: redis:8-alpine
|
||||
command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- SnapOtter-redisdata:/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
SnapOtter-data:
|
||||
SnapOtter-workspace:
|
||||
SnapOtter-pgdata:
|
||||
SnapOtter-redisdata:
|
||||
```
|
||||
|
||||
### 為何不設定 `no-new-privileges` {#why-no-new-privileges-is-not-set}
|
||||
|
||||
`security_opt: [no-new-privileges:true]` 是刻意省略的。進入點以 root 啟動以修正磁碟區擁有權,然後透過 [gosu](https://github.com/tianon/gosu) 降權至 `snapotter` 使用者,這需要 setuid。一旦降權完成,行程即以 `snapotter` 執行,並移除除上述五項之外的所有 capabilities。
|
||||
|
||||
如果你使用 Kubernetes 或 Docker 的 `--user` 旗標直接以非 root 執行(繞過 gosu),則 `no-new-privileges` 可安全啟用。
|
||||
|
||||
### 為何不設定 `read_only` {#why-read-only-is-not-set}
|
||||
|
||||
`read_only: true` 未設定,因為 PUID/PGID 重新對應會在啟動時寫入 `/etc/passwd` 和 `/etc/group`。如果你使用 Docker 的 `--user` 旗標或 Kubernetes `runAsUser` 而非 PUID/PGID,則可安全啟用唯讀根檔案系統。
|
||||
|
||||
## 網路隔離 {#network-isolation}
|
||||
|
||||
正常運作期間,容器不會建立**任何對外網路連線**。所有檔案處理都使用內建函式庫在本機完成。
|
||||
|
||||
```
|
||||
Browser --> Reverse Proxy (TLS) --> SnapOtter container --> (nothing)
|
||||
```
|
||||
|
||||
唯一的例外是 **AI 模型下載**:當使用者透過 UI 安裝 AI 功能套件組時,容器會從 Hugging Face 下載預先建置的套件組封存檔,外加一些來自 GitHub Releases、Google Storage 和 PyPI 的個別模型檔案。這些下載每個套件組只發生一次,並儲存在 `/data` 磁碟區中。
|
||||
其他出站流量是功能驅動的:AI 捆綁包/模型安裝下載簽名發布輸入; URL導入獲取用戶請求的公共URL;並明確配置的 OIDC、SAML、OpenTelemetry、webhooks、S3 兼容存儲或類似集成會聯繫管理員選擇的目標。執行階段模型下載預設為停用。只有在明確選擇啟用自動備援下載時,才設定 `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1`。[離線捆綁導入](/zh-TW/guide/deployment) 可以在沒有運行時模型出口的情況下提供 AI 功能。
|
||||
|
||||
**防火牆建議:**
|
||||
|
||||
| 情境 | 對外規則 |
|
||||
|設想|出站規則|
|
||||
|---|---|
|
||||
| 氣隙隔離(無 AI) | 封鎖容器的所有對外流量 |
|
||||
| 需要 AI 套件組 | 安裝期間允許對 `huggingface.co`、`*.xethub.hf.co`、`cdn-lfs.huggingface.co`、`github.com`、`objects.githubusercontent.com`、`storage.googleapis.com`、`pypi.org`、`files.pythonhosted.org` 的 HTTPS,之後封鎖 |
|
||||
| AI 安裝後 | 封鎖所有對外流量 — 模型已快取於本機 |
|
||||
|氣隙|設定`SNAPOTTER_TELEMETRY=0`和`SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0`,使用離線AI捆綁導入,停用URL導入和外部集成,然後阻止出口|
|
||||
|預設遙測|允許瀏覽器/網頁日誌列出的 PostHog 和 Sentry 端點;如果策略不允許,則停用遙測|
|
||||
|需要 AI 捆綁包|安裝過程中,允許HTTPS到`huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org`;然後阻止這些主機|
|
||||
|外部集成|僅允許管理員準確配置的 OIDC/SAML/OTLP/webhook/物件儲存目標|
|
||||
|
||||
套件組封存檔由 Hugging Face 的 Xet 儲存提供,它透過 `*.xethub.hf.co` 端點平行傳輸,正是這讓數 GB 的套件組下載得以快速完成。如果你的防火牆允許 `huggingface.co` 但封鎖 `*.xethub.hf.co`,安裝仍會成功,但會回退至較慢的單串流下載,因此請將 Xet 主機加入允許清單以維持在快速路徑上。完全離線的安裝可略過這一切,改用 [離線套件組匯入](/zh-TW/guide/deployment)。
|
||||
捆綁包檔案由 Hugging Face 的 Xet 儲存空間提供,該儲存透過 `*.xethub.hf.co` 端點並行傳輸,這使得多 GB 捆綁包下載速度更快。如果您的防火牆允許 `huggingface.co` 但阻止 `*.xethub.hf.co`,安裝仍然會成功,但會回退到較慢的單流下載,因此將 Xet 主機列入白名單以保持快速路徑。完全離線安裝可以跳過所有這些並使用[離線捆綁導入](/zh-TW/guide/deployment)。
|
||||
|
||||
關於反向代理設定(Nginx、Traefik、Caddy、Cloudflare Tunnels),請參閱 [部署指南](/zh-TW/guide/deployment#reverse-proxy)。
|
||||
有關反向代理配置(Nginx、Traefik、Caddy、Cloudflare Tunnels),請參閱[部署指南](/zh-TW/guide/deployment#reverse-proxy)。
|
||||
|
||||
## Docker Secrets {#docker-secrets}
|
||||
|
||||
@@ -255,85 +165,103 @@ spec:
|
||||
|
||||
關於資源規模,請參閱 [硬體需求](/zh-TW/guide/deployment#hardware-requirements)。
|
||||
|
||||
## 備份與復原 {#backup-and-recovery}
|
||||
## 備份與還原 {#backup-and-recovery}
|
||||
|
||||
持久化狀態分散於兩個磁碟區:
|
||||
生產 Compose 堆疊定義了四個磁碟區。在進行協調備份之前停止入口並讓活動作業完成,以便 PostgreSQL、Redis 和檔案狀態描述相同的時間點。
|
||||
|
||||
| 磁碟區 | 內容 | 是否關鍵? |
|
||||
|體積|內容|復健治療|
|
||||
|---|---|---|
|
||||
| `SnapOtter-pgdata` | PostgreSQL 資料庫(使用者、設定、管線、作業、稽核記錄) | 是 |
|
||||
| `/data`(app 磁碟區) | 使用者上傳的檔案、AI 模型、Python venv | 部分(見下文) |
|
||||
|`SnapOtter-pgdata`|PostgreSQL 使用者、設定、管道、作業、文件元資料和審核日誌|批判的;使用快速故障邏輯轉儲進行可移植恢復|
|
||||
|`SnapOtter-data`|保存的庫物件、日誌和 AI 狀態 (`/data/files, /data/logs, /data/ai, /data/ai/venv`)|備份整個磁碟區;為了節省空間,故意省略所有 AI 狀態並重新安裝其捆綁包|
|
||||
|`SnapOtter-redisdata`|Redis AOF 用於持久的 BullMQ 隊列狀態|暫停應用程式並強制`SAVE`後備份;需要準確地恢復排隊的工作|
|
||||
|`SnapOtter-workspace`|暫存物件儲存鍵 (`/tmp/workspace/uploads, /tmp/workspace/outputs`)|所有作業清空或取消後不要進行備份;當工作處於活動狀態時切勿丟棄它|
|
||||
|
||||
在 `/data` 磁碟區中:
|
||||
Compose 通常在磁碟區名稱前加上項目名稱作為前綴。從已安裝的容器中解析真實的來源卷,而不是假設顯示名稱(例如 `SnapOtter-data`)是 Docker 磁碟區名稱。
|
||||
|
||||
| 路徑 | 內容 | 是否關鍵? |
|
||||
|---|---|---|
|
||||
| `/data/uploads/`、`/data/outputs/` | 使用者檔案與處理結果 | 是 |
|
||||
| `/data/ai/` | 已下載的 AI 模型檔案 | 否(可重新下載) |
|
||||
| `/data/venv/` | Python 虛擬環境 | 否(啟動時重建) |
|
||||
### 資料庫備份{#database-backup}
|
||||
|
||||
### 資料庫備份 {#database-backup}
|
||||
|
||||
在堆疊執行期間,使用 `pg_dump` 備份資料庫:
|
||||
使用 PostgreSQL 的自訂存檔格式並在將備份視為完整之前驗證存檔:
|
||||
|
||||
```bash
|
||||
# Dump the database
|
||||
docker exec SnapOtter-postgres pg_dump -U snapotter snapotter > backup.sql
|
||||
docker exec SnapOtter-postgres \
|
||||
pg_dump --format=custom --no-owner -U snapotter snapotter > snapotter.dump
|
||||
test -s snapotter.dump
|
||||
docker exec -i SnapOtter-postgres pg_restore --list < snapotter.dump >/dev/null
|
||||
|
||||
# Restore into a fresh database
|
||||
cat backup.sql | docker exec -i SnapOtter-postgres psql -U snapotter snapotter
|
||||
# Restore only into a fresh/disposable target first; any SQL error fails the command.
|
||||
docker exec -i SnapOtter-postgres \
|
||||
pg_restore --exit-on-error --clean --if-exists --no-owner \
|
||||
-U snapotter -d snapotter < snapotter.dump
|
||||
```
|
||||
|
||||
或者,停止堆疊並對 `SnapOtter-pgdata` 磁碟區建立快照:
|
||||
透過將每個備份還原到隔離堆疊、檢查資料庫記錄和檔案校驗和以及啟動應用程式來測試每個備份。儲存庫的 `tests/qa/backup-restore-drill.sh` 會針對明確 `QA_IMAGE` 自動執行該發佈閘。
|
||||
|
||||
如果您的平台採用崩潰一致的磁碟區快照,請先停止整個堆疊,並將所有關鍵磁碟區快照為一組。來自正在運行的容器的原始 PostgreSQL 資料目錄副本不是受支援的邏輯備份。
|
||||
|
||||
### 檔案與佇列備份 {#file-and-queue-backup}
|
||||
|
||||
在捕獲文件和隊列卷之前暫停應用程式。使用 `docker inspect` 解析實際磁碟區名稱,強制 Redis 保留其目前狀態,並在保留所有權和權限的情況下進行歸檔:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
docker run --rm -v SnapOtter-pgdata:/data -v $(pwd)/backup:/backup \
|
||||
alpine tar czf /backup/snapotter-pgdata.tar.gz -C /data .
|
||||
docker stop SnapOtter
|
||||
docker exec SnapOtter-redis redis-cli -a "$REDIS_PASSWORD" --no-auth-warning SAVE
|
||||
docker stop SnapOtter-redis
|
||||
|
||||
DATA_VOLUME="$(docker inspect SnapOtter --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
|
||||
REDIS_VOLUME="$(docker inspect SnapOtter-redis --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
|
||||
|
||||
install -d -m 700 backup
|
||||
docker run --rm -v "$DATA_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
|
||||
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-data.tar.gz -C /source .
|
||||
docker run --rm -v "$REDIS_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
|
||||
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-redis.tar.gz -C /source .
|
||||
sha256sum backup/snapotter-*.tar.gz > backup/SHA256SUMS
|
||||
```
|
||||
|
||||
### 使用者檔案備份 {#user-files-backup}
|
||||
應用前重啟Redis。如果您有意排除 `/data/ai`,請刪除整個 AI 子樹,而不是保留不含模型或虛擬環境的 `installed.json` 記錄。保持備份檔案加密、存取受控,並與執行 SnapOtter 的主機分開。
|
||||
|
||||
```bash
|
||||
# Snapshot the app data volume (excluding re-downloadable AI models)
|
||||
docker run --rm -v SnapOtter-data:/data -v $(pwd)/backup:/backup \
|
||||
alpine tar czf /backup/snapotter-files.tar.gz \
|
||||
--exclude='ai' --exclude='venv' -C /data .
|
||||
```
|
||||
## 合規工件 {#compliance-artifacts}
|
||||
|
||||
所有套件組的 AI 模型總計最多約 24 GB。由於它們可重新下載,請將 `/data/ai/` 和 `/data/venv/` 排除在備份之外以節省空間。只有資料庫與使用者檔案是關鍵的。
|
||||
每個 SnapOtter 版本都包含以下安全工件:
|
||||
|
||||
## 合規產出物 {#compliance-artifacts}
|
||||
|
||||
每次 SnapOtter 發行都包含下列安全產出物:
|
||||
|
||||
| 產出物 | 格式 | 取得位置 |
|
||||
| 人工製品 | 格式 | 在哪裡可以找到它 |
|
||||
|---|---|---|
|
||||
| SBOM(CycloneDX) | JSON | [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases) 資產:`snapotter-v{version}-sbom.cdx.json` |
|
||||
| SBOM(SPDX) | JSON | [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases) 資產:`snapotter-v{version}-sbom.spdx.json` |
|
||||
| 漏洞掃描 | Trivy JSON | [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases) 資產:`snapotter-v{version}-trivy.json` |
|
||||
| 漏洞掃描 | SARIF | [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security) 分頁 |
|
||||
| 靜態分析 | CodeQL(JS/TS + Python) | [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security) 分頁,每週 + 每次 PR 執行 |
|
||||
| 相依性審查 | GitHub 原生 | 每次 PR 檢查,在新增高嚴重性項目時失敗 |
|
||||
| Python 相依性稽核 | pip-audit | 每次推送的 CI 執行記錄 |
|
||||
| 釋放主體綁定 | 規範 JSON + GitHub 證明 | [GitHub發布](https://github.com/snapotter-hq/SnapOtter/releases)資產:`snapotter-v{version}-release-subjects.json` |
|
||||
| 歸檔 SBOM | CycloneDX 和 SPDX JSON | 釋放資產:`snapotter-v{version}-archive-linux-{arch}-sbom.{cdx,spdx}.json` |
|
||||
| 圖片 SBOM | CycloneDX 和 SPDX JSON | 釋放資產:`snapotter-v{version}-image-linux-{arch}-sbom.{cdx,spdx}.json` |
|
||||
| 漏洞掃描 | Trivy JSON | 發布具有匹配 `archive-linux-{arch}` 或 `image-linux-{arch}` 前綴的資產 |
|
||||
| 漏洞掃描 | SARIF | [GitHub 安全性](https://github.com/snapotter-hq/SnapOtter/security) 選項卡 |
|
||||
| 靜態分析 | CodeQL (JS/TS + Python) | [GitHub 安全](https://github.com/snapotter-hq/SnapOtter/security) 選項卡,每週運行 + 每個 PR |
|
||||
| 依賴性審查 | GitHub 本機 | 按 PR 檢查,高嚴重性添加失敗 |
|
||||
| Python依賴審計 | pip-audit | CI 在每次推送時執行日誌 |
|
||||
| 安全政策 | Markdown | 儲存庫中的 [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) |
|
||||
| 相依性更新 | Dependabot | 針對 npm、pip、Docker、Actions 的每週自動化 PR |
|
||||
| 依賴項更新 | Dependabot | npm、pip、Docker、Actions 的自動每週 PR |
|
||||
|
||||
**執行你自己的掃描:**
|
||||
**執行您自己的掃描:**
|
||||
|
||||
從發行中下載 SBOM,並用你偏好的工具掃描它:
|
||||
下載發布主題清單並驗證它是否已由發布工作流程證明:
|
||||
|
||||
```bash
|
||||
gh attestation verify snapotter-v2.1.0-release-subjects.json \
|
||||
--repo snapotter-hq/SnapOtter \
|
||||
--signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml
|
||||
```
|
||||
|
||||
清單中分別記錄了 `releaseTag`、`releaseCommit` 和 `workflowTriggerCommit`。驗證 `releaseCommit` 是否為從不可變標記中剝離的提交,然後驗證存檔、映像、SBOM 的 SHA-256 摘要,或根據 `subjects` 中的條目進行掃描。這種區別是有意為之的:簽出新建立的發布提交不會更改工作流程的 OIDC 憑證中的提交標識。
|
||||
|
||||
您也可以直接掃描下載的 SBOM 或影像:
|
||||
|
||||
```bash
|
||||
# Scan with Grype using the CycloneDX SBOM
|
||||
grype sbom:snapotter-v1.17.2-sbom.cdx.json
|
||||
grype sbom:snapotter-v2.1.0-image-linux-amd64-sbom.cdx.json
|
||||
|
||||
# Scan with Trivy using the SPDX SBOM
|
||||
trivy sbom snapotter-v1.17.2-sbom.spdx.json
|
||||
trivy sbom snapotter-v2.1.0-image-linux-amd64-sbom.spdx.json
|
||||
|
||||
# Scan the Docker image directly
|
||||
trivy image snapotter/snapotter:1.17.2
|
||||
trivy image snapotter/snapotter:2.1.0
|
||||
```
|
||||
|
||||
::: info
|
||||
SBOM 與漏洞掃描反映的是該發行所發布的確切映像檔。部署後安裝的 AI 模型套件組不包含在 SBOM 中,因為它們是在執行期間下載的。
|
||||
::: info
|
||||
影像 SBOMs 和掃描反映了針對該版本發布的具體架構特定影像。檔案 SBOMs 和掃描分別描述預建存檔。部署後安裝的 AI 模型包不包含在這些 SBOMs 中,因為它們是在執行時下載的。
|
||||
:::
|
||||
|
||||
Reference in New Issue
Block a user