Skip to content

Commit 93adb61

Browse files
Merge pull request #538 from OpenTouryoProject/3rd_agent
続・GitHubUsage.md
2 parents b33b64e + c36a662 commit 93adb61

2 files changed

Lines changed: 176 additions & 18 deletions

File tree

.github/workflows/build-windows.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ name: Build on Windows
3232
on:
3333
push:
3434
branches: [deps]
35+
# master 宛の PR ではリリースの関所として動かす。
36+
#
37+
# ・master へのマージは develop からのリリース時だけで、往復しない。
38+
# 1 回あたり 10 分前後の待ちは受け入れられる。
39+
# ・RELEASE.md フェーズ 1 の「検証 3 本」を機械的に担保する。
40+
#
41+
# develop は対象にしない。deps ⇔ develop ⇔ feature と往復が多く、
42+
# 毎回のマージが CI 待ちになる。deps 由来の変更は deps で検証済みでもある。
43+
pull_request:
44+
branches: [master]
3545
workflow_dispatch:
3646

3747
permissions:

GitHubUsage.md

Lines changed: 166 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ https://github.com/organizations/OpenTouryoProject/settings/security_products
3636
| Code scanning(CodeQL 既定セットアップ) | 有効 | ワークフローを自前で持たずに済む |
3737
| Private vulnerability reporting | 有効 | **暗号・認証ライブラリを公開している**ため、非公開の報告窓口が要る |
3838
| Secret scanning | 有効 | |
39-
| **Secret scanning: Push protection** | **無効** | **後述。棚卸し後に有効化する** |
39+
| **Secret scanning: Push protection** | **有効** | 2026-08-10 に有効化(後述) |
4040
| **Secret scanning: Non-provider patterns** | **無効** | **後述** |
4141
| **Secret scanning: Validity checks** | **無効** | **後述** |
4242
| GitHub Advanced Security | 有効 | 公開リポジトリでは無料 |
@@ -52,12 +52,12 @@ https://github.com/organizations/OpenTouryoProject/settings/security_products
5252

5353
#### Push protection
5454

55-
**段階分けのため。** 有効にすると、検知した push をその場で止める。
55+
**段階分けのため、最初は無効にした** 有効にすると、検知した push をその場で止める。
5656

5757
```
58-
1. Secret scanning だけ先に有効化 ← いまここ
59-
2. アラートを棚卸し(現在 0 件)
60-
3. Push protection を有効化
58+
1. .github/secret_scanning.yml を develop へ入れる
59+
2. Secret scanning だけ先に有効化 → アラート 0 件で立ち上がり、棚卸しは不要だった
60+
3. Push protection を有効化 → 2026-08-10 実施
6161
```
6262

6363
いきなり両方入れると、**リリース作業の途中で不意に止まる**
@@ -123,7 +123,7 @@ CodeQL の内訳(上位)。
123123
| **`requireSSL``true` でない** | 4 | **対応済み。** `requireSSL="true"` の行を**コメントアウトで併記**し、「本番ではコメントアウトを外す」と明記。**サンプルは HTTP で動かす**ため、既定は `false` のまま |
124124
| **`Encryption using ECB`** | 1 | **対応済み → 棄却。** `EnumSymmetricAlgorithm.CipherMode_ECB`**`[Obsolete]` を付与**した。利用者が指定したときだけ通る 5 択の 1 つで、**既定ではない**(指定しなければ .NET 既定の CBC)。リポジトリ内に指定箇所は 0 件。**削除は下位互換を壊す**ため、非推奨化に留めた。コードは残るのでアラートは消えず、`won't fix` で棄却する |
125125
| **`Cookie 'Secure'` 未設定** | 2 | **対応済み → 棄却。** 指摘されたのは `StdMigration/CookieExtensions.cs``Set` 3 つのうち **`CookieOptions` を受け取らない 2 つ**で、**リポジトリ内に呼び出しが 1 件も無い**(利用者向けに残している移植 API)。実際に Cookie を作るのは `CookieOptions` 版を呼ぶ `FxCmnFunction` で、そちらの **4 箇所**(net48 / netcore100 各 2)を修正した。**`HttpOnly` は設定済みだったが `Secure` が無かった**(前者は XSS 対策、後者は盗聴対策で別物) |
126-
| **`DOM text reinterpreted as HTML`** | 2 | **対応済み。** サンプルの `common.js`(自作、CS / VB 同一)。擬似ダイアログの `iframe.src` に URL を設定する前に`Fx_IsSafeDialogUrl()` でスキームを検証するようにした。**既存の脆弱性を塞いだのではない**(URL はフレームワークの `ShowModalScreen` が組み立て、利用者入力は入らず、AppScan もクリア済み)**サンプルを手本にしたコードが動的な URL を流し込む場合に備える多層防御** |
126+
| **`DOM text reinterpreted as HTML`** | 2 | **多層防御を追加したが、アラートは残る → 棄却。** 擬似ダイアログの `iframe.src` に URL を設定する前に `Fx_IsSafeDialogUrl()` でスキームを検証するようにした。**CodeQL はこの検証関数をサニタイザと認識しない**ため、指摘は消えない。**既存の脆弱性を塞いだのではない**(URL はフレームワークの `ShowModalScreen` が組み立て、利用者入力は入らず、AppScan もクリア済み) |
127127

128128
**棄却は削除ではない。** Security タブに `Dismissed` として残り、
129129
棄却した人・日時・理由・コメントが記録される。復帰もできる。
@@ -173,6 +173,60 @@ newCookie.Secure = HttpContext.Current.Request.IsSecureConnection; // net48
173173
> `//Secure = CookieSecurePolicy.Always` がコメントアウトで残っている。
174174
> アプリ全体の Cookie ポリシーで一括指定する方法もある(未検討)。
175175
176+
### CodeQL の挙動(踏んだので記録する)
177+
178+
#### ① 棄却は、コードが動くと外れる
179+
180+
**アラートの同一性は「ルール + 位置」で判定される。**
181+
棄却した箇所の周辺を編集して行番号がずれると、
182+
**別のアラートとして作り直され、`open` に戻る。**
183+
184+
`Encryption using ECB` がこれに当たった。棄却済みだったが、
185+
`#pragma warning disable` とコメントを足したことで **287 行 → 291 行**にずれ、
186+
新しい番号で `open` に戻ったため、**同じ理由で棄却し直した。**
187+
188+
**棄却済みの箇所を触ったら、棄却が残っているかを確認すること。**
189+
190+
#### `fixed` は「直った」とは限らない
191+
192+
`#537` のマージ後、`fixed` が 12 件になったが、
193+
**本当に解消したのは `Missing X-Frame-Options` の 6 件だけ**だった。
194+
195+
```
196+
fixed 12 件 ≠ 12 件が直った
197+
6 件 解消(新しいアラートが出ていない)
198+
6 件 位置が変わっただけ(同数が open に出ている)
199+
```
200+
201+
**`fixed` の件数だけを見て「減った」と判断してはならない。**
202+
`open` の中身とあわせて確認する。
203+
204+
#### ③ PR のチェックと、ブランチ全体のスキャンは別物
205+
206+
**PR のチェックは差分中心で、ブランチ全体の状態とは一致しない。**
207+
208+
- `#537``CodeQL` チェックは `fail` だったが、**新しい脆弱性ではなかった**
209+
行番号がずれた 2 件を「この PR で追加された」と数えたため
210+
- 逆に、PR 時点では `'requireSSL'` が解消したように見えたが、
211+
**`develop` の全体スキャンでは残っていた**
212+
213+
**判断は `develop`(既定ブランチ)のスキャン結果で行うこと。**
214+
215+
#### ④ 検証を足してもアラートは消えないことがある
216+
217+
`DOM text reinterpreted as HTML` に対して `Fx_IsSafeDialogUrl()` を追加したが、
218+
**CodeQL はこの関数をサニタイザと認識せず、指摘は残った。**
219+
220+
**「アラートが消えること」と「安全になること」は別である。**
221+
222+
| | アラート | 実際の安全性 |
223+
|---|---|---|
224+
| `'requireSSL'`(コメントで併記) | **消えない** | **変わらない**(有効な設定は `false` のまま) |
225+
| `DOM text ...`(検証を追加) | **消えない** | **上がった**(危険なスキームを実際に弾く) |
226+
| `X-Frame-Options`(ヘッダ追加) | **消えた** | **上がった** |
227+
228+
**アラート件数を目的にすると判断を誤る。** 何を守りたいかで決めること。
229+
176230
---
177231

178232
## 2. ブランチと保護
@@ -187,8 +241,9 @@ newCookie.Secure = HttpContext.Current.Request.IsSecureConnection; // net48
187241

188242
### 現状の弱点
189243

190-
- **`develop` に必須ステータス チェックが無い。** CI は `push: [deps]` でしか動かないため、
191-
`develop` へ入る変更は CI で検証されていない
244+
- **`develop` に必須ステータス チェックは置かない**(意図的)。
245+
`deps``develop` ⇔ feature と往復が多く、毎回のマージが CI 待ちになるため。
246+
代わりに **`master` 宛の PR で CI を動かす**(7 節)
192247
- `master``enforce_admins` は無効(少人数運用のため意図的)
193248
- `delete_branch_on_merge` は無効。マージ済みブランチが残る
194249

@@ -199,17 +254,63 @@ newCookie.Secure = HttpContext.Current.Request.IsSecureConnection; // net48
199254
200255
---
201256

202-
## 3. ワークフロー
257+
## 3. GitHub Actions
203258

204-
| ファイル | 発火 | 内容 |
259+
### `.github/` の中身
260+
261+
| ファイル | 役割 |
262+
|---|---|
263+
| [`workflows/build-windows.yml`](.github/workflows/build-windows.yml) | 検証 3 本(ビルド・単体テスト・疎通)を windows-latest で |
264+
| [`workflows/dependabot-retarget.yml`](.github/workflows/dependabot-retarget.yml) | Dependabot PR の向き先を `deps` へ変更 |
265+
| [`secret_scanning.yml`](.github/secret_scanning.yml) | Secret scanning のアラートから除外するパス(1 節) |
266+
267+
**`dependabot.yml` は置いていない**(4 節)。
268+
269+
### ワークフロー
270+
271+
| ファイル | 発火 | `permissions` |
205272
|---|---|---|
206-
| [`build-windows.yml`](.github/workflows/build-windows.yml) | `push: [deps]` / 手動 | 検証 3 本(ビルド・単体テスト・疎通)を windows-latest で |
207-
| [`dependabot-retarget.yml`](.github/workflows/dependabot-retarget.yml) | `pull_request_target` | Dependabot PR の向き先を `deps` へ変更 |
208-
| CodeQL(既定セットアップ) | GitHub 管理 | ワークフロー ファイルを持たない |
273+
| `build-windows.yml` | `push: [deps]` / **`pull_request: [master]`** / 手動 | `contents: read` |
274+
| `dependabot-retarget.yml` | `pull_request_target``opened` | `pull-requests: write` |
275+
| CodeQL(既定セットアップ) | GitHub 管理`push` / `pull_request` / 週次 | ワークフロー ファイルを持たない |
209276

210277
`build-windows.yml` は既知の署名エラー(`MSB3482` / `MSB3325` / `MSB3321`)を除外する。
211278
**ローカルと CI で出るコードが違う**理由はファイル冒頭のコメントにある。
212279

280+
> **`dependabot-retarget.yml``pull_request_target` を使う。**
281+
> これは**ベース ブランチ側の定義を、書き込み権限付きで動かす**トリガである。
282+
> ここで PR のコードを `checkout` して実行すると、
283+
> **PR に任意のコードを書ける相手へ権限を渡すことになる**(pwn request)。
284+
>
285+
> 本ワークフローは **`checkout` を行わず**`gh` コマンドだけを実行し、
286+
> 権限も `pull-requests: write` の 1 つに絞っている。
287+
> **この 2 点は変更しないこと。** 理由はファイル冒頭のコメントにある。
288+
289+
### GitHub 側の設定
290+
291+
```
292+
Actions enabled
293+
allowed_actions all … 使用できるアクションを制限していない
294+
sha_pinning_required false … アクションの SHA 固定を強制していない
295+
default_workflow_permissions read … 2026-08-10 に write から変更
296+
can_approve_pull_request_reviews false … 2026-08-10 に true から変更
297+
Secrets / Variables なし
298+
```
299+
300+
**既定を `read` にした。** 各ワークフローは `permissions:` を明示して
301+
最小権限にしているため、**既定を下げても動く**(実測で確認)。
302+
**`permissions:` を書き忘れた新しいワークフローが、
303+
書き込み権限を持ってしまう状態を無くすため。**
304+
305+
> それでも**新しいワークフローを足すときは `permissions:` を書くこと。**
306+
> 何を必要としているかが、ファイルを見て分かる方がよい。
307+
308+
**`can_approve_pull_request_reviews` も無効にした。**
309+
Actions が PR を承認できると、`master` のレビュー必須が形骸化するため。
310+
311+
`Secrets` は 1 つも登録していない。**認証が要る操作は入れていない**ということであり、
312+
足すときは「本当に必要か」を先に考える。
313+
213314
---
214315

215316
## 4. Dependabot
@@ -290,25 +391,72 @@ gh api orgs/OpenTouryoProject/code-security/configurations/265927/repositories \
290391
--jq '.[] | {status, repo: .repository.full_name}'
291392
```
292393

293-
### 次に Push protection を有効化するとき
394+
### 2026-08-10 : Push protection の有効化
294395

295-
**構成の値を変えるだけでよい**(リポジトリ個別の設定は触らない)。
396+
Secret scanning のアラートが 0 件で安定したことを確認した上で実施。
397+
**構成の値を変えるだけ**で、リポジトリ個別の設定は触らない。
296398

297399
```bash
298400
gh api -X PATCH orgs/OpenTouryoProject/code-security/configurations/265927 \
299401
-f secret_scanning_push_protection=enabled
402+
403+
# リポジトリ側へ反映されたかを確認する
404+
gh api repos/OpenTouryoProject/OpenTouryo --jq '.security_and_analysis'
405+
```
406+
407+
**戻すとき**も同じ形(`-f secret_scanning_push_protection=disabled`)。
408+
409+
### 2026-08-10 : Actions の既定権限を絞る
410+
411+
**リポジトリ設定**(組織の構成ではない)。`repo` スコープで実行できる。
412+
413+
```bash
414+
gh api -X PUT repos/OpenTouryoProject/OpenTouryo/actions/permissions/workflow -f default_workflow_permissions=read -F can_approve_pull_request_reviews=false
415+
416+
# 確認
417+
gh api repos/OpenTouryoProject/OpenTouryo/actions/permissions/workflow
300418
```
301419

420+
**下げる前に、各ワークフローが `permissions:` を明示しているかを確認すること。**
421+
既定に頼っているワークフローがあると、権限不足で失敗する。
422+
423+
```bash
424+
grep -A3 '^permissions:' .github/workflows/*.yml
425+
```
426+
427+
### 有効化後の運用
428+
429+
**新しく push する内容だけ**が検査される。既存の履歴は対象外。
430+
431+
| | |
432+
|---|---|
433+
| 止まるもの | **発行元を特定できる形のキー**(NuGet の `oy2...`、AWS、GitHub PAT など) |
434+
| 止まらないもの | `.pfx` / `.cer`、接続文字列の `Password=``Non-provider patterns` が無効のため) |
435+
436+
止まった場合は理由を選んでバイパスできる(**管理者に通知され、記録が残る**)。
437+
438+
> **`.github/secret_scanning.yml` の除外は Push protection には効かない。**
439+
> あちらはアラート(Secret scanning)の抑制であり、push の判定は見ていない。
440+
302441
---
303442

304443
## 7. 未着手の提案
305444

306445
| | 内容 |
307446
|---|---|
308-
| **`develop` の CI 必須化** | `build-windows.yml` `on: push``develop` を追加し、ブランチ保護で必須チェックにする |
309-
| **Push protection の有効化** | Secret scanning のアラートが 0 件で安定していることを確認してから |
447+
| **`master` の CI 必須化** | `build-windows.yml` `on: pull_request: branches: [master]` を足し、`master` のブランチ保護で必須チェックにする。**リリースの関所**として働く(下記) |
448+
| `allowed_actions` を絞る / SHA 固定 | 現在 `all` / 強制なし。サプライ チェーン対策。**運用が重くなる**ので、必要性とあわせて判断する |
310449
| `delete_branch_on_merge` | マージ済みブランチを自動削除する |
311450
| `SECURITY.md` | Private vulnerability reporting は有効にしたが、文書は未整備 |
312451
| Issue / PR テンプレート | 「調査 → 実装 → 検証」の型が定まっているのでテンプレート化できる |
313452
| `.github/dependabot.yml` | #517 の決着後 |
314-
| **CodeQL の再スキャン待ち** | 修正した 10 件(`X-Frame-Options` 6 / `requireSSL` 4)は `develop` で再スキャンされるまで `open` のまま。**`requireSSL` は有効な設定が `false` のままなので消えない見込み**で、その 4 件は結果を見てから棄却する |
453+
454+
> **`develop` を必須チェックの対象にはしない。**
455+
> `deps``develop` ⇔ feature と**往復が多いハブ**であり、
456+
> 毎回のマージが CI 待ちになる。
457+
> `deps` 由来の変更は**すでに `deps` で検証済み**なので、二度手間でもある。
458+
>
459+
> **`master` は往復しない。** `develop` からのマージはリリース時だけで、
460+
> 1 回あたり 9 分程度の待ちは受け入れられる。
461+
> `RELEASE.md` フェーズ 1 の「検証 3 本」を、機械的に担保できる。
462+

0 commit comments

Comments
 (0)