Rust/Tauri製のWindowsアプリ「disk-insight」を公開した後、配布ZIPの中にあるexeを確認したところ、ビルド環境のパスが残っていました。GitHub Releasesから公開v1.0.0のZIPを取得して調べ直すと、Windowsユーザーディレクトリ接頭辞は319箇所。後続のv1.0.1では0件でした。
319は文字列の出現回数です。319個の独立した問題やcredential漏えいを意味しません。この記事では、公開済みartifactで何が見つかり、どう確認・修正したか、その検証範囲と限界を説明します。
この記事は元記事(Zenn)をもとに、Qiita単体でも読めるように構成しています。
何が起きたか
v1.0.0は2026年9月4日(日本時間)に公開した安定版です。9月27日の記録には、公開済みGUI exeにビルド環境由来のパスが含まれていたことと、修正候補の検証結果が残っています。v1.0.1は9月29日に公開しました。
検出対象はZIP内の disk-insight-ui.exe でした。Cargoの依存ソースを置くregistryディレクトリと、プロジェクトのソースディレクトリのパスが含まれていました。修正commitとリリースノートでは、panicの発生箇所を示す文字列に残っていたものとして記録しています。
ここで問題にしたのは、開発環境のユーザー名やローカルの配置を含む、配布には不要な情報です。資格情報の漏えいを確認したという話ではありません。また、このパス残留によって利用者のファイルが送信された、攻撃が成立した、という事実も確認していません。実際のユーザー名や絶対パスは掲載せず、検出内容の種類だけを示します。
ソース確認と配布物の確認は別
ソースコードから、ビルド設定やパスを扱う仕組みを調べて残留の可能性を検討することはできます。「ソースを見ても絶対に分からない問題」だったとは言えません。
一方、実際に公開したexeにその環境のパスが残っているかを確認するには、配布物そのものを調べる必要があります。ソースレビューだけでZIPの中身まで確認済みにはできません。
今回は公開後に見つかった事例です。「公開直前に見つけて未然に防いだ」のではなく、すでに公開した版を調査し、後続版で修正しました。
公開ZIPを改めて調べた
記事の準備時(2026年10月7日)に、GitHub Releasesからv1.0.0とv1.0.1のZIPを取得し、メモリ上でexeを読んで再確認しました。exeは実行していません。
当時の最初の発見に使ったコマンドそのものは確認できていません。次の表は、記事準備時に改めて行った確認結果です。
| 確認対象 | v1.0.0 | v1.0.1 |
|---|---|---|
| exe内のWindowsユーザーディレクトリ接頭辞の出現回数 | 319 | 0 |
| exe内の当該ビルドworkspace接頭辞の出現回数 | 6 | 0 |
これは指定したバイト列・パターンの出現回数です。319人分の情報や319個の独立した問題ではありません。
保存されていたローカルv1.0.0 ZIPは、今回取得した公開ZIPとハッシュが一致しませんでした。そのため表の旧版はローカルコピーではなく、GitHub Releasesから改めて取得したZIPです。v1.0.1は、保存済み配布ZIPと公開ZIPのSHA-256が一致しました。
ZIP内のexeを実行せずに確認する例
Python標準ライブラリで、ZIP内のexeからWindowsユーザーディレクトリ接頭辞の出現回数を調べる例です。
import re
import sys
import zipfile
with zipfile.ZipFile(sys.argv[1]) as archive:
names = [n for n in archive.namelist()
if n.endswith("disk-insight-ui.exe")]
if len(names) != 1:
raise SystemExit("Expected exactly one target exe")
data = archive.read(names[0])
count = len(re.findall(rb"[Cc]:[\/]Users[\/]", data))
print("Windows user directory prefix occurrences:", count)
保存したZIPを引数に渡します。他のアプリで使う場合は対象exe名を変更してください。自分のビルドworkspaceについては、その接頭辞も別途検索します。文字列全文を出力せず件数で確認すれば、調査ログへ実ユーザー名を複写せずに済みます。
この例が調べるのはASCIIで表現された特定のパスだけです。別OSのパス、別のエンコーディング、秘密情報、その他の付随ファイルは検査しません。検出0件は、配布物全体の安全性を証明しません。
release build scriptで修正した
修正commitでは、scripts/build-release-package.ps1のTauriビルド直前に次の設定を加えました。
if (-not $env:RUSTFLAGS) {
$cargoHome = if ($env:CARGO_HOME) {
$env:CARGO_HOME
} else {
"$env:USERPROFILE\.cargo"
}
$env:RUSTFLAGS = "--remap-path-prefix=$cargoHome=/cargo --remap-path-prefix=$projectRoot=/disk-insight"
}
npm run tauri build
$projectRootは、このscriptが既に求めているプロジェクトルートです。この部分だけを別のscriptへ貼り付けても動く例ではありません。
Cargoの配置先とプロジェクトルートを、環境に依存しないパスへ置換する設定です。Rustの--remap-path-prefixは、出力中のソースパス接頭辞を書き換えるオプションです。
実装には条件があります。すでにRUSTFLAGSが設定されている場合、その値を上書きしません。したがって、別のビルド環境でも必ずこの置換が有効になるとは言えません。独自のRUSTFLAGSを使う環境では、実際の設定と生成物を確認する必要があります。
修正後に確認したこと
9月27日の候補版の記録には、release tests、clean package build、GUI起動・version・icon、CLI表示、ZIP内hash、実ビルドパスの残留なしの確認があります。これらは当時の記録に基づく説明で、記事準備時にビルドやGUI検証を再実行したものではありません。
候補版にはその後別の修正も入り、最終的に公開したv1.0.1 ZIPのSHA-256は次の値です。
2d7fb98325df1adc2419305c7a551810543e1d8d03b460e5f8479df080e82fd7
今回取得した公開ZIPもこのhashと一致し、上記2種類の接頭辞は検出0件でした。v1.0.1の公開リリースノートにも、exeのpanic-location文字列からビルド環境のソースパスを除いたことが記載されています。
ハッシュ一致は「同じZIPを確認した」ための識別で、安全性の証明ではありません。旧版の公開ZIPも今回取得できました。新版で修正されたことと、旧版の情報が回収されたことは別です。
再発防止として残ったこと
修正は一回限りのコマンドではなく、release packageを作るscriptに加えました。一方、現在のscriptにバイナリ中のパス残留を自動検査して失敗させる処理はありません。
実際のビルド設定と公開する最終ZIPの確認は分けて扱う必要があります。設定が入っていることだけで、生成物の確認を省かないというのがこの事例から得たlessonです。
配布前の短いchecklist
- ソースだけでなく、公開する最終ZIPとその中のexeを確認したか。
- ビルド環境のユーザーディレクトリ・workspace接頭辞を検索したか。
- 出力に実ユーザー名・絶対パスを不用意に複写していないか。
- ビルド設定を修正した後、生成し直した配布物で確認したか。
- 確認したZIPと公開対象を、hash・versionで識別できるか。
- 「指定パターンの検出0件」と「全体が安全」を区別しているか。
- 公開済み旧版がある場合、その扱いを新しい版の修正とは別に判断したか。
今回の確認と改善はここまでです。より体系的に公開前レビューを進めたい方には、公開前レビューのZenn Bookも用意しています。ソース、Git履歴、配布物まで含めた公開前レビュー全体と、最終の人間判断を体系化しています。