自定义报告模版
使用变量 & 执行js代码
模版中使用 {{.....}} 作为命令分隔符。您可以在模板中添加变量,如:{{reportName}}、{{testBasis[0]}}、{{vulList[0].vulName}},也可以通过exec标志来执行js运算。
以下表达式将在您的浏览器中运行:
javascript
{{exec
myFun = () => Math.random()
}}For循环:
javascript
{{FOR t IN testBasis}}
{{$t}}
{{END-FOR t}}在列表中使用:
javascript
----------------------------------------------------------
| 名称 | 备注 |
----------------------------------------------------------
| {{FOR t IN target}} | |
----------------------------------------------------------
| {{$t[0]}} | {{$t[1]}} |
----------------------------------------------------------
| {{END-FOR t}} | |
----------------------------------------------------------If判断:
javascript
{{IF supplierType === 'company'}}
{{supplierCompanyName}}
{{END-IF}}默认参数
系统默认提供如下参数供模版调用:
点击展开参数
| 参数名 | 描述 | 类型 |
|---|---|---|
| reportId | 报告编号 | string |
| reportName | 报告名称 | string |
| projectName | 项目名称 | string |
| systemName | 系统名称 | string |
| testTime | 测试时间 | string[] |
| reportTime | 编写报告时间 | string |
| retestTime | 复测时间 [仅复测] | string |
| testGroup | 测试成员 | string |
| author | 作者 | string |
| dangerLevel | 威胁等级 | '高危'|'中危'|'低危' |
| summary | 测试总结 | string |
| target | 目标 | [string[]] |
| customerCompanyName | 提测方公司名称 | string |
| customerName | 提测方联系人 | string |
| customerPhone | 提测方联系电话 | string |
| customerEmail | 提测方联系邮箱 | string |
| customerAddress | 提测方联系地址 | string |
| supplierType | 测试方主体类型 | 'company'|'personal' |
| supplierCompanyName | 测试方公司名称 | string |
| supplierCompanyAddress | 测试方公司联系地址 | string |
| supplierName | 测试方联系人 | string |
| supplierPhone | 测试方联系电话 | string |
| supplierEmail | 测试方联系邮箱 | string |
| testTools | 测试工具 | [string[]] |
| testBasis | 测试依据 | [string] |
| vulList | 漏洞列表 | [] |
| vulListIndexObj | 根据类型的漏洞列表索引 | {} |
| coverSignature | 封面测试主体名称 | string |
| vulHighRiskSum | 高危漏洞数量 | int |
| vulMediumRiskSum | 中危漏洞数量 | int |
| vulLowRiskSum | 低危漏洞数量 | int |
| vulRetestHighRiskSum | 高危漏洞复测后剩余数量 [仅复测] | int |
| vulRetestMediumRiskSum | 中危漏洞复测后剩余数量 [仅复测] | int |
| vulRetestLowRiskSum | 低危漏洞复测后剩余数量 [仅复测] | int |
| vulHighRiskList | 高危漏洞列表 | [] |
| vulMediumRiskList | 中危漏洞列表 | [] |
| vulLowRiskList | 低危漏洞列表 | [] |
| vulTypeObj | 全部漏洞类型 | {} |
自动目录
如果您需要在模版中插入目录,并且在报告导出后的第一次打开自动更新,需要在上传模版前进行如下操作:
- 在文档中插入常规目录
- 使用如下Python脚本对文档进行修剪
脚本用法:
shell
python3 -m pip install lxml # 安装XML解析依赖
python3 docx-tidy.py ~/Desktop/xxx.docx # 对docx进行修剪点击展开脚本代码:docx-tidy.py
python
import io
import os
import sys
import zipfile
from typing import IO
from lxml import etree
def tidy_setting(setting_content: IO[bytes]) -> str:
tree = etree.parse(setting_content)
root = tree.getroot()
namespaces = {
'w': root.nsmap['w'],
}
update_fields_elem = tree.xpath('/w:settings/w:updateFields', namespaces=namespaces)
if not update_fields_elem:
settings_elem = tree.xpath('/w:settings', namespaces=namespaces)[0]
update_fields_elem = etree.Element('{' + namespaces['w'] + '}updateFields')
update_fields_elem.set('{' + namespaces['w'] + '}val', 'true')
settings_elem.append(update_fields_elem)
return etree.tostring(tree, xml_declaration=True, encoding='UTF-8', standalone='yes').decode()
return ""
def tidy_document(document_content: IO[bytes]) -> str:
tree = etree.parse(document_content)
root = tree.getroot()
namespaces = {
'w': root.nsmap['w'],
}
hyperlink_fields_elem = tree.xpath('//w:hyperlink', namespaces=namespaces)
if hyperlink_fields_elem:
for hyperlink in hyperlink_fields_elem:
hyperlink.getparent().remove(hyperlink)
return etree.tostring(tree, xml_declaration=True, encoding='UTF-8', standalone='yes').decode()
return ""
if __name__ == '__main__':
docx_path = ''
if docx_path == "":
if len(sys.argv) == 2:
docx_path = sys.argv[1]
else:
print("请指定文件")
exit()
if not os.path.isfile(docx_path):
print("文件不存在")
exit()
with zipfile.ZipFile(docx_path) as z:
with io.BytesIO() as i:
i.write(z.read('word/settings.xml'))
i.seek(0)
new_setting_content = tidy_setting(i)
with io.BytesIO() as i:
i.write(z.read('word/document.xml'))
i.seek(0)
new_document_content = tidy_document(i)
with io.BytesIO() as buffer:
with zipfile.ZipFile(buffer, 'w') as mem_zip:
for item in z.infolist():
if item.filename == 'word/settings.xml' and new_setting_content != "":
mem_zip.writestr(item, new_setting_content)
elif item.filename == 'word/document.xml' and new_document_content != "":
mem_zip.writestr(item, new_document_content)
else:
mem_zip.writestr(item, z.read(item.filename))
with open(docx_path, 'wb') as output_file:
output_file.write(buffer.getvalue())
print('Done')