Skip to content

custom rules and autofix

github-actions[bot] edited this page Sep 6, 2026 · 3 revisions

設計: GUIからの禁止ワード追加と自動修正

要件

  1. YAMLを編集できない人 (UXライター・運用担当) が、フォームから禁止ワード・禁止表現を追加できること
  2. それをセキュアに成立させること
  3. 検出だけでなく、自動修正のモードを持つこと

脅威モデル

辞書は利用者全員の環境で正規表現として実行されます。つまり辞書への書き込みは コード実行に近い権限です。想定する脅威は次の4つです。

脅威 内容
ReDoS 破滅的バックトラックを起こす正規表現の混入。CI・フック・エディタが停止する
誤検出爆弾 広すぎるパターンの混入で、正当な文書が大量に検出され、ツールが使われなくなる
直列化インジェクション フォーム入力を文字列連結でYAML化したときの構造破壊
なりすまし・スパム 誰でも書ける経路を置いた場合の、辞書の汚染

設計: 3層に分ける

[提案層]  フォーム GUI (Issue Form / 提案ページ / dead-cliche ui)
    ↓ 構造化された提案 (リテラル文字列 + why/ask + 例文)
[検証層]  スキーマ検証・安全性検査・負例テスト・CI・人間のレビュー
    ↓ マージ
[辞書層]  rules/*.yml (共有辞書) / .deadcliche/custom-rules.yml (プロジェクト辞書)

原則は2つです。

  • 信頼できない入力はリテラル文字列 (surface) だけを受け付ける。リテラルはエンジン側で 正規表現エスケープされるため、ReDoSもインジェクションも構造的に起こせない
  • 正規表現 (pattern) はレビュー済みの経路だけに通す。フォームからは提案として受け取り、 人間がレビューして初めて辞書に入る

提案層の実装形態 (併存させる)

a. GitHub Issue Form (OSS本体への提案。実装済み)

.github/ISSUE_TEMPLATE/propose-rule.yml のフォームで、表現・理由・悪い例・良い例・ 負例を構造化して受け付けます。認証はGitHubアカウント、書き込み権限は不要で、 マージまでは辞書に入りません。ホストするサーバーがなく、攻撃面を増やしません。 将来はActionで提案Issueから下書きPRを自動生成します (ロードマップ)。

b. 提案フォームのページ (実装済み)

入力からYAMLスニペットと、上記Issue FormへのプリフィルURLを生成する静的ページです。 バックエンドを持たず、どこにも自動送信しません。生成物を既存のセキュアな経路 (Issue / PR) に運ぶだけなので、設置自体にリスクがありません。

c. プロジェクト内カスタム辞書 + ローカルUI (辞書機構は実装済み)

チーム固有の禁止ワードはOSS本体ではなく、各リポジトリの .deadcliche/custom-rules.yml に置きます。.deadclicherc.json から参照します。

{
  "preset": "business",
  "customRules": [".deadcliche/custom-rules.yml"]
}
  • surface (リテラル) は常に許可。フォーム入力をそのまま入れられる安全な形式
  • pattern (正規表現) は既定で無効。"trustCustomPatterns": true を明示した場合のみ 読み込み、さらに長さ上限とコンパイル検査、量指定子の入れ子の拒否を通す
  • カスタム辞書はリポジトリにコミットされるため、変更は必ずPRレビューを通る

ローカルUI (dead-cliche ui) は実装済みです。127.0.0.1にだけ束ね、起動時に作る 合言葉をページの取得と保存の両方で要求し、受け取るのはsurface (リテラル) だけです。 Hostヘッダを検査して別名からの到達 (DNSリバインディング) を拒み、Originヘッダが 別サイトのときも拒みます。書き込むのはカスタム辞書ファイルだけで、rules/ には 書きません。保存はjs-yamlのdumpを通し、フォーム入力を文字列連結でYAML化しません。

dead-cliche ui [--port 7777] [--file .deadcliche/custom-rules.yml]

起動すると合言葉付きのURLが表示されます。そのURLでだけ開けます。

検証層

  • スキーマ検証と重複idの拒否 (実装済み、CI)
  • 悪い例が検出され、良い例と負例が検出されないことの強制 (実装済み、CI)
  • カスタムパターンの安全性検査 (実装済み): 200文字上限、コンパイル可能、 (a+)+ 型の量指定子の入れ子をヒューリスティックで拒否
  • 静的検査でReDoSを完全には排除できないため、信頼境界を「レビューを通ったか」に 置きます。フォーム入力がレビューなしで実行に到達する経路を作らないことが本体です

自動修正の設計: 2層に分ける

クリシェの大半は「情報の欠落」であり、機械置換では直せません (羅針盤 を消しても、 誰が何を判断するのかは書かれないままです)。そこで修正を2層に分けます。

第1層: 決定論的修正 (実装済み)

意味を変えずに機械置換できるルールにだけ、辞書に fix フィールドを持たせます。 表記の規範 (して下さいしてくださいが失敗しましたに失敗しましたすることができますできます など) が対象です。

  • CLI: dead-cliche fix <files> は差分の提示のみ (dry-runが既定)。 --write を付けたときだけ書き込む
  • textlint: fixerとして実装済み。npx textlint --fix で同じ置換が効く
  • fixを持つルールはテストで往復検証される (悪い例にfixを適用すると検出0件になる)

第2層: LLMによる書き直し (実装済みの経路を使う)

fixを持たないルールはask (何を書くべきかの問い) に答える書き直しが必要で、 これはClaude側の役割です。フックが検出時にexit 2で書き直しを要求する現行の 仕組みが、Claude環境ではそのまま自動修正モードとして機能します。全自動にしたい場合は /dead-cliche:check --fix を使います。書き直し後に再チェックが走り、0件になる まで収束させます。LLMの書き直しは非決定的なため、CIには第1層だけを載せます。

段階的な導入計画

フェーズ 内容 状態
1 custom-rules機構、fixフィールド、Issue Form、提案ページ 実装済み (v0.7.0)
2 提案Issueから下書きPRを生成するAction 実装済み
3 dead-cliche ui (ローカル編集フォーム) 実装済み
4 組織ダッシュボード (検出統計・辞書の採用状況) 構想のみ

Clone this wiki locally