from __future__ import annotations

import os
from pathlib import Path

from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Cm, Pt

import build_official_v3 as fmt


OUT = Path(
    os.environ.get(
        "CODE_AUDIT_V4_OUTPUT_DIR",
        str(Path.home() / "Desktop/等保整改证明材料-20260916/01-代码审计/标准报告参考版V4"),
    )
)
REPORT = OUT / "智慧营养健康餐厅管理系统重点代码安全审查报告.docx"
RECORD = OUT / "智慧营养健康餐厅管理系统代码安全审查记录及证明材料.docx"

BRANCH = "codex/csfzx-staff-info-horizontal-auth-20260824"
COMMIT = "3428f1522d9b7c1a3dfed1c372963e0e15c25e44"
TREE = "86158685b3dddcaab82a04250ab48b5a175a146d"
FILE_HASH = "5cc6b23a99a3990f94c236b7d99378194c61a48057b101c460b55a5f5c22368c"


def page_field(paragraph) -> None:
    paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = paragraph.add_run("第 ")
    fmt.style_run(run, size=10.5)
    fld = OxmlElement("w:fldSimple")
    fld.set(qn("w:instr"), "PAGE")
    paragraph._p.append(fld)
    run = paragraph.add_run(" 页 共 ")
    fmt.style_run(run, size=10.5)
    fld = OxmlElement("w:fldSimple")
    fld.set(qn("w:instr"), "NUMPAGES")
    paragraph._p.append(fld)
    run = paragraph.add_run(" 页")
    fmt.style_run(run, size=10.5)


def new_doc(title: str, subject: str) -> Document:
    doc = Document(fmt.SOURCE)
    fmt.clear_body(doc)
    fmt.configure_document(doc)
    section = doc.sections[0]
    section.top_margin = Cm(2.2)
    section.bottom_margin = Cm(2.1)
    section.left_margin = Cm(2.4)
    section.right_margin = Cm(2.4)
    section.footer_distance = Cm(1.1)
    section.footer.is_linked_to_previous = False
    footer = section.footer.paragraphs[0]
    page_field(footer)
    doc.core_properties.title = title
    doc.core_properties.subject = subject
    return doc


def heading(doc: Document, text: str, *, size=16, before=9, after=4) -> None:
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.LEFT
    p.paragraph_format.space_before = Pt(before)
    p.paragraph_format.space_after = Pt(after)
    p.paragraph_format.keep_with_next = True
    r = p.add_run(text)
    fmt.style_run(r, font=fmt.HEADING_FONT, size=size)


def body(doc: Document, text: str, *, size=14, line=24, first=True) -> None:
    fmt.add_text(doc, text, first_line=first, line_pt=line, size=size)


def cover(doc: Document, title: str, subtitle: str) -> None:
    p = doc.add_paragraph()
    p.paragraph_format.space_before = Pt(95)
    p.paragraph_format.space_after = Pt(24)
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = p.add_run(title)
    fmt.style_run(r, font=fmt.TITLE_FONT, size=24)
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.paragraph_format.space_after = Pt(70)
    r = p.add_run(subtitle)
    fmt.style_run(r, font=fmt.HEADING_FONT, size=17)
    fmt.add_table(
        doc,
        ["项目", "内容"],
        [
            ["系统名称", "智慧营养健康餐厅管理系统"],
            ["审查性质", "指定源代码快照重点安全审查"],
            ["审查日期", "2026年9月10日"],
            ["编制单位", "北京康比特体育科技股份有限公司"],
        ],
        [4.0, 11.5],
        font_size=12,
    )
    fmt.add_text(doc, "北京康比特体育科技股份有限公司\n（盖章）\n2026年9月10日",
                 first_line=False, align=WD_ALIGN_PARAGRAPH.RIGHT, line_pt=25, before=35, size=15)


def build_report() -> None:
    doc = new_doc("智慧营养健康餐厅管理系统重点代码安全审查报告", "网络安全限期整改代码审查材料")
    cover(doc, "智慧营养健康餐厅管理系统\n重点代码安全审查报告", "网络安全限期整改证明材料")

    fmt.add_page_break(doc)
    fmt.add_title(doc, "智慧营养健康餐厅管理系统", "重点代码安全审查报告")
    heading(doc, "一、结果摘要")
    body(doc, "针对网络安全监督检查指出的系统上线前未开展代码审计问题，我单位于2026年9月10日对指定源代码快照开展重点代码安全审查，检查身份鉴别、访问控制、输入与查询、敏感信息、会话令牌和口令存储等重点环节，并核对历史修复、定向测试及服务器目标文件。")
    fmt.add_table(
        doc,
        ["摘要项目", "审查结果"],
        [
            ["审查对象", "智慧营养健康餐厅管理系统指定源代码快照"],
            ["版本标识", f"分支：{BRANCH}\n提交：{COMMIT}"],
            ["实际范围", "登录与会话、人员接口、日期配餐、模型与校验、公共安全逻辑、系统及生产配置、历史专项记录"],
            ["发现结果", "形成6类待整改事项；没有关闭证据的事项均保持开放状态"],
            ["已核验事项", "历史人员详情接口子问题已有修复提交和定向测试；线上与本地Staff.php文件哈希一致"],
            ["总体结论", "已完成指定快照重点范围审查，不等同全代码库基线审查、第三方独立审计或生产全系统验证"],
        ],
        [4.0, 11.5],
        font_size=10.5,
    )
    heading(doc, "二、审查对象、日期与范围")
    body(doc, f"审查日期为2026年9月10日。审查快照分支为{BRANCH}，提交号为{COMMIT}，树对象为{TREE}。审查开始时工作树为干净状态，且该分支与同名远端分支一致。")
    body(doc, "本次覆盖管理端登录与会话逻辑、人员信息相关接口、日期配餐查询、人员模型与请求校验、公共安全逻辑及生产配置等重点区域。未建立整站当前生产部署与上述Git提交的完整版本映射，未执行全量动态接口测试、依赖库全面审查或第三方独立审计。")

    heading(doc, "三、实际方法与过程")
    body(doc, "本次采用有限范围人工辅助静态检查：先固定审查快照和工作树状态，再围绕高风险入口追踪认证、授权、输入处理、查询构造、令牌保护、口令存储和配置加载逻辑；对已有修复提交核对差异和历史测试记录，并对服务器目标文件执行哈希回读。")
    body(doc, "审查中同时确认了登录失败锁定、密码过期与复杂度检查、认证Cookie安全属性，以及人员详情接口在查询前执行对象范围校验等已有控制。上述控制不抵销本次发现，也不代表其他接口已通过复核。")
    heading(doc, "四、发现结果")
    fmt.add_table(
        doc,
        ["编号", "发现事项", "影响概述", "截至9月10日状态"],
        [
            ["01", "二次认证状态由客户端Cookie参与判定", "认证状态与用户、会话绑定不足，可能削弱二次认证可信度", "开放，待整改复核"],
            ["02", "人员更新、删除及展示等接口对象级授权不完整", "可能造成越权访问或操作其他对象", "详情子问题已修复；其余开放"],
            ["03", "日期配餐查询存在动态SQL拼接路径", "异常输入可能影响查询边界并形成注入风险", "开放，待参数化及动态复核"],
            ["04", "版本库跟踪配置存在明文敏感信息风险", "可能造成凭据暴露及横向影响", "开放，待移除、轮换及历史评估"],
            ["05", "会话令牌使用固定AES-CBC密钥与IV，部分路径缺少状态复核", "令牌机密性和失效控制可能不足", "开放，待改造及环境核实"],
            ["06", "口令采用双重MD5及低强度随机盐", "抵抗离线破解能力不足", "开放，待兼容迁移及回归"],
        ],
        [1.1, 5.6, 5.2, 3.5],
        font_size=9.5,
    )

    fmt.add_page_break(doc)
    heading(doc, "五、问题处置与复核结果")
    body(doc, f"历史人员详情接口授权问题对应修复提交为{COMMIT}，修改文件为application/p/controller/Staff.php。历史定向测试记录显示3项测试、10项断言通过；同批SecurityFixesTest仍有4项失败、1项错误，因此该结果只证明单接口定向回归，不作全量测试通过结论。")
    body(doc, f"2026年9月10日服务器回读显示，线上与本地Staff.php文件SHA-256均为{FILE_HASH}。该结果证明目标文件内容一致，不证明整站Git版本一致。受限账号线上合法对象、越权对象对照复测尚未完成。")
    body(doc, "其余5类发现尚未取得完整的修复提交、测试、复核和上线映射证据，继续保持开放状态。整改工作将依照安全开发、变更、授权审批等制度逐项登记，完成修复、测试、复核和记录留存。")
    heading(doc, "六、结论及后续安排")
    body(doc, "截至2026年9月10日，我单位已完成指定源代码快照的首轮重点安全审查，形成可追溯的对象、版本、范围、方法、发现和部分复核记录；6类发现中，仅历史人员详情接口子问题取得局部修复和验证证据，不能据此认定全部源码审查复核通过。")
    body(doc, "我单位将继续按照发现清单逐项整改和复核，补充修复提交、测试结果、上线版本映射和审批记录，并于2026年9月16日前提交本阶段证明材料。未闭环事项持续据实跟踪。")
    body(doc, "附件：《智慧营养健康餐厅管理系统代码安全审查记录及证明材料》", first=False)
    fmt.add_text(doc, "北京康比特体育科技股份有限公司\n（盖章）\n2026年9月10日",
                 first_line=False, align=WD_ALIGN_PARAGRAPH.RIGHT, line_pt=24, before=16, size=14)
    doc.save(REPORT)


FINDINGS = [
    ("01", "二次认证状态由客户端Cookie参与判定", "application/p/logic/Login.php（身份鉴别与会话处理相关逻辑）", "二次认证完成状态由客户端可写Cookie参与判定，且与当前用户、会话的服务端绑定不足。", "可能削弱二次认证控制的可信度。", "改为服务端保存一次性、短时、与用户及会话绑定的认证状态；完成正常、过期、篡改和跨账号场景复测。", "开放，待整改复核"),
    ("02", "人员接口对象级授权不完整", "application/p/controller/Staff.php（info、update、delete、sportShow等相关接口）", "历史详情接口已增加UUID范围校验，但更新、删除和展示等路径仍缺少一致的目标对象或部门范围授权校验。", "可能造成水平越权读取或操作其他对象。", "统一在服务端执行对象级授权校验并默认拒绝；覆盖本人、同部门、跨部门、无权限账号和失效账号测试。", "详情子问题已修复；其余开放"),
    ("03", "日期配餐查询存在动态SQL拼接路径", "application/p/controller/DateMenu.php；application/p/logic/DateMenu.php；application/model/Staff.php", "请求值进入动态查询拼接路径，现有证据未证明全部输入均被参数化或严格白名单限制。", "异常输入可能影响查询边界并形成注入风险。", "使用参数化查询或严格白名单映射，补充边界输入、特殊字符和异常输入动态复测。", "开放，待参数化及动态复核"),
    ("04", "版本库跟踪配置存在明文敏感信息风险", "application/config/prod/zhct_csfzx/及相关应用配置", "已识别Git跟踪配置中的明文敏感信息风险；对外材料不展示具体值。", "凭据泄露可能扩大数据库、服务或其他环境暴露范围。", "将敏感值迁移至受控密钥或环境配置，轮换已暴露凭据并评估历史版本影响。", "开放，待移除、轮换及历史评估"),
    ("05", "会话令牌保护及账号状态复核不足", "application/p/logic/Login.php及公共会话令牌处理逻辑", "令牌处理使用固定AES-CBC密钥与IV，部分访问路径缺少活动会话或账号状态复核。", "令牌失效、重放和密钥泄露后的控制能力可能不足。", "采用受控密钥管理和随机IV或现代认证加密；统一校验令牌时效、会话状态和账号状态。", "开放，待改造及环境核实"),
    ("06", "口令摘要算法和盐值强度不足", "application/p/logic/Login.php及口令校验、更新相关逻辑", "现有口令处理采用双重MD5及低强度随机盐，难以满足现代口令存储抗破解要求。", "数据库摘要泄露后抵抗离线破解能力不足。", "制定兼容迁移方案，逐步采用专用口令哈希算法和充分成本参数，并验证登录、改密、重置和旧账号升级。", "开放，待兼容迁移及回归"),
]


def finding_block(doc: Document, item) -> None:
    no, title, location, desc, impact, rec, status = item
    heading(doc, f"{no}　{title}", size=14, before=8, after=3)
    fmt.add_table(
        doc,
        ["项目", "记录"],
        [
            ["位置或对象", location],
            ["检查记录", desc],
            ["可能影响", impact],
            ["处置建议", rec],
            ["真实状态", status],
        ],
        [3.0, 12.5],
        font_size=9.5,
    )


def build_record() -> None:
    doc = new_doc("智慧营养健康餐厅管理系统代码安全审查记录及证明材料", "网络安全限期整改代码审查记录")
    cover(doc, "智慧营养健康餐厅管理系统\n代码安全审查记录及证明材料", "与重点代码安全审查报告配套使用")

    fmt.add_page_break(doc)
    fmt.add_title(doc, "智慧营养健康餐厅管理系统", "代码安全审查记录及证明材料")
    heading(doc, "一、审查记录基本信息")
    fmt.add_table(
        doc,
        ["项目", "记录内容"],
        [
            ["审查日期", "2026年9月10日"],
            ["审查对象", "智慧营养健康餐厅管理系统指定源代码快照"],
            ["版本", f"分支：{BRANCH}\n提交：{COMMIT}\n树对象：{TREE}"],
            ["实施方式", "有限范围人工辅助静态检查；核对历史提交、定向测试记录和服务器目标文件哈希"],
            ["范围", "登录与会话、人员接口、日期配餐、模型与校验、公共安全逻辑、系统及生产配置、历史专项记录"],
            ["限制", "未完成全代码库基线审查、全量动态接口测试、整站生产版本映射或第三方独立审计"],
        ],
        [3.6, 11.9],
        font_size=10.5,
    )
    heading(doc, "二、检查过程记录")
    body(doc, "固定分支、提交、树对象和工作树状态后，围绕认证、授权、输入、查询、会话、口令与配置加载路径进行代码追踪；对历史人员详情接口修复核对提交和定向测试；对服务器Staff.php文件进行SHA-256回读。")
    body(doc, "检查同时确认登录失败锁定、密码过期与复杂度检查、认证Cookie安全属性及人员详情接口对象范围校验等已有控制。发现与既有控制分别记录，未以制度条文或局部成功路径替代实际复核。")

    fmt.add_page_break(doc)
    heading(doc, "三、发现记录（一）")
    for item in FINDINGS[:2]:
        finding_block(doc, item)

    fmt.add_page_break(doc)
    heading(doc, "四、发现记录（二）")
    for item in FINDINGS[2:4]:
        finding_block(doc, item)

    fmt.add_page_break(doc)
    heading(doc, "五、发现记录（三）")
    for item in FINDINGS[4:]:
        finding_block(doc, item)

    fmt.add_page_break(doc)
    heading(doc, "六、历史修复与验证记录")
    fmt.add_table(
        doc,
        ["证据", "回读结果", "结论边界"],
        [
            ["仓库状态", f"分支{BRANCH}；HEAD {COMMIT}；工作树干净并与同名远端分支一致。", "证明审查快照身份，不证明当前整站生产版本"],
            ["历史修复", "人员详情接口在查询前增加目标UUID范围校验；修改文件application/p/controller/Staff.php。", "证明该历史子问题已有代码修复"],
            ["定向测试", "历史记录为3项测试、10项断言通过；同批SecurityFixesTest为4项失败、1项错误。", "仅证明人员详情接口定向回归，不是全量测试通过"],
            ["部署文件", f"线上与本地Staff.php文件SHA-256均为{FILE_HASH}。", "仅证明目标文件一致，不证明整站Git版本一致"],
            ["线上复测", "受限账号合法对象、越权对象专项对照复测尚未完成。", "补齐前不作该接口生产闭环结论"],
        ],
        [3.0, 8.2, 4.3],
        font_size=9.5,
    )
    heading(doc, "七、制度执行依据")
    body(doc, "《安全开发管理规定》覆盖安全需求、阶段评审、源代码与版本一致性、扫描与人工检查、问题整改及上线前安全测试和安全评审；《组织及岗位职责管理规定》《变更管理规定》《授权和审批管理规定》提供职责、变更、测试和审批框架。制度证明管理要求，不替代本次检查、修复、测试和服务器回读记录。", size=13, line=22)
    heading(doc, "八、仍需完成的事项")
    body(doc, "继续完成6类开放发现的修复、测试、复核和上线映射；补充受限账号线上合法对象、越权对象专项复测；形成可回读的变更、审批及发布记录，并按回函于2026年9月16日前提交本阶段证明材料。上述事项未形成证据前，保持开放状态。", size=13, line=22)
    doc.save(RECORD)


def main() -> None:
    OUT.mkdir(parents=True, exist_ok=True)
    build_report()
    build_record()
    print(REPORT)
    print(RECORD)


if __name__ == "__main__":
    main()
