#!/usr/bin/env python3
"""
GCB 政府組態基準檢測工具 - Apache HTTP Server 2.4 (TWGCB-04-007)

用法:
  sudo python3 gcb_check_apache.py
  sudo python3 gcb_check_apache.py --exclusion exclusion.json

參數:
  --exclusion FILE    排除清單 JSON（從管理平台匯出）
"""
import json, subprocess, socket, datetime, sys, os, argparse

def load_checks(json_path):
    with open(json_path, 'r', encoding='utf-8') as f:
        items = json.load(f)
    return [it for it in items if it.get('check_cmd')]

def load_exclusions(exc_path):
    if not exc_path or not os.path.exists(exc_path):
        return set()
    with open(exc_path, 'r', encoding='utf-8') as f:
        data = json.load(f)
    return set(item['id'] for item in data.get('excluded_items', []))

def run(cmd):
    try:
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=15)
        output = (r.stdout + r.stderr).strip()
        for line in reversed(output.split('\n')):
            if 'PASS' in line: return 'pass', output
            if 'FAIL' in line: return 'fail', output
        return 'fail', output
    except Exception as e:
        return 'error', str(e)

def main():
    parser = argparse.ArgumentParser(description='GCB Apache 2.4 檢測工具')
    parser.add_argument('gcb_json', nargs='?', default=None, help='GCB 定義檔路徑')
    parser.add_argument('--exclusion', '-e', default=None, help='排除清單 JSON')
    args = parser.parse_args()

    if os.geteuid() != 0:
        print("[!] 請使用 sudo 執行: sudo python3 gcb_check_apache.py"); sys.exit(1)

    json_path = args.gcb_json or os.path.join(os.path.dirname(os.path.abspath(__file__)), 'apache_gcb.json')
    if not os.path.exists(json_path):
        print(f"[!] 找不到 GCB 定義檔: {json_path}"); sys.exit(1)

    checks = load_checks(json_path)
    exclusions = load_exclusions(args.exclusion)

    active_checks = [c for c in checks if c['id'] not in exclusions]
    excluded_checks = [c for c in checks if c['id'] in exclusions]

    print(f"\n{'='*65}")
    print(f"  GCB 檢測工具 - Apache HTTP Server 2.4 (TWGCB-04-007)")
    print(f"  主機: {socket.gethostname()}")
    print(f"  時間: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"  檢測項目: {len(active_checks)} 項  |  排除: {len(excluded_checks)} 項")
    print(f"{'='*65}\n")

    results = []
    total = len(active_checks)
    for i, c in enumerate(active_checks, 1):
        status, output = run(c['check_cmd'])
        icon = '\u2705' if status=='pass' else ('\u274c' if status=='fail' else '\u26a0\ufe0f')
        print(f"  [{i:3d}/{total}] {icon} {c['id']} {c['name']} -> {status.upper()}")
        result = {"id":c['id'],"category":c.get('category',''),"name":c.get('name',''),
                  "expected":c.get('expected',''),"status":status,"output":output[:200]}
        if status == 'fail':
            result["remediation"] = c.get('remediation', c.get('method', '請參閱 GCB 說明文件'))
        results.append(result)

    for c in excluded_checks:
        results.append({"id":c['id'],"category":c.get('category',''),"name":c.get('name',''),
                        "expected":c.get('expected',''),"status":"excluded","output":"已排除"})

    p=sum(1 for r in results if r['status']=='pass')
    f=sum(1 for r in results if r['status']=='fail')
    e=sum(1 for r in results if r['status']=='error')
    ex=sum(1 for r in results if r['status']=='excluded')
    rate=round(p/total*100,1) if total else 0

    report={"type":"apache","gcb_id":"TWGCB-04-007","title":"Apache HTTP Server 2.4 政府組態基準檢測報告",
        "hostname":socket.gethostname(),"scan_time":datetime.datetime.now().isoformat(),
        "summary":{"total":total,"pass":p,"fail":f,"error":e,"excluded":ex,"compliance_rate":rate},
        "results":results}

    fname=f"gcb_apache_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
    with open(fname,'w',encoding='utf-8') as fp: json.dump(report,fp,ensure_ascii=False,indent=2)

    print(f"\n{'='*65}")
    print(f"  \u2705 通過: {p}/{total} ({rate}%)")
    print(f"  \u274c 不通過: {f}  |  \u26a0\ufe0f 錯誤: {e}  |  \u23ed\ufe0f 排除: {ex}")
    if f > 0:
        print(f"\n  --- 不通過項目修復建議 ---")
        for r in results:
            if r['status']=='fail' and r.get('remediation'):
                print(f"\n  [{r['id']}] {r['name']}")
                for line in r['remediation'].split('\n'):
                    print(f"    > {line}")
    print(f"\n  \U0001f4c4 報表: {fname}")
    print(f"{'='*65}\n")

if __name__=='__main__': main()
