ビルドツールチェーン監査
autotools ビルドチェーン、GitHub Actions CI、 ベンダーされたソースの来歴、インストール経路のリスクを レビュー。何が固定済みで、何が署名済みで、何が未対応か 正直に書きます。
1. ビルドツールチェーンリスク
lha ソースは GNU autotools を使用。リリースバイナリは
alpine:3.20 Docker コンテナ内でビルドされ
(ホストは GitHub Actions Linux runner)、その後 macOS /
Windows runner でパッケージ。ツールチェーン:
autoconf 2.72、automake 1.16.5、
libtool 2.5.4、gcc 13.2.1、
musl 1.2.5(監査時点の alpine:3.20)。
bash scripts/build.sh
( cd "$SRC" && autoreconf -is ) # m4 マクロ展開
( cd "$BUILD_DIR" && "$SRC/configure" ) # 生成された shell スクリプト
( cd "$BUILD_DIR" && make -j"$JOBS" ) # cc + ld
この 3 ステップはそれぞれビルドホスト上で任意のコードを 実行します。以下、リスク別の監査。
R1. autoreconf -is — M4 マクロ展開
何をするか:configure.ac を
autoconf + automake + libtool + autoheader + autopoint で
展開して configure を生成する。M4 はマクロ
言語で、shell を呼んだり、プログラムを走らせたり、
ファイルを読み込める。
リスク:upstream/lha/ の
configure.ac や .m4 ファイルに
バックドアがあれば、ビルド時に任意のコマンドが走る。
ベンダーされたソースはバイト単位で upstream
jca02266/lha@86094cb と一致するため、これは
upstream 信頼の問題で、ljh-sh 特有ではない。
緩和は §3 を参照。
現在の緩和:lha 1.14i の M4 入力ファイルは小さく、
2003 年から実質的に変わっていない。configure.ac
全体を読み、不審な AC_RUN_IFELSE /
AC_TRY_RUN / AC_LINK_IFELSE 本体は
ない(5 軸ソース監査 の一部)。
R2. 生成された configure スクリプト
何をするか:~10,000 行の shell でツールチェーンを
検出し、AC_TRY_RUN 試験(実 fork +
exec + wait で小さな試験
プログラム)を動かし、Makefile を出力する。
リスク:侵害された autoconf は任意のコマンドを
走らせる configure を生成する可能性がある。
生成された configure(~10,000 行)は直接
監査しない。元になる configure.ac
(~300 行)にあるバックドアは見えるが、autoconf の
M4 マクロにあるバックドアは再生成まで隠れる。
現在の緩和:alpine:3.20(イメージ)に
固定している。リスク:イメージは
フローティングタグで digest ではない。Alpine 3.20
が強制リビルドされる(セキュリティ事故)と CI が静かに
取り込む。1 行修正:workflow で
docker: image: alpine@sha256:<digest>、
digest は既知良好な docker pull alpine:3.20
から取得。
R3. make + cc
何をするか:gcc -g -O2 -DHAVE_CONFIG_H ... で
C ソースをコンパイルし、リンクする。
リスク:C ビルドの標準。-g -O2 は安全;
ビルド時の懸念は Makefile が shell を呼ぶ箇所のみ。
生成された Makefile を shell injection パターン
(変数のクォート漏れ、glob のクォート漏れ等)で確認、
該当なし。
現在の緩和:C は標準 autoconf 検出フラグでコンパイル。
LTO なし、PGO なし、サードパーティコンパイララッパなし。
ビルドは bit-for-bit 再現可能(別ホストで再実行、ビルド
タイムスタンプが __TIME__ に焼かれる点を除き
一致——lha の __TIME__ はバージョン文字列の
出力にのみ使用、ランタイム挙動に影響なし)。
R4. libiconv / libz / libSystem 依存
static-musl ビルドは C ライブラリのみリンク。macOS ビルドは
システム libiconv を使用(macOS では
libSystem に含まれる)。Linux gnu ビルドは
apt パッケージの libz + libiconv を
リンク。なし:libcurl、
libssl、libxml2、
libsystemd。
リスク:低 — すべてパッケージマネージャ由来;唯一の ソフトウェア定義入力は workflow で固定されたパッケージ名。
2. CI リスク
lha リリースパイプライン:
on:
push:
tags: ['v*']
workflow_dispatch:
jobs:
build:
steps:
- uses: actions/checkout@v4 # ❌ 未固定
- uses: msys2/setup-msys2@v2 # ❌ 未固定
- run: bash scripts/build.sh # ✅ リポジトリ内
- run: docker run alpine:3.20 ... # ❌ 未固定
- uses: softprops/action-gh-release@v2 # ❌ 未固定
R5. 未固定のサードパーティ actions
リスク:高。actions/checkout@v4、
msys2/setup-msys2@v2、
softprops/action-gh-release@v2 — すべて
フローティングタグ。書き込み権限のある者(アカウント
乗っ取り含む)は CI 環境で任意のコードを送り込み、
secrets.GITHUB_TOKEN と release 書き込み権限を
得られる。
1 行修正:各 uses: を commit SHA に
固定。GitHub の
セキュリティ
ハードニングガイド も推奨。
現在の緩和:なし。リスクは上流リポジトリの書き込み 権限者数(小)と、怪しいリリースに気付く確率(低 — artifacts 変化に気付く必要あり)で抑止されているのみ。
R6. 未固定の alpine:3.20
リスク:中。上記と同じフローティングタグ。digest に 固定する。
R7. CI runner の secrets / 書き込み権限
リスク:ljh-sh/lha への push 権限を持つ
者なら誰でも workflow を改変できる(メンテナの GitHub
身元で実行、secrets.GITHUB_TOKEN + release
書き込み権限付き)。
現在の緩和:ブランチ保護ルール(PR review 必須)— 確認
済み。Release は v* tag push 時のみ — メンテナ
による署名タグ push が必要。PR 作成者は単独で release を
トリガできない。
1 行改善:CODEOWNERS ファイルを追加し、workflow 改変は特定メンテナの review を必須にする。
3. ベンダーされたソースの来歴
upstream/lha/ はバイト単位で
jca02266/lha@86094cb
(LHa for UNIX 1.14i-ac20220213)と一致。Vendoring は
git subtree add で
https://github.com/jca02266/lha.git master 86094cb
から実施;subtree 履歴は upstream/lha/.git
と squash-merge コミット内に保存。
R9. Subtree vendoring は再現可能
ベンダーされたコミット(履歴の merge commit
4ccece7...)は正確な upstream コミットを参照。
いつでも検証可能:
git -C upstream/lha log -1 --format='%H %s'
git ls-remote https://github.com/jca02266/lha.git 86094cb
SHA が一致すれば、ベンダーされたソースは upstream の bit-for-bit。CI に毎回 release 時にこれを実行するステップを 追加すべき(release job に 1 行)。
R10. ベンダー信頼点は upstream
jca02266/lha の履歴が後に改変されても
(force-push、rebase、削除)、ベンダーされたコピーは
変わらない— スナップショットがある。ただし
我々のリポジトリが release 前に侵害されると、
攻撃者は upstream スナップショットと同じに見える
バックドア版でベンダーソースを差し替え可能。
1 行修正:CI で checkout 後に
upstream/lha/.git が期待 86094cb SHA
に一致するか検証。不一致ならビルド失敗。これが
「我々の lha は本当に upstream 由来」チェック。
4. 配布 / インストール経路リスク
ユーザは以下のいずれかでバイナリを取得:
x eget use lha—ljh-sh/lhaのreleases/latestからダウンロード。x i lha— x-cmd-install のsrc/common/lha.yml内のbrew install lha/apt install lhasaコマンドへルーティング。- GitHub Releases ページから直接ダウンロード。
R11. Release 未署名
リスク:中。Release artifacts(tarball、zip)は何も 署名していない。GitHub の commit 署名は commit を保護するが release artifact は保護しない。GPG 署名なし、cosign 署名 なし、sigstore envelope なし。
1 行修正:release workflow に
cosign sign-blob ステップ追加。GitHub が
OIDC ベース keyless 署名を提供(cosign
sign-blob)。
R12. x eget use lha は GitHub 提供の SHA256SUMS を検証するが、checksum ファイル自体は未署名
前版の本ページへの訂正。eget はデフォ
ルトで checksum を検証します — GitHub の release から
SHA256SUMS(または各 asset の
.sha256)ファイルを取得し、ダウンロードし
た asset の hash と比較します。一般ユーザは
x eget use lha を実行するたびに checksum
検証を得ます。本ページの以前の説明(eget は TLS のみで
検証)は誤りでした。
残る課題。SHA256SUMS ファイルもバイナ
リ asset も同じ GitHub release に紐づきます。GitHub アカ
ウントの侵害 — あるいはメンテナによる release 編集権限の
悪用 — は両方を一度に差し替え可能。Checksum ファイル自
体は署名されていないので、eget の検証は asset と
checksum の間の内部一貫性をチェックするもので、
それぞれの出所ではありません。
1 行修正:R11 と同じ — 各 release asset に
cosign sign-blob(keyless、GitHub OIDC 経
由)。これで GitHub の checksum の上に sigstore 署名が一
層乗ります。強い完全性を求めるユーザはダウンロード後に
cosign verify-blob を実行。アカウント侵害後
のギャップを塞ぎます。
R13. SBOM なし
リスク:小さい静的バイナリには低。静的バイナリの唯一の 「コンポーネント」は libc(macOS では libSystem、OS の一部)。 lha 自体のみ。SBOM 価値は限定的。
5. まとめ — 実際にやる変更
impact-to-effort 比率順(高い順):
| # | 変更 | 工数 | 低減するリスク |
|---|---|---|---|
| R9 | CI で subtree SHA 検証 | 5 分 | 「我々の lha は upstream 由来ではない」攻撃面を捕捉 |
| R5 | 各 uses: を commit SHA に固定 |
15 分 | action タグ移動攻撃面を排除 |
| R6 | alpine:3.20 を digest に固定 |
5 分 | イメージ再ビルド攻撃面を排除 |
| R11 | cosign で各 release artifact 署名 | 30 分 | 配布チャネルの完全性底 |
| R7 | CODEOWNERS for .github/ |
10 分 | CI 挙動を改変できる人を限定 |
| R13 | SBOM を release asset に | 30 分 | コンプライアンス |
合計 ~1.5 時間の作業で業界標準 → 十分ハードニングへ。