ユーザー入力を信じるな:不可視文字が引き起こしたバグ

背景

ある日、サーバーから招待コードを受け取り、そのコードを使ってエミュレータで次の操作を行うプログラムを作成しました。初めての正式なプログラムでしたが、ユーザーの行動に対する懸念はありました(それでも不十分でした)。サーバーメッセージを受信後、メッセージの前後と中間のスペースを削除しました(このシナリオではメッセージに中間スペースが存在するべきではありません)。テストが完了し問題がなければ、安心して寝てしまいました...

問題発覚

翌朝、スマートフォンを開いたら苦情が表示されていました。

ユーザー:「ウェブサイトに入力したのに全く効果がないです、低評価です!!!」

OS:「いや、私は何度もテストしたのに、ほぼ(このほぼに注意)すべてのケースで正常に動作しているはず。どうして問題が起こるんだろう。」と返信し、「アクティベーションコードを間違えて入力したのではないか?」と尋ねました。

ユーザー:「あり得ない、絶対にあり得ない。私は直接コピー&ペーストしたんだ!」

少し不安になり、ユーザーのアクティベーションコードを尋ねてログを確認しましたが、完全に同じコードの記録は見つかりませんでした。困惑し、1文字ずつ削除して関連する記録を見つけようと試みました。努力は裏切りませんでした。

受信したアクティベーションコード: 1b 636 e 1 a
ERROR - api - 363: WebSocketメッセージ処理エラー: 'gbk' codec can't encode character '\u2006' in position 82: illegal multibyte sequence

招待コードの中のスペースを見て、自分のスペース処理コードを見て、その下のERRORログを見て、言葉を失いました。ゲームから招待コードをコピーするなぜこのような問題が発生するのか理解できませんでした。シナリオを再現したところ、LDPlayerエミュレータで以下のコマンドを実行すると、特殊文字(\\u2006)が存在するために失敗することがわかりました。

ldconsole adb --index 0 --command "shell input text '入力するテキスト'"

解決策

最終的に、コードにスペースを削除した後に特殊文字を削除する機能を追加しました。

# コード内の不可視文字を削除
async def 文字列をサニタイズ(テキスト: Union[str, any]) -> str:
    """
    すべての不可視Unicode文字(\u2006、ゼロ幅スペース、制御文字など)を削除します

    Args:
        テキスト: 入力文字列

    Returns:
        サニタイズされた文字列。非文字列型はそのまま返します
    """
    if not isinstance(テキスト, str):
        return テキスト

    # 1. NFKC正規化(特殊なスペースを通常のスペースに変換)
    テキスト = unicodedata.normalize('NFKC', テキスト)

    # 2. すべての不可視文字を削除
    # \p{C}: その他(制御文字、書式文字、サロゲートなど)
    # \p{Z}: 区切り文字(スペース、行区切りなど)
    # \u2000-\u200F, \u2028-\u202F, \u205F, \u3000, \uFEFF などを含む
    テキスト = re.sub(r'[\\p{C}\\p{Z}]', '', テキスト, flags=re.UNICODE)
    テキスト = テキスト.replace(" ",'')

    return テキスト

結論

ついにユーザー入力の処理が完了し、プログラムは正常に動作するようになりました。これからは二度とユーザーの入力を信用しないと決めました(いや、この特殊文字は一体どこから来るんだ)

(本当に解決したのだろうか、続く...)

タグ: 文字処理 バグ修正 ユーザー入力検証 Python Unicode

6月5日 17:13 投稿