FULLALLGOL 技術ノート

「赤にならない」の前に、欠陥が本当に入ったかを数える

2026-08-31

テストの強さを確かめる一番確実な方法は、わざと壊して赤を見ることだ。
守りたかった状態を壊し、テストが落ちることを確認して初めて、そのテストは「効いている」と言える。

問題はその次にある。壊したのに赤にならなかったとき、何を疑うか。

同じ観測に、正反対の原因が 2 つある

3 本のガードを注入検証したところ、1 本だけ緑のままだった。
一瞬、「このテストは弱い、書き直そう」と思った。間違いだった。

実際には、注入に使ったシェルコマンドの引用が壊れていて、置換が 1 文字も起きていなかった
ファイルは元のまま。テストが緑なのは当たり前で、テストはむしろ正しく振る舞っていた。

「赤が出ない」には原因が 2 つある。

この 2 つは観測が完全に同じなのに、対処が正反対だ。
区別しないまま進むと、効いているテストを「弱い」と決めつけて書き直すという、
最悪の方向へ手が動く。書き直した先のテストが本当に強いかは、また同じ手順で確かめることになる。

判定の前に「注入できたこと」を数える

対処はごく単純で、置換件数を印字してから判定する

before = s.count(target)
s = s.replace(target, broken)
open(p, 'w').write(s)
after = open(p).read().count(target)
print('%d → %d (0 なら欠陥が入った)' % (before, after))

1 → 0 を目で見てから、はじめてテストを回す。今回は 1 → 1 のままだったので、
置換し直したら即座に赤になった。テストは最初から効いていた。

置換ヘルパは一致件数が期待と違ったら sys.exit(1) にしておく

注入検証は「壊す・確かめる・戻す」の 3 段で、戻し漏れも同じくらい静かに起きる

これは「検出器が母集団を見ていないのに 0 件で緑になる」問題とまったく同じ形だ。
0 件は合格ではなく「測れていない」であり、件数を印字しない手順は信じられない。
注入検証にとっての母集団は、置換対象が何箇所あったかである。

壊れたのはコードではなく、引用だった

注入が失敗した直接の原因も書いておく。python3 -c "..." に、
クォートと改行とバックスラッシュの混ざった置換文字列を渡していた。
シェルが先に展開してしまい、Python に届いた時点で別の文字列になっていた。

複雑な引用をシェル経由で渡さない。 ヒアドキュメントで渡す。

python3 - <<'PY'
target = "if (!isAdmin(user)) return deny();"
...
PY

終端子を 'PY' とクォートすると、シェルは中身を一切展開しない。
<<PY とクォート無しで書くと変数やコマンド置換が展開されるので、
クォートの有無だけで結果が変わる。ここも「気づけないと静かに間違う」側の分岐だ。

まとめ