部署で作った Python・GAS の業務用スクリプトを毎月集めて読み、認証情報の直書き・全件削除・例外の握りつぶしなど危ない書き方を判定して持ち主に直し方を返す
各部署が作った Python と Google Apps Script のスクリプトを毎月集め、認証情報の直書きや確認なしの全件削除などの危ない書き方を判定します。該当する行と直し方を持ち主に返し、情報システムの担当は判定の確認と難しいものに絞って関わります。
- 生成AI
- ChatGPT/Claude/Gemini
- 対象業界
- 商社/物流/製造
- 対象部門
- 情報システム
- 対象業務
- 内容確認・チェック
- 主な課題
- 人手が足りない/属人化している/確認ミスが多い
- AIで行う処理
- 判定
- 主な効果
- 品質標準化/属人化解消/工数削減
- 導入難易度
- ★★★☆☆
- 実装レベル
- 半自動化
- 費用感
- API連携(中)
- 人間の確認
- 必須
01導入前 / 導入後の業務フロー
- 台帳から、今月点検するスクリプトの一覧を作る
- リポジトリや Apps Script のエディタを開き、ソースを読む
- 危ない書き方があれば、行番号と内容をメモする
- 持ち主にメールで知らせ、直し方を説明する
- 直ったものを読み直し、台帳に点検の日付を付ける
- 自動毎月1日に、台帳から今月点検するスクリプトの一覧を作る
- 自動Python はリポジトリから、Apps Script は Apps Script API でソースを取り出す
- 自動Python は Bandit で解析し、結果を JSON で受け取る。Apps Script はマニフェストの権限を取り出す
- 自動文字列の検索で、APIキーやトークンらしい文字列を探して印を付ける
- 自動Claude API に、ソース、解析の結果、台帳の目的を渡し、8つの観点で判定させる
- 自動判定の結果を、観点ごとの該当の有無・行・重さ・直し方の形で受け取り、点検の一覧に書く
- 人担当者が「重い」と出たものと、AIが判断できなかったものを開いて確かめる
- 人担当者が確かめた指摘を、持ち主に送る。直し方の文はAIの下書きを使う
- 人持ち主が直したものは、翌月の点検で同じ観点が消えたかを見る
各工程の詳しい説明を読む
- 台帳から、今月点検するスクリプトの一覧を作る
- リポジトリや Apps Script のエディタを開き、ソースを読む
- 危ない書き方があれば、行番号と内容をメモする
- 持ち主にメールで知らせ、直し方を説明する
- 直ったものを読み直し、台帳に点検の日付を付ける
(a)120本を読み切れない。 1本30分でも月60時間です。兼務の2名には無理な量で、定期の見直しの番が来たものは、ほとんど読まれないまま点検の日付だけが更新されています。
(b)見る観点が人によって違う。 1人はセキュリティの経験があり、直書きのキーと外部への送信をよく見ます。もう1人は業務システムの経験が長く、削除や上書きの条件をよく見ます。 どちらが読んだかで、見つかるものが違います。
(c)事故の後に見つかる。 昨年、営業所のスクリプトが表の「出荷済み」の行を消すはずが、条件の列を取り違えてその月の受注の表の全行を消しました。 版の履歴から戻せましたが、半日の作業が止まりました。そのスクリプトは登録時に点検済みでしたが、点検の後に変更されていました。
(d)直し方の説明に時間がかかる。 「パスワードを直書きしないでください」と書くだけでは、持ち主は何に置き換えればよいか分かりません。Apps Script ならスクリプトのプロパティ、Python なら環境変数や秘密情報の置き場所、と1件ずつ説明しています。
- 【自動】 毎月1日に、台帳から今月点検するスクリプトの一覧を作る
- 【自動】 Python はリポジトリから、Apps Script は Apps Script API でソースを取り出す
- 【自動】 Python は Bandit で解析し、結果を JSON で受け取る。Apps Script はマニフェストの権限を取り出す
- 【自動】 文字列の検索で、APIキーやトークンらしい文字列を探して印を付ける
- 【自動】 Claude API に、ソース、解析の結果、台帳の目的を渡し、8つの観点で判定させる
- 【自動】 判定の結果を、観点ごとの該当の有無・行・重さ・直し方の形で受け取り、点検の一覧に書く
- 【人】 担当者が「重い」と出たものと、AIが判断できなかったものを開いて確かめる
- 【人】 担当者が確かめた指摘を、持ち主に送る。直し方の文はAIの下書きを使う
- 【人】 持ち主が直したものは、翌月の点検で同じ観点が消えたかを見る
7番目が、この設計の分かれ目です。人が開くのは全件ではありません。 該当なしと出たスクリプトは一覧で流し見て、重いものと判断できなかったものだけを開きます。 全件を開く設計にすると、60.0時間はほとんど減りません。
8番目で指摘を送るのは、人です。 AIの指摘が誤っていることもあります。持ち主にとって、情報システム部からの指摘は「直さなければならないもの」です。 誤った指摘が届けば、動いているスクリプトを壊す直しにつながります。
02今回想定するシステム構成
スクリプトの登録の台帳(今月の点検の一覧) ▼【トリガー】毎月1日 収集のプログラム(情報システム部が用意) ├──▶ Python:社内のリポジトリから取り出す → Bandit で解析(JSON) ├──▶ Apps Script:Apps Script API(projects.getContent)で取り出す │ → マニフェストの権限を取り出す ├──▶ キー・トークンらしい文字列の検索 ▼ Claude API ── 8つの観点で判定(構造化出力) │ ① 認証情報の直書き ② 全件の削除・上書き ③ 例外の握りつぶし │ ④ 外部への送信先 ⑤ 権限の広さ ⑥ 宛先の直書き │ ⑦ 通信の検証の無効化 ⑧ 終わらない繰り返し ▼ 点検の一覧(重い/注意/該当なし/判断できない) ▼ 【担当者が重いものと判断できないものを確かめる】→ 持ち主へ直し方を送る
| 役割 | 想定する製品 | 代替候補 |
|---|---|---|
| 処理 | Claude API(8つの観点の判定と直し方の下書き) | OpenAI API、Gemini API |
| 解析 | Bandit(Python のソースの静的解析) | Semgrep |
| 収集 | Apps Script API(Apps Script のソースの取り出し) | clasp |
| 台帳 | スクリプトの登録の台帳(いま使っているもの) | - |
新しく足すのは、収集のプログラムと、Claude API の利用だけです。 台帳とリポジトリは変えません。読んで探す部分を、解析とAIの判定に替えます。
Apps Script のソースは、Apps Script API の projects.getContent で取り出せます。 公式のリファレンスでは、GET https://script.googleapis.com/v1/projects/{scriptId}/content でスクリプトのプロジェクトの中身を返し、返る files の一覧のうち1つは必ず「appsscript」という名前の JSON のマニフェストとされています。必要な OAuth のスコープは script.projects か script.projects.readonly のどちらかで、点検には読み取りだけの script.projects.readonly を使います。 versionNumber を付けなければ HEAD の版が返ります。
取り出したファイルには、名前、種類、中身、最後に直した人、直した日時が付いています。 File のリファレンスでは、type は SERVER_JS(サーバー側のコード)、HTML、JSON(マニフェストだけ)のいずれかで、source がファイルの中身、lastModifyUser と updateTime が最後の変更とされています。updateTime を台帳の点検の日付と比べれば、点検の後に変更されたスクリプトが分かります。 第3章の(c)の事故は、ここで拾えました。
判定は、Claude API の構造化出力で受け取ります。 公式のドキュメントでは、output_config.format に type: "json_schema" とスキーマを渡す形で、Claude API では一般提供(GA)とされています。返ってくる JSON の形が決まるので、点検の一覧に機械的に書けます。
03どうやって実装するのか
処理の起点を決める
毎月1日の定時に動かします。 点検の対象は、台帳から次の3つを抜き出して決めます。
| 対象 | 抜き出し方 |
|---|---|
| 新しく登録されたもの | 台帳の登録日が先月のもの |
| 変更されたもの | Python はリポジトリの最終の変更日、Apps Script は updateTime が前回の点検の日より新しいもの |
| 定期の見直しの番が来たもの | 前回の点検から11か月を超えたもの |
2行目が、これまでの点検に無かったものです。 登録のときだけ読む運用では、登録の後の変更が点検されません。 毎月、変更の日付と点検の日付を比べることで、変更されたスクリプトが必ず翌月の点検に入ります。
定時に一括で動かすのは、急ぎの業務ではないためです。 点検の結果は月の初めの数日で担当者が確かめ、持ち主に返します。新しく登録されたものをすぐに見たいときは、台帳の登録をきっかけに1本だけ動かす経路も作れます。
入力データを集める
| データ | 中身 | 取得元 |
|---|---|---|
| ソース | Python のファイル、Apps Script の SERVER_JS と HTML のファイル | リポジトリ/Apps Script API |
| マニフェスト | Apps Script の appsscript.json(権限のスコープ) | Apps Script API |
| 解析の結果 | Bandit の検出(テストの番号、行、重さ、確かさ) | Bandit の JSON 出力 |
| 文字列の検索の結果 | キー・トークンらしい文字列の行 | 収集のプログラム |
| 台帳の情報 | スクリプトの目的、持ち主、使うデータ、動かす頻度 | 登録の台帳 |
| 観点の表 | 8つの観点、該当の例、直し方の決まり | 情報システム部が作る |
質を決めるのは、台帳の目的の欄です。 同じ「表の全行を消す」処理でも、毎朝作り直す集計用の表なら問題がなく、受注の台帳なら重大です。 目的が分からなければ、AIは書き方だけで判定することになり、該当なしと重いの区別が付きません。
Bandit は、Python のソースを解析して危ない書き方を探すツールです。 公式のドキュメントでは、Bandit は各ファイルを処理して AST(構文の木)を作り、その節に対して該当するプラグインを動かし、全部のファイルを見終えたら報告を出すとされています。報告の形には JSON や SARIF などがあります。この構成で特に使うのは、次の検出です。
| Bandit のテスト | 内容 | 対応する観点 |
|---|---|---|
| B105/B106 | パスワードらしい文字列の直書き、関数の引数への直書き | ① 認証情報の直書き |
| B110/B112 | 例外を受けて pass、continue だけをする | ③ 例外の握りつぶし |
| B113 | タイムアウトを付けない通信 | ⑧ 終わらない繰り返し |
| B501 | 証明書の検証を無効にした通信 | ⑦ 通信の検証の無効化 |
| B602 | shell=True でのコマンドの実行 | ② 全件の削除・上書き(コマンドの組み立てによる) |
Apps Script には Bandit が使えないので、文字列の検索とAIの判定だけで見ます。 その分、Apps Script の判定は「判断できない」に倒れやすくしておきます。
データの取得方法を決める
Python のソースは、社内のリポジトリから台帳に登録された置き場所のファイルを取り出します。 取り出したら Bandit を報告の形を JSON にして動かし、結果を受け取ります。
Apps Script のソースは、台帳に登録されたスクリプトのIDで projects.getContent を呼びます。 呼び出しには、点検用のアカウントに各プロジェクトを読む権限が要ります。部署が作ったプロジェクトを、点検用のアカウントに閲覧者として共有してもらうことを、登録の決まりに加えます。
| 取るもの | どこから | 何に使うか |
|---|---|---|
files[].source(SERVER_JS) | projects.getContent | 判定するソース |
files[].source(JSON のマニフェスト) | projects.getContent | oauthScopes から権限の広さを見る |
files[].updateTime | projects.getContent | 点検の後に変更されたかを見る |
files[].lastModifyUser | projects.getContent | 台帳の持ち主と違う人が直したかを見る |
Bandit の results | Bandit の JSON 出力 | 行ごとの検出 |
4行目の「最後に直した人」は、持ち主が異動した後に別の人が直しているスクリプトを見つけるのに使います。 台帳の持ち主と違う人が直していれば、台帳の更新が漏れているか、引き継ぎができていない合図です。
取り出したソースは、点検のあいだだけ情報システム部の作業用の場所に置きます。 部署のフォルダやリポジトリに、点検の結果を書き戻すことはしません。
AIへ渡す前に整形する
- 台帳とソースの対応を確かめる … 台帳の置き場所にファイルが無いもの、IDで取り出せないものは「取り出せない」として一覧に残します
- キー・トークンらしい文字列を伏せる … 検索で見つかった値は、AIに渡す前に
*MASKED* に置き換え、行番号だけを残します - 長いソースを分ける … 1,000行を超えるものは、関数やファイルの単位で分けて判定させ、結果を合わせます
- マニフェストの権限を一覧にする …
oauthScopesを取り出し、Drive やメールの全体に及ぶスコープに印を付けます - Bandit の結果を行番号でソースに結び付ける … AIが「この行の検出」として読めるようにします
- 前回の点検の結果を付ける … 前回の指摘が直っているかを見るため
2番目を省かないでください。 認証情報の直書きを探すために、直書きされたキーそのものを外部のAPIに送ることになっては本末転倒です。 値は伏せ、「この行に直書きがある」という事実だけを渡します。
4番目の権限の広さは、Apps Script で特に効きます。 受注の表を1枚だけ読むスクリプトが、Drive の全ファイルを読み書きできる権限を持っていることがあります。 動いているうちは問題になりませんが、乗っ取られたときの被害の範囲が変わります。
AIに処理させる
させるのは、8つの観点それぞれについて、該当するか、どの行か、重さはどうか、どう直すかを判定することだけです。
| 観点 | 判定の仕方 | 判断できないときの扱い |
|---|---|---|
| ① 認証情報の直書き | 伏せた値の行と Bandit の B105/B106 を見て、本当に認証情報か | 値が伏せられていて種類が分からなければ「判断できない」 |
| ② 全件の削除・上書き | 削除・上書き・ゴミ箱への移動に、件数の上限か確認があるか | 対象の表が何か分からなければ、台帳の目的で見る |
| ③ 例外の握りつぶし | 例外を受けて何も記録せずに続けるか | ログに残していれば該当なし |
| ④ 外部への送信先 | 社外のURLやメールへの送信があるか、台帳の使うデータと合うか | 送信先が社内か社外か分からなければ「判断できない」 |
| ⑤ 権限の広さ | マニフェストのスコープが処理に対して広すぎないか | Python は対象外 |
| ⑥ 宛先の直書き | メールアドレスや表のIDがソースに直書きされているか | - |
| ⑦ 通信の検証の無効化 | 証明書の検証を切っているか | - |
| ⑧ 終わらない繰り返し | タイムアウトや回数の上限の無い待ちや繰り返しがあるか | 上限が別の関数にあるかもしれなければ「注意」 |
重さは3段階で返させます。 「重い」は、そのままでは情報の漏えいかデータの消失につながるもの。「注意」は、条件しだいで問題になるもの。「該当なし」。重さの基準は観点の表に書いておき、AIに独自の基準を作らせません。
| させないこと | 理由 |
|---|---|
| ソースを書き換えて返す | 直すのは持ち主。直したソースを渡すと、確かめずに貼り付ける |
| 8つ以外の観点で指摘する | 指摘が増えすぎ、持ち主が何から直すか分からなくなる |
| スクリプトを止めるべきかを判断する | 止めるかは業務への影響を見て、情報システム部と部署が決める |
| 伏せた値を推測する | 値は不要。行と種類だけでよい |
| 書き方のよしあしを論じる | 点検の目的は危ない書き方を見つけることで、コードの品質の評価ではない |
2行目がいちばん起きやすい失敗です。 AIはソースを読むと、変数の名前の付け方や関数の分け方まで指摘したくなります。指摘が20件並ぶと、持ち主は大事な1件を見落とします。 観点を8つに固定し、それ以外は書かせません。
指示内容を固定する
あなたは情報システム部の担当として、社内の部署が作った業務用スクリプトに
危ない書き方が無いかを点検する立場です。下の8つの観点だけで判定してください。
【観点】
1. 認証情報の直書き(パスワード、APIキー、トークン)
2. 全件の削除・上書き(件数の上限や確認の無い削除、上書き、ゴミ箱への移動)
3. 例外の握りつぶし(例外を受けて記録せずに続ける)
4. 外部への送信先(社外のURL・メールへの送信)
5. 権限の広さ(Apps Script のマニフェストのスコープが処理に対して広い)
6. 宛先の直書き(メールアドレス、表やフォルダのID)
7. 通信の検証の無効化(証明書の検証を切る)
8. 終わらない繰り返し(タイムアウトや回数の上限の無い待ち・繰り返し)
【重さの基準】
- high:そのままでは情報の漏えいかデータの消失につながる
- medium:条件しだいで問題になる
- none:該当しない
- unknown:与えられた情報だけでは判断できない
迷ったときに none を選ばないでください。
【厳守事項】
- 8つの観点以外の指摘をしないでください。変数名や書き方の好みに触れないでください。
- ***MASKED*** の値を推測しないでください。
- ソースを書き換えたコードを返さないでください。直し方は文で、1件2文以内で書いてください。
- 直し方は【直し方の決まり】に沿ってください。
(例:Apps Script の認証情報はスクリプトのプロパティに置く。Python は環境変数か社内の秘密情報の置き場所)
- 該当する行は、ソースの行番号で示してください。推測の行番号を書かないでください。
- 台帳の目的から見て、削除や上書きが意図どおりと読めるときも、件数の上限や確認が無ければ medium にしてください。
- スクリプトを止めるべきかは書かないでください。
【台帳の情報】{purpose, owner, data_used, frequency}
【解析の結果】{bandit_results}
【マニフェストの権限】{oauth_scopes}
【前回の指摘】{previous_findings}
【ソース(行番号付き)】{source}
「迷ったときに none を選ばない」を明記しないと、AIは該当なしに寄ります。 ソースの一部しか見えないとき、「問題は見当たりません」と書くのがAIにとって最も無難だからです。 unknown を選ばせて人に回すほうが、見落としより安全です。
「意図どおりと読めても上限が無ければ medium」を入れるのは、第3章の(c)のためです。 「出荷済みの行を消す」処理は、意図どおりの削除に見えます。問題は、条件を取り違えたときに全行が消えることです。 件数の上限か確認があるかで見させます。
出力形式を固定する
構造化出力で、次の形の JSON を受け取ります。
{
"script_id": "",
"language": "python | apps_script",
"findings": [
{
"aspect": "hardcoded_secret | mass_delete | swallowed_exception | external_send | broad_scope | hardcoded_target | tls_disabled | unbounded_loop",
"severity": "high | medium | none | unknown",
"lines": [0],
"evidence": "",
"fix": ""
}
],
"previous_findings_resolved": ["mass_delete"],
"note_for_reviewer": ""
}
findings には、8つの観点のそれぞれを必ず1つずつ並べさせます。 該当しない観点も none として返させ、観点を飛ばしていないことを形で確かめます。 8個そろっていない結果は、人に回します。
1つ目の理由は、点検の一覧に機械的に書けることです。 severity が high か unknown のものだけを担当者の確認待ちにし、medium は持ち主への連絡の下書きに回し、すべて none のものは点検済みにします。
2つ目は、スキーマの制約に合わせられることです。 公式のドキュメントでは、構造化出力は数値の範囲や文字列の長さの制約、minItems が0か1を超える配列の制約に対応しておらず、additionalProperties は false にする必要があるとされています。8個そろっているかはスキーマでは強制できないので、受け取った後のプログラムで数えます。 拒否や max_tokens での打ち切りのときは形どおりに返らないことがあるとされているので、その場合も人に回します。
3つ目は、previous_findings_resolved で前回の指摘が直ったかを追えることです。 前回 high だった観点が今回 none になっていれば、直ったと読めます。
システムへ連携する
| つなぎ先 | 方式 | 内容 |
|---|---|---|
| スクリプトの登録の台帳 | 収集のプログラムが読む | 今月の対象、目的、持ち主 |
| 社内のリポジトリ | 収集のプログラムが読む | Python のソース |
| Apps Script API | projects.getContent(読み取りのスコープ) | Apps Script のソース、マニフェスト、変更日 |
| Bandit | 収集のプログラムが動かす | Python の解析の結果(JSON) |
| Claude API | API呼び出し(構造化出力) | 8つの観点の判定 |
| 点検の一覧 | 収集のプログラムが書く | 判定の結果と確認待ちの印 |
部署のスクリプトとリポジトリには、何も書き込みません。 点検用のアカウントは読み取りだけの権限で動かします。点検の仕組みそのものが全部のスクリプトを書き換えられる権限を持つと、今度は点検の仕組みが最も危ないスクリプトになります。
人が確認する
unknownを先に見る … 多くは、ソースが分かれていて上限や確認が別の場所にあるものhighを確かめる …evidenceと行番号でソースを開き、本当に該当するかを見ます- 持ち主への連絡を送る … AIの
fixを下書きに、担当者が直して送ります。送るのは担当者です - 重いものは持ち主の上長にも知らせる … 認証情報の直書きと、確認の無い全件の削除
- 判定を覆したら記録する … どの観点を、どちらに変えたか
目標は、120本をならして1本6分です。 すべて none のスクリプトは一覧で流し見て、high と unknown のものに時間を使います。 該当の多い月は6分を超えます。
例外に対処する
| 起きること | 対応 |
|---|---|
| 台帳の置き場所にファイルが無い/IDで取り出せない | 「取り出せない」として一覧に残し、持ち主に置き場所を確かめる |
| Apps Script のプロジェクトが点検用のアカウントに共有されていない | 持ち主に共有を頼む。共有されるまで点検済みにしない |
findings が8個そろっていない | 人に回す。作り直しを1回だけ試す |
| 拒否や打ち切りで形どおりに返らない | 人に回す |
| 1,000行を超えて分けたソースで、判定が食い違う | 重いほうを採り、人に回す |
| 外部のライブラリの中に危ない書き方がある | この構成の対象外。社内で書いた部分だけを判定する |
| 持ち主が退職・異動している | 部署の上長に持ち主の付け替えを頼み、台帳を直す |
上から2行目までが大半を占めます。 どちらもAIの問題ではなく、台帳と共有の整備の問題です。 最初の数か月は、点検より台帳の整備に時間がかかります。
記録を残す
- 点検した日、対象のスクリプトと版(Apps Script は
updateTime、Python はリポジトリの版) - Bandit の JSON 出力と、文字列の検索の結果(値は伏せたもの)
- Claude API が返した JSON の全文
- 人が判定を覆した記録 … どの観点を、どちらに変えたか
- 持ち主へ送った連絡と、直った版
- 観点ごと・部署ごとの
highの件数(月ごと)
最後の行は、研修の題材を選ぶのに使います。 特定の部署で認証情報の直書きが続くなら、その部署向けにスクリプトのプロパティの使い方を教えるほうが、1本ずつ指摘するより早く減ります。
04実装レベルの3段階
本記事が想定するのは半自動化です。 1本30分が6分になるのは、この段階です。読む20分が、判定の結果を確かめる時間に置き換わります。 本格構成で変わるのは、点検の時期です。 月に1回ではなく、変更のたびに点検されます。第3章の(c)のような「点検の後の変更」の事故を、翌月まで待たずに止められます。 ただし、変更をきっかけにする仕組みは置き場所ごとに作る必要があり、開発の手間が増えます。
05工数削減シミュレーション
導入後 120件 × 6分 ÷ 60 = 12 時間/月
自社条件で導入効果を整理したい方へ
このユースケースを自社に当てはめた場合の前提値と削減見込みを、業務ヒアリングをもとに整理します。
06向いている企業・向いていない企業
- 現場の部署が Python や Google Apps Script で業務の小さな自動化を作るようになり、登録されたスクリプトが数百本ある会社。情報システム部門が全部を読む人手が無く、APIキーやパスワードの直書き、確認なしの全件削除が見つかるのは事故の後になっている場合。スクリプトの登録の台帳と置き場所(共有フォルダやリポジトリ、Apps Script のプロジェクト)がすでに決まっている場合。
- スクリプトが数十本で、情報システム部門が変更のたびに目で読めている場合。業務のプログラムをすべて開発会社が作り、コードレビューの手順がすでにある場合。スクリプトの置き場所がばらばらで、どこに何本あるかが分からない場合(先に台帳づくりが要る)。なお、そのスクリプトを使い続けてよいかの判断、業務への影響の評価、脆弱性の診断は、この構成では代替できません。
07最小構成で試す方法
- 登録済みのスクリプトから10本を選ぶ(うち数本は、過去に指摘して直したものの直す前の版を入れる)
- 認証情報らしい値を手で伏せる
- 手元のAIサービスの画面に、第7章の観点と重さの基準と一緒に1本ずつ貼る
- 返ってきた判定を、過去の指摘と見比べる
10本は必ずやってください。 収集の仕組みを作る前に、8つの観点で判定できるか、指摘が増えすぎないかを確かめます。
| 出てきた内容 | 判断 |
|---|---|
過去に指摘したものと同じ観点が high で出た | 収集とAPIの呼び出しに進む |
| 8つ以外の指摘が混ざる | 指示の書き方で直る。構成は有効 |
unknown が多い | 台帳の目的の欄が足りない。台帳の整備が先 |
3行目が出ることは珍しくありません。 目的が「業務の効率化」とだけ書かれた台帳では、削除が意図どおりかをAIも人も判断できません。 何の表を、何のために、どの頻度で触るのかを書いてもらう決まりにします。
08実装時につまずきやすいポイント
| 問題 | 対策 |
|---|---|
| 直書きのキーをそのままAPIに送ってしまう | 文字列の検索で見つけた値を伏せてから渡す |
| AIが該当なしに寄る | 迷ったら unknown を選ばせ、人に回す |
| 8つ以外の指摘が増える | 観点を固定し、指示に「触れない」と書く |
| 観点を飛ばした結果が返る | 8個そろっているかをプログラムで数える。スキーマでは強制できない |
| 意図どおりの削除を見逃す | 件数の上限か確認が無ければ medium にする |
| Apps Script のプロジェクトが取り出せない | 点検用のアカウントへの共有を登録の決まりにする |
| 点検の後の変更が見られない | 変更日と点検日を比べ、変更されたものを毎月の対象に入れる |
| 点検用のアカウントの権限が広すぎる | script.projects.readonly など読み取りだけのスコープにする |
| AIが直したソースを返す | 直し方は文で、2文以内にさせる |
| 指摘が自動で持ち主に飛ぶ | 担当者が確かめてから送る |
上の2行が、この構成の失敗のほとんどです。 1行目は点検のために秘密を外へ出してしまう失敗、2行目は見落としを「該当なし」として記録してしまう失敗です。どちらも、点検をしたつもりで安全が下がる形で起きます。
09セキュリティ・AIガバナンス上の注意点
この構成で扱うデータ: 部署のスクリプトのソース、その中にある社内のシステムの接続先、表やフォルダのID、メールアドレス、そして直書きされた認証情報です。
- 認証情報の値を外へ出さない … 文字列の検索で見つけた値は伏せてから API に渡します。検索で見つからなかった直書きは、そのまま送られる可能性があることを前提にし、API の利用の条件を確かめておきます
- 点検の権限を読み取りに限る … Apps Script API は
script.projects.readonly、リポジトリも読み取りだけにします - 秘密の置き場所の決まりを先に作る … Apps Script のスクリプトのプロパティは、公式のガイドで「開発者の外部データベースのユーザー名とパスワード」のようなアプリ全体の設定の置き場所の例とされています。ただし、ガイドには値の暗号化や保護についての記載はありません。どの秘密をどこに置くかは、情報システム部が決まりとして示します
- この構成は脆弱性の診断を代替しません … 8つの観点は、部署のスクリプトでよく起きる危ない書き方に絞ったものです。外部に公開するシステムや個人情報を大量に扱う処理は、別に診断を受けます
- 止めるかどうかは人が決める …
highが出ても、スクリプトを止めるかは業務への影響を見て情報システム部と部署が決めます - 持ち主を責める道具にしない … 指摘の件数を人事の評価に使いません。部署のスクリプトづくりを続けてもらうことが、そもそもの目的です
誤りが起きた場合のリスクは、危ない書き方を見落とすことと、点検のために秘密を外へ出すことの2つです。 前者は unknown に倒す指示と8個の数の確認で、後者は値を伏せる前処理で防ぎます。
10まず何から始めるか
1週目:観点の表と直し方の決まりを作る
8つの観点ごとに、該当する例、重さの基準、直し方の決まりを1枚にまとめます。Apps Script はスクリプトのプロパティ、Python は環境変数か秘密情報の置き場所、のように、置き場所の決まりもここで決めます。
2週目:10本で試す
過去に指摘したことのあるスクリプトを含む10本で、第8章の手順を試します。AIが過去の指摘と同じ観点を拾うか、8つ以外の指摘が増えないかを最優先で見ます。
3週目:台帳と共有を整える
台帳の目的の欄を書き直してもらい、Apps Script のプロジェクトを点検用のアカウントに共有してもらいます。 共有されていないものの一覧を部署ごとに出します。
4週目:収集のプログラムを作る
台帳を読み、ソースを取り出し、Bandit を動かし、Claude API で判定して一覧に書くところまでを作ります。この時点では持ち主への連絡は手で送り、判定の当たり外れを記録します。
2か月目: 毎月1日の定時実行に切り替え、high と unknown の件数を数えます。3か月目以降: 1本30分が何分になったかを実測し、部署ごとの high の件数を研修の題材に使います。unknown が全体の1割を切り、点検の後の変更が毎月必ず点検に入るようになった時点で、この構成は完成です。
11関連ユースケース
12この仕組みを理解するための記事
13技術仕様の確認日・参考情報
| 確認した内容 | 情報源 | 確認日 |
|---|---|---|
構造化出力が output_config.format に type: "json_schema" とスキーマを渡す形であり、Claude API で一般提供(GA)であること。数値の範囲、文字列の長さ、minItems が0か1を超える配列の制約に対応せず、additionalProperties は false が必要なこと。拒否や max_tokens の打ち切りで形どおりに返らないことがあること | Claude API ドキュメント: Structured outputs | 2026-10-12 |
GET https://script.googleapis.com/v1/projects/{scriptId}/content でプロジェクトの中身を返すこと。files のうち1つが「appsscript」という名前の JSON のマニフェストであること。スコープが script.projects か script.projects.readonly であること。versionNumber を付けなければ HEAD が返ること | Apps Script API: projects.getContent | 2026-10-12 |
File に name、type、source、lastModifyUser、updateTime があること。type が SERVER_JS、HTML、JSON(マニフェストのみ)であること | Apps Script API: File | 2026-10-12 |
| スクリプトのプロパティが、開発者の外部データベースのユーザー名とパスワードのようなアプリ全体の設定に使われるとされていること。値の暗号化や保護の記載が無いこと | Apps Script: Properties Service | 2026-10-12 |
| Bandit が Python のコードのよくあるセキュリティの問題を探すツールで、各ファイルの AST を作ってプラグインを動かし、報告を出すこと | Bandit documentation | 2026-10-12 |
| B105/B106(パスワードの直書き)、B110/B112(try-except-pass/continue)、B113(タイムアウトの無い通信)、B501(証明書の検証の無効化)、B602(shell=True)のテストがあること | Bandit: Test Plugins | 2026-10-12 |
| 報告の形に csv、html、json、sarif、text、xml、yaml などがあること | Bandit: Report Formatters | 2026-10-12 |
スクリプトを使い続けてよいか、止めるかは、自社の情報システム部と部署が業務への影響を見て判断してください。 本記事の8つの観点は構成の例であり、脆弱性の診断に代わるものではありません。
実装ステータス:構成例。 公開仕様に基づいて設計した構成であり、当社で実際に構築・検証したものではありません。工数の数値はモデル条件による試算です。
自社の業務に使えるAI活用候補を整理します
このユースケース(UC-1282)についてのご相談はこちらから。
