0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIのnull修正でmemberまで許可された:小さな差分の回帰を見つける

0
Posted at

「nullでも落ちないようにしました」。AIから返った修正は、たった一行。実際にnullを渡してもエラーが出なくなったので、そのまま取り込みたくなります。

ところが、今回の提案例はエラーを消す代わりに、一般ユーザーまで許可します。直してほしかった入力で成功しても、守るべき拒否条件が残っているとは限りません。 差分が小さいほど安心、とは言えない例です。

単純化した認可関数を使って、修正対象と維持すべき動作を同じ入力表で比較します。ゴールは、AIの説明を読んで終わらず、条件式を自分の言葉に戻し、取り込めない修正を具体的な入力で示せることです。

JavaScriptの関数とGit差分を読める方が対象です。2026年11月11日にmacOS・Node.js v26.8.1で掲載コードを再実行し、以下の出力を確認しました。題材は架空データを使うローカルの関数で、本番用の認証・認可システムではありません。

修正の成功条件には「変わってはいけないこと」も入れる

元の仕様は、roleがadminの利用者だけを許可することです。問題は、userがnullだとuser.roleを読めず例外になる点です。

ここで修正依頼を「例外を消す」だけにすると、仕様の半分が抜けます。今回必要なのは、次の状態です。

入力 期待する結果 役割
{role:'admin'} true 許可していた人は引き続き許可する
{role:'member'} false 拒否していた人は引き続き拒否する
{} false 権限情報がなければ許可しない
null / undefined false 今回直したい例外を、拒否として扱う

nullの行だけが通っても、この表全体を満たしたことにはなりません。変更レビューでは、修正したい症状の隣に、壊してはいけない動作を並べます。

条件式を日本語に戻すと、意味の変化が見える

元のコードと提案例の差分は、次の一行です。

- return user.role === 'admin';
+ return Boolean(user);

元の式は「roleがadminと一致するか」です。提案例は「userがtruthyな値か」へ変わっています。JavaScriptでは空のオブジェクトもtruthyなので、memberも権限情報のないオブジェクトもtrueになります。

例外が消えた理由は、nullからプロパティを読まなくなったからです。しかし同時に、roleを見ること自体をやめています。「nullへ対応した」という説明だけでは、この二つ目の変更は見えません。

修正版のuser?.role === 'admin'は、userがnullかundefinedならroleの取得を止め、結果のundefinedとadminを比較してfalseになります。今回の仕様では、許可条件を残したまま欠損へ対応できます。

元の例外を再現し、提案と修正版を同じ入力で比べる

新しい練習用フォルダに、次をreview.mjsとして保存し、node review.mjsを実行します。追加パッケージや外部アカウントは不要です。

review.mjs
import assert from 'node:assert/strict';
const before = user => user.role === 'admin';
const proposed = user => Boolean(user);
const fixed = user => user?.role === 'admin';
assert.throws(() => before(null), TypeError);
for (const [label,input,expected] of [
  ['admin',{role:'admin'},true], ['member',{role:'member'},false],
  ['empty',{},false], ['null',null,false], ['undefined',undefined,false]
]) {
  console.log(`${label}: proposed=${proposed(input)}, fixed=${fixed(input)}, expected=${expected}`);
  assert.equal(fixed(input),expected);
}
assert.equal(proposed({role:'member'}),true);
console.log('PASS: null failure reproduced; proposed authorization regression detected; fixed 5 cases');

実結果です。

$ node review.mjs
admin: proposed=true, fixed=true, expected=true
member: proposed=true, fixed=false, expected=false
empty: proposed=true, fixed=false, expected=false
null: proposed=false, fixed=false, expected=false
undefined: proposed=false, fixed=false, expected=false
PASS: null failure reproduced; proposed authorization regression detected; fixed 5 cases

assertのどれかが期待と食い違うと、AssertionErrorが表示されて0以外の終了コードで止まります。最後のPASS行まで出れば、すべての検査を通過しています。

注目するのは、memberとemptyの行です。提案例はtrue、修正版と期待値はfalseです。nullの例外を再現できたことだけでなく、「修正により以前の拒否が壊れた」ことを観察できます。

最後のPASSは、危険な提案例を採用してよいという意味ではありません。元の不具合を再現し、提案例の回帰を検出し、修正版の5ケースを確認した、という教材の検査結果です。

自分の差分では、変わった条件に入力をぶつける

レビューする変更が大きくても、最初に一つの条件式へ絞れます。変更前は何を判定していたか、変更後は何を判定するかを一文ずつ書き、二つの答えが異なる入力を探します。

今回なら「roleがadmin」と「オブジェクトが存在する」が違うので、memberや空オブジェクトが候補になります。上限チェックなら、上限ちょうどと超過した入力。未入力対応なら、空文字と0などが候補です。入力の選び方が、変更の意味から導けます。

Gitではgit status --shortで対象ファイルを確認し、未ステージはgit diff --、ステージ済みはgit diff --cached --で読みます。未追跡の新規ファイル(statusで??の行)は通常のdiffに出ないため、直接開いて確認します。

git diff --checkは行末空白やコンフリクトマーカーの検出には役立ちますが、今回の認可条件の変化は見つけません(今回の一行差分に対しても終了コード0で何も報告しません)。形式チェックと、条件を読んで実行する検査は役割が異なります。

期待と違ったときは、テストを緩めず変更目的へ戻る

観察した結果 判断と次の行動
nullは直ったがmemberがtrue 拒否条件の回帰。修正をそのまま取り込まず、許可条件を維持する変更へ戻す
adminまでfalse 権限値の表記や入力構造が想定と合っているか確認する。許可範囲を広げてごまかさない
練習コードは通るが実APIで壊れる 関数に渡るデータの出所・型・呼び出し場所を調べる
AIの変更に関係ない設定や依存が混ざる null対応に必要な理由を確認し、目的に必要な差分へ絞る

この表は実装へ適用する際の調査順です。実APIの接続や認証基盤を、今回のローカル実験で検証したわけではありません。

正しい条件式でも、信頼する値の出所が違えば守れない

本例のroleは、信頼できるサーバー側の利用者情報から得たものという前提です。ブラウザから送られたrole:'admin'をそのまま信じるなら、関数の比較が正しくても認可を守れません。実サービスでは、本人確認とサーバー側の権限情報に基づく判定が必要です。

今回持ち帰りたいのは、修正版の一行だけでなく、レビューの見方です。直したい失敗を一つ、維持したい許可と拒否を一つずつ書く。 この三つを同じ入力表で見ると、修正の説明に隠れた意味の変化を具体的に指摘できます。

参考資料(一次情報)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?