本卡屬 FR-195(Agent 資安修正),第 1 批 F8。修 FR-186 掃描的 P3 F1、P3 F2、H5 F1(皆 3/3 票),嚴重度:中。
三處讓同一台機器上的普通帳號讀得到(甚至掉包)Agent 的資料。①原生安裝(不用 docker)的 systemd 服務沒設檔案權限遮罩,Agent 寫出來的證據檔、本機資料庫是「人人可讀」。②資料目錄用預設權限建立,人人可進。③「Office 證據轉成的 PDF」與「準備送回主系統的檔案暫存」放在公共 /tmp 底下的固定路徑,本機任何帳號都能搶先建好這個資料夾,偷看或掉包證據 PDF,甚至用捷徑檔讓 root 寫任意檔。
b8b1158 重核過,仍然有效)scripts/build/systemd/guidant-agent.service:23-37 [Service] 段沒有 UMask=;:27 User=root。config/config.py:62-68 database_url() 用 os.makedirs(os.path.dirname(db_path), exist_ok=True) 建資料目錄(預設權限 0755);scripts/install/install.sh:798-801 mkdir -p "$VERSIONS_DIR" "$DATA_DIR" "$CERT_DIR" "$CONF_DIR" "${DATA_DIR}/vulndb",之後只對 $CERT_DIR 與 $CONF_DIR 做 chmod 700,$DATA_DIR 沒有。config/config.py:71-72 pdf_cache_dir() 預設 /tmp/agent-pdf-cache;scripts/install/install.sh:1098 寫進設定檔的是 PDF_CACHE_DIR=/tmp/guidant-agent-pdf-cache。使用處:guidant_agent/platform/bootstrap.py:42-43(os.makedirs(..., exist_ok=True),不檢查擁有者)、guidant_agent/files/app/blob_ops.py:148-150 outbound_dir()(在 pdf_cache_dir() 底下建 outbound)。scripts/install/install.sh、原生 systemd unit、與 Python。不碰 deploy/install.sh 與 scripts/install/lib/:FR-193(rootless Docker)大部分已合回主線 b8b1158,但它還有幾張卡會繼續改那些檔。首腦 2026-11-11 核過:scripts/install/install.sh 的 :798-801、:1098、:1388 與 unit 的 :23-37 在最新主線上都沒位移。compose 版(容器內)的 /tmp 是容器自己的,不是主機公共區,不在這條範圍。scripts/build/systemd/guidant-agent.service
scripts/install/install.sh
config/config.py
guidant_agent/platform/bootstrap.py
guidant_agent/files/app/blob_ops.py
test/test_platform_bootstrap.py
guidant-agent.service [Service] 段加一行 UMask=0077,上面一行註解寫為什麼(「證據檔與本機資料庫只給服務帳號讀;不設就是 systemd 預設 0022=人人可讀」)。scripts/install/install.sh:801 那行 chmod 700 的對象加上 "$DATA_DIR"。🔴 既有客戶升級時資料目錄已經存在而且是 0755:同一段在升級流程也會跑到嗎?開工讀 install_files() 的呼叫端(do_upgrade 在 :1388)確認;升級不會經過的話,在 do_upgrade 裡補同一行 chmod 700 "$DATA_DIR"。config/config.py database_url():os.makedirs(...) 後面補 os.chmod(dir, 0o700),包在 try/except OSError 裡(容器內掛載的目錄可能改不了權限,改不了就 logger.warning 一行、不擋啟動)。pdf_cache_dir() 的預設值改成資料目錄底下:os.path.join(data_dir(), "pdf-cache")(data_dir() 在同檔 :44)。PDF_CACHE_DIR 環境變數照舊可以覆寫。scripts/install/install.sh:1098 那行改成 PDF_CACHE_DIR=${DATA_DIR}/pdf-cache(開工確認該段變數名,檔內資料目錄變數是 DATA_DIR)。bootstrap.py:42-43 與 blob_ops.py:148-150 建目錄的地方:建完後檢查並收緊——os.makedirs(path, mode=0o700, exist_ok=True),接著 st = os.lstat(path),如果是捷徑檔、或擁有者不是目前行程的 uid(st.st_uid != os.geteuid()),就 raise RuntimeError(訊息講哪個路徑、為什麼拒絕);通過就 os.chmod(path, 0o700)。兩處用同一支小函式:放 guidant_agent/shared/secure_file.py(已有 write_atomic,53 行),新增 ensure_private_dir(path: str) -> str。PDF_CACHE_DIR 還是舊的 /tmp/guidant-agent-pdf-cache(設定檔升級時不會重寫):這就是為什麼第三點的「檢查擁有者+收緊權限」一定要做——舊路徑照樣安全。不要去寫遷移設定檔的程式。sonarqube.py:153 有一句註解提到 PDF_CACHE_DIR=/tmp/pdf-cache,那是說明文字,不要為了改註解去動那支超標檔。原生安裝的新檔案權限變成只有服務帳號可讀;資料目錄變成 0700。同一台機器上若有客戶自己的腳本用別的帳號去讀 Agent 資料目錄,會讀不到(這本來就不該讀得到)。