งูต้นฉบับตรวจประตูเครื่องมือหลายบานตามทางเดินและเลือกเฉพาะบานที่มีกลอนไฟสีอำพัน

Python shutil.which: หา executable ก่อนเรียก subprocess อย่างมีขอบเขต

พี่โชว์Engineering

โปรแกรมที่ต้องเรียกเครื่องมือภายนอกควรแยกสองคำถาม: คำสั่งนี้อนุญาตหรือไม่ และ executable อยู่ที่ไหน shutil.which ช่วยตอบข้อหลังโดยค้นตาม PATH หรือ path ที่เรากำหนด แต่ไม่ใช่เครื่องตรวจความน่าเชื่อถือของไฟล์ครับ

WHY: หาเจอไม่เท่ากับอนุญาตให้รัน

เหมือนพนักงานบอกว่ากุญแจอยู่ลิ้นชักไหน แต่หัวหน้าต้องตัดสินใจอีกชั้นว่าประตูนั้นเปิดได้หรือไม่ เราจึงใช้ allowlist กับชื่อคำสั่งก่อน แล้วค้นเฉพาะ directory ที่ระบบตั้งใจ ไม่รับชื่อหรือ PATH ทั้งชุดจากผู้ใช้โดยตรง

import os
import shutil
import tempfile
import unittest
from pathlib import Path


ALLOWED = {"report-tool"}


def resolve_tool(name: str, search_path: str) -> str:
    if name not in ALLOWED:
        raise ValueError("command is not allowed")
    found = shutil.which(name, path=search_path)
    if found is None:
        raise FileNotFoundError(name)
    return found


class WhichTests(unittest.TestCase):
    def test_finds_only_allowed_executable(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            tool = Path(tmp, "report-tool")
            tool.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8")
            tool.chmod(0o700)
            self.assertEqual(resolve_tool("report-tool", tmp), str(tool))
            with self.assertRaises(ValueError):
                resolve_tool("other-tool", tmp)

    def test_missing_tool(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            with self.assertRaises(FileNotFoundError):
                resolve_tool("report-tool", tmp)


if __name__ == "__main__":
    unittest.main()

HOW: ส่ง argv โดยไม่ผ่าน shell

เมื่อได้ absolute path แล้ว ให้เรียก subprocess.run([path, "--flag", value], shell=False, check=True, timeout=...) และกำหนด working directory กับ environment เท่าที่จำเป็น อย่าต่อ command string จาก input เพราะ which ไม่ป้องกัน shell injection และอย่าเชื่อไฟล์เพียงเพราะอยู่ก่อนใน PATH

ในระบบ production ควร pin directory ที่ admin ควบคุม ตรวจ owner/permission ตาม threat model และจัดการกรณีไฟล์ถูกสลับหลังตรวจแต่ก่อนรัน หากต้องการความมั่นคงสูงกว่า PATH discovery ให้กำหนด absolute executable ใน config ที่ deploy มาพร้อมระบบ

Recap

  • ตรวจ allowlist ก่อนค้น executable
  • ส่ง path= ที่ควบคุมเองเมื่อขอบเขตสำคัญ
  • เรียก subprocess ด้วย argv และ shell=False
  • ตั้ง timeout, cwd และ environment แบบขั้นต่ำ
  • อย่าถือว่า “หาเจอ” แปลว่าไฟล์เชื่อถือได้