1. 页面概览
1.1 是什么
「安全合规」页(对应前端源码 action/web/src/views/apollo/SecurityPage.vue,页面标题「Apollo 安全合规」)是 LightApollo 的安全与合规操作台(对齐 TAD-10)。页面用三个 Tab 拆成三块能力:合规总览(overview)用九张 summary 卡汇总项目安全态势并提供一个可手动调用的「安全门禁评估」表单;扫描与漏洞(scan)围绕制品做 SBOM 物料清单生成、漏洞扫描触发与扫描结果查看;安全策略(policies)管理策略清单与内置策略模板。整页回答的是「上生产前怎么证明安全」:先登记制品 → 生成 SBOM → 触发扫描 → 建立安全策略 → 提交资源快照做门禁预演 → 在「部署与漂移」页真正激活部署时,由同一套策略引擎在激活前强制执行门禁。 理解本页要先建立一个双评估入口的概念。安全策略引擎(deny_latest_tag / require_signature / critical_vuln / no_plaintext_secret / require_resource_limits / custom 六类规则)存在两个调用点:一是本页 Tab1 里的「安全门禁手动评估」表单,它是演示/预演入口,提交一段资源快照 JSON,后端按当前启用的策略求值并返回 allow / warn / deny 决策与命中违规清单;二是部署链路上的真实门禁——后端在「期望状态从 draft 激活为 active」之前(见第 5.4 节)对声明执行同一套 Evaluate,决策为 deny 时直接以 403 拒绝激活,把带病声明拦在生产之前。本页的手动评估能让你在不上真实部署的情况下,把资源快照喂给策略引擎观察决策,是理解「为什么那条部署会被拦下来」的最短路径。 页面的数据流大致为「读(Tab1 汇总 / Tab2 扫描结果 / Tab3 策略与模板)→ 写(新建策略、启停、删除、应用模板、触发 SBOM/扫描、门禁评估)」。输入主要是策略表单字段与资源快照 JSON、以及所选制品 id;输出是后端返回的决策/违规/报告 JSON 在卡片、徽标与表格中的渲染。项目 id 暂用常量 1(PROJECT_ID=1,default 项目),本页所有列表都挂在项目 1 下,后续才支持从 URL 取项目参数。 典型使用链路(演示视角的完整闭环):① 先在「制品管理」页登记制品(拿到 artifact id);② 回本页 Tab2 选中该制品点「生成 SBOM」再点「触发扫描」,观察漏洞报告与分级计数;③ 到 Tab3「应用」内置模板(如「禁止生产 latest 标签」)或手动新建 deny 级策略;④ 回 Tab1 在资源快照里放 {"tags":["latest"]} 之类触发 deny 的内容点「执行评估」,观察 decision=deny 与违规行;⑤ 到「部署与漂移」页激活关联该制品的期望状态声明,若声明 digest 关联了扫描出的 Critical 漏洞或声明带 latest 标签,真实部署同样会被拒绝——两个入口共享同一套策略与审计,形成「策略→门禁→审计」闭环。
1.2 核心价值/能力表
| 能力 | 说明 | 对应页面操作 |
|---|---|---|
| 合规总览 | 九张 summary 卡汇总策略数/启用数/近 30 天评估与决策计数/扫描与漏洞统计 | Tab「合规总览」顶部卡片区(GET /projects/1/compliance-report) |
| 门禁手动评估 | 提交资源快照 JSON,由启用策略求值出 allow/warn/deny 并列出违规策略 | Tab1「安全门禁手动评估(Security Gate)」表单「执行评估」 |
| SBOM 生成 | 对制品生成 SPDX 格式物料清单(同制品单条幂等覆盖) | Tab2「生成 SBOM」 |
| 漏洞扫描 | 触发内置扫描器(zy_builtin)对制品做规则与 CVE 命中检查并回写状态 | Tab2「触发扫描」 |
| 漏洞报告查看 | 单制品分级计数(Critical/High/Medium/Low)+ 条目表(severity 徽标/vuln_id/包名/版本/描述) | Tab2 漏洞报告区 |
| 项目漏洞汇总 | 项目下全部制品的扫描状态与分级汇总行 | Tab2「项目漏洞报告(项目 #1)」列表 |
| 安全策略管理 | 新建/编辑/删除策略,切换启用态,生效环境 JSON 限定 | Tab3 安全策略表 |
| 内置模板应用 | 一键应用 5 类安全基线模板,幂等创建并默认启用 | Tab3「内置策略模板(5 类安全基线)」行「应用」 |
1.3 一句话总结
本页把「制品 SBOM/扫描 → 安全策略 → 门禁决策 → 合规汇总」串成部署前的安全闭环,让策略与漏洞状态在浏览器里可观察、可预演,并强制阻断带病制品进入生产。
2. 访问入口
2.1 路由与菜单
| 项 | 值 |
|---|---|
| 路由 path | /apollo/security |
| 路由 name | ApolloSecurity |
| meta.title | Apollo 安全合规 |
| 侧边栏入口 | ApolloLayout 侧边栏「安全合规」,位于「告警自愈」之后、「配置管理」之前 |
| 前端源码 | action/web/src/views/apollo/SecurityPage.vue |
| API 客户端 | action/web/src/api/apolloClient.js |
| 路由注册 | action/web/src/router/index.js,/apollo 子路由 children 下,component: ApolloSecurityPage(import 的 SecurityPage),挂 ApolloLayout |
相邻页(侧边栏顺序):告警自愈 alerts.md(前)、配置管理 configs.md(后)、制品管理 artifacts.md(Tab2 的制品下拉数据来源)、部署与漂移 deployments.md(真实部署门禁在此页的激活/sync 动作上触发)、用户管理 admin-users.md(权限点授给角色后决定本页可见性)。
2.2 认证与权限
- 路由
requiresAuth: true:未登录访问被前端路由守卫拦到/apollo/login(登录态为独立apollo_token,向后兼容回退读取旧aip_token)。 - 后端安全端点全部注册在 protected 组,按权限点鉴权:策略/合规读取用
security:read(PermSecurityRead)、策略写与删除用security:write(PermSecurityWrite)、扫描触发/门禁评估/启停/应用模板用security:execute(PermSecurityExecute)。403 时 apolloClient 统一alert('无权限执行该操作')。 - 前端 401(apollo_token 失效)由响应拦截器清 token 并跳转
/apollo/login。 - 注意区分:门禁评估接口本身要求 security:execute,而「部署链路上的自动门禁」是后端进程内部调用(不经 HTTP 鉴权),只认策略引擎,与本页登录态无关。
2.3 端口与 API 前缀
- Apollo 后端端口 18082。Vite 开发代理把
/apollo-api前缀 rewrite 为/api转发(后端路由注册在/api/v1),客户端 baseURL/apollo-api/v1。 - 统一响应 envelope
{code,message,data,request_id}:拦截器在code===0时把response.data解包为业务数据,页面const { data } = await apiClient.get(...)直接拿业务数据;data可能是数组或{items,...}等对象,页面用toList健壮解析。 - 例外:门禁评估决策为 deny 时后端返回 HTTP 403 的 fail envelope(
code:40301),会先触发拦截器的全局alert('无权限执行该操作'),再走页面 catch(详见 4.3 与第 8 章缺陷)。
3. 界面布局
┌──────────────────────────────────────────────────────────────┐
│ Apollo 安全合规 [新建安全策略] │
│ alert-info:安全合规(TAD-10)…部署前强制安全门禁… │
│ [操作结果提示条(v-if alert.message,可「关闭」)] │
├──────────────────────────────────────────────────────────────┤
│ Tab:合规总览 | 扫描与漏洞 | 安全策略 │
├──────────────────────────────────────────────────────────────┤
│ ┌ Tab1 合规总览 ────────────────────────────────────────────┐ │
│ │ 九张 summary 卡:策略数/启用策略/评估次数(近30天)/deny决策/ │ │
│ │ warn决策/allow决策/扫描制品数/含漏洞制品/漏洞条目数 │ │
│ │ ┌ 安全门禁手动评估(Security Gate)卡片 ────────────────┐ │ │
│ │ │ 项目id(禁用=1)/资源类型select/资源id/resource_data JSON│ │ │
│ │ │ [执行评估] → 评估结果区:decision 徽标 + policy_count + │ │ │
│ │ │ evaluated_at + 违规表(策略名/规则类型/级别/原因) │ │ │
│ │ └───────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ ┌ Tab2 扫描与漏洞 ──────────────────────────────────────────┐ │
│ │ 制品漏洞扫描(SBOM / Scan):制品下拉[生成 SBOM][触发扫描] │ │
│ │ SBOM 结果块(format/生成时间/sbom_data JSON) │ │
│ │ 漏洞报告块:状态徽标 + C/H/M/L 卡 + 漏洞条目表 │ │
│ │ 项目漏洞报告(项目 #1):制品行汇总表 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ ┌ Tab3 安全策略 ────────────────────────────────────────────┐ │
│ │ 安全策略(n):ID/名称/分类/规则类型/级别/生效环境/描述/ │ │
│ │ 启用(开关按钮)/操作(编辑/删除) │ │
│ │ 内置策略模板(5 类安全基线):模板名称/分类/规则类型/级别/ │ │
│ │ 描述/操作(应用) │ │
│ └────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│ ┌ 新建/编辑安全策略弹窗(modal,@click.self 可关)───────────┐ │
│ │ 新建安全策略 | 编辑安全策略 #id(项目 #1) [关闭] │ │
│ │ 名称* 分类* 规则类型* 级别* 生效环境JSON(可选) 描述 │ │
│ │ [取消] [创建/保存] │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
各板块职责:
| 板块 | 职责 |
|---|---|
| 页头 + 顶部 alert-info | 标题「Apollo 安全合规」、按 Tab 变化的「新建安全策略」、TAD-10 功能说明、全局操作结果提示(可关闭) |
| Tab 切换条 | 合规总览 / 扫描与漏洞 / 安全策略 三个面板切换 |
| 合规总览 summary 行 | 九张卡渲染 compliance-report 的九项计数(请求失败保持原值不打扰) |
| 安全门禁手动评估卡片 | 收集资源类型/资源 id/资源快照 JSON,调用门禁接口展示 decision 与违规表 |
| 制品漏洞扫描卡片 | 制品下拉 + SBOM 生成/触发扫描,联动展示 SBOM 与漏洞报告(watch 制品自动加载) |
| 项目漏洞报告卡片 | 项目 #1 下全部制品的扫描状态与分级汇总行 |
| 安全策略卡片 | 项目 #1 的策略表:行内启停/编辑/删除,顶部新建 |
| 内置策略模板卡片 | 5 类安全基线模板表:行内「应用」(幂等创建并默认启用) |
| 新建/编辑策略弹窗 | 策略字段表单(名称/分类/规则类型/级别/生效环境/描述) |
4. 交互元素
4.1 页头、顶部说明与操作结果提示条
| 控件 | 位置 | 含义 | 必填/默认/可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 页面标题 | 页头 h2 | 页面身份标识 | 常驻 | Apollo 安全合规 | 无 | 与源码 h2 逐字一致 |
| 「新建安全策略」 | 页头右侧(btn-primary) | 打开新建策略弹窗 | 始终可点 | 与 Tab3 空态内嵌的「新建安全策略」同走 openCreatePolicy | 无(本地弹窗) | 在 Tab 1/2 下也显示且可用;Tab 切换不隐藏 |
| 顶部 alert-info | 标题下 | TAD-10 能力说明 | 常驻 | 只读文案 | 无 | 文字与源码逐字一致(见附录 A) |
| 操作结果提示条 | alert-info 下 | 展示最近一次操作结果 | 有 alert.message 才显示;type 取 info/success/error | 成功用 success、失败用 error、默认 info 显示消息 | 无 | 单槽覆盖:新结果替换旧提示 |
| 提示条「关闭」 | 提示条右侧 | 清空提示 | 提示可见时可用 | alert.message='' | 无 | 纯本地状态 |
页面状态用一组 ref 维护:loading(四个布尔位 policies/templates/vuln/projVulns,注意无 compliance 位)、busy(当前操作键字符串,非空即全局互斥——所有行内/页头按钮 :disabled="!!busy")、alert(单槽提示)、summary、gateForm/gateResult、artifacts/selectedArtifactId/lastSbom/vulnReport/findings/projVulns、policies/templates、policyModal/policyForm。注意 busy 是全局互斥位:任一写操作(评估/SBOM/扫描/保存策略/启停/删除/应用模板)进行中,其余全部操作按钮同时禁用;Tab 内的查看类操作与 Tab 切换不受 busy 影响。
4.2 Tab1:合规总览 summary 卡
页面挂载(onMounted)即调 GET /projects/1/compliance-report,把响应解包对象按别名兜底映射进 summary,由 2×? 排布(flex wrap)的九张卡渲染。各卡取值(后端字段在 5.3 列出):
| 卡片 | summary 字段 | 语义 |
|---|---|---|
| 策略数 | total_policies | 项目 #1 与全局(project_id=0)的策略总数 |
| 启用策略 | enabled_policies | 其中 enabled=true 的数量 |
| 评估次数(近30天) | evaluations_30d | 近 30 天落库的评估次数 |
| deny 决策 / warn 决策 / allow 决策 | denied_count / warn_count / allow_count | 近 30 天评估决策分布 |
| 扫描制品数 / 含漏洞制品 | scanned_artifacts / vulnerable_artifacts | 项目下已扫描制品与检出漏洞的制品数 |
| 漏洞条目数 | vuln_count | 检出漏洞条目总数 |
失败行为:fetchCompliance 的 catch 只把 summary.value 复制一份(保持原值不置零),不弹错误提示——合规卡是只读仪表,加载失败静默保持旧值。该接口不在任何 Tab 切换时重复请求,只在页面挂载与「策略写操作成功后」(新建/编辑/启停/删除/应用模板的成功路径)由页面主动重拉。
4.3 Tab1:安全门禁手动评估表单(Security Gate)
「安全门禁手动评估(Security Gate)」卡片是整页最重要的预演入口。卡片正文解释文案:模拟部署前门禁检查:提交资源快照(tags / digest / signer_id / config / vulns / environment),由启用策略求值汇总 allow / warn / deny 决策并记录审计轨迹。
| 控件/字段 | 位置 | 含义 | 必填/默认/可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 项目 id(project_id) | 表单第一行左 | 当前评估项目 | 恒为 1(disabled 输入框展示常量) | 只读展示 | 随 payload 提交 project_id:1 | 不可改;后端按 project 加载启用策略(含全局) |
| 资源类型(resource_type) | 第一行右下拉 | 被评估资源类别 | 默认 artifact;可选 artifact(制品)/deployment(部署)/config(配置) | 决定资源快照的求值语义与审计中的 resource_type | 随 payload 提交 | 值为枚举字符串 artifact/deployment/config,见 RESOURCE_TYPES |
| 资源 id(resource_id) | 第二行输入 | 资源标识 | 选填(默认空串) | trim 后随 payload 提交;空则后端不填 | 随 payload 提交 | placeholder 如 artifact-3 或 deployment 目标名 |
| 资源数据(resource_data JSON) | 第三行 textarea rows=4 | 资源快照对象 | 选填(留空提交空对象) | 非空先 JSON.parse,语法错则拦截 | 随 payload 提交 resource_data | 支持键(muted 原文):tags / sha256 / digest / signer_id / config(对象或字符串)/ vulns / environment;placeholder 示例见附录 A |
| 「执行评估」 | 表单右下(btn-primary submit) | 提交评估 | !busy 才可点;进行中显示 评估中... | 见下 | POST /security-gate/evaluate | 提交前清空上一次 gateResult |
点「执行评估」后的行为(handleEvaluateGate):先把 resource_data trim 后 JSON.parse——语法错误走 showAlert('resource_data 不是合法 JSON:' + e.message, 'alert-error') 直接返回,不发请求。合法则组装 payload {project_id:1, resource_type, resource_id:trim 后, resource_data:对象} 提交。成功(allow/warn)或 HTTP 403(deny)都会把结果放进 gateResult 并展示评估结果区:
- 评估结果区:第一行
<strong>评估结果:</strong>+ decision 徽标(allow 绿 / warn 橙 / deny 红)+命中启用策略 {{policy_count ?? '-'}} 条 · 评估时间 {{formatTime(evaluated_at)}}。 - 无违规(violations 空)显示
全部启用策略通过,未命中违规。;有违规渲染违规表:策略名 / 规则类型 / 级别 / 原因(原因空显示-)。gateResult.violations元素含 policy_name/rule_type/severity/reason。 - 提示条:decision 为 allow/warn 时 success
安全门禁评估完成:decision=allow;deny 时alert-error(后端 deny 走 403,见下方「deny 边界」)。
deny 边界(与直觉相反,务必知晓):后端把 deny 决策作为 HTTP 403 返回(envelope code:40301,body 携带 data:{decision,policy_count,violations,evaluated_at}),因此: 1. apolloClient 响应拦截器先对 403 执行 alert('无权限执行该操作')——浏览器会先弹一个系统级 alert; 2. 页面 catch 走 showAlert('安全门禁评估失败:' + errMsg(err), 'alert-error'),errMsg 取 body.message(即 部署被安全策略拒绝: {策略名}: {原因}; ...); 3. gateResult 不被赋值(catch 分支不解析 403 body 里的 data),所以 decision=deny 时页面不会渲染 violations 违规表——违规清单只对 allow/warn 可见。这是前端缺陷(见第 8 章);要看 deny 违规细节,可让快照只命中 warn 级策略(如 require_signature),或改 HTTP 直调接口读响应 body。 失败提示统一 安全门禁评估失败:{errMsg}(网络/非 deny 业务错时)或先弹「无权限执行该操作」(403 deny)。
4.4 Tab2:制品下拉与工具栏
| 控件 | 位置 | 含义 | 必填/默认/可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|---|
| 制品下拉 | scan-bar 左侧 select | 选择要生成 SBOM/扫描/查报告的制品 | 默认空(placeholder 选择制品...);有制品时页面挂载后自动选中第一个 | 切换即清空 lastSbom 并 watch 自动拉该制品漏洞报告;切空则清空报告 | 切换时 GET /artifacts/:id/vulnerabilities | option 文案 {{name}}({{version||'-'}} #{{id}});空态 muted 暂无制品(Tab 制品管理登记后此处可选) |
| 「生成 SBOM」 | 下拉右(btn-outline) | 生成 SPDX 物料清单 | !busy && selectedArtifactId 才可点;进行中显示 生成中... | 成功后 lastSbom 填充、提示 SBOM 生成成功(format=spdx) | POST /artifacts/{id}/sbom body {format:'spdx'} | 同制品重复生成幂等覆盖;失败提示 生成 SBOM 失败:{msg} |
| 「触发扫描」 | 下拉右(btn-primary) | 触发内置漏洞扫描 | !busy && selectedArtifactId 才可点;进行中显示 扫描中... | 成功后提示 制品 #{id} 扫描:{msg}(msg 取响应 message||status||'扫描完成'),随即重拉该制品漏洞报告与项目漏洞汇总 | POST /artifacts/{id}/scan,成功后自动 GET /artifacts/:id/vulnerabilities + GET /projects/1/vulnerabilities | 失败提示 触发扫描失败:{msg} |
制品列表本身由 onMounted 的 fetchArtifacts(GET /projects/1/artifacts)加载,失败提示 加载制品列表失败:{msg}。制品 id 默认选中第一项后,watch 会立刻触发一次漏洞报告加载。
4.5 Tab2:SBOM 结果块与漏洞报告块
「生成 SBOM」成功后,在工具栏下方渲染 SBOM 块(.sbom-block):头行 SBOM:{{format||'-'}}(制品 #{{artifact_id || selectedArtifactId}}) + 生成时间 {{generated_at||created_at}}(格式化后),正文 JSON.stringify(sbom_data, null, 2) 的 code block 原文展示 SPDX JSON。注意:本页用「触发扫描」接口(POST /artifacts/:id/scan)后不自动展示 SBOM,SBOM 块只由「生成 SBOM」驱动;漏洞报告块与 SBOM 块相互独立,各自有值才渲染。 漏洞报告块(.vuln-block)数据来自 GET /artifacts/{selectedId}/vulnerabilities,页面对多形态响应做 parseVulnPayload 兼容(详见 5.3/5.4)。渲染内容:
| 区块 | 内容 | 细节 |
|---|---|---|
| 头行 | 漏洞报告(制品 #{{artifact_id || selectedArtifactId}}) | 右侧 muted 扫描器 {{scanner||'-'}} |
| 状态与计数卡 | 状态徽标 + Critical/High/Medium/Low + 漏洞条目 | status 徽标色:clean 绿 / vulnerable 红 / scanning 蓝 / error 橙(未识别灰);「漏洞条目」取 findings.length |
| 扫描信息 | muted 扫描完成时间:{{scanned_at||created_at}} | 有 scan_result_path 时追加 · 结果文件 {{path}}(mono) |
| 空态/表格 | findings 为空显示空态,否则表格 | status=scanning 显示 扫描进行中,请稍后刷新查看结果。;clean 显示 暂无漏洞条目(clean)。 |
findings 表格列:级别(severity 徽标 critical 红 / high 橙 / medium 黄 / low 蓝)/ 漏洞编号(mono,vuln_id)/ 组件包(package_name)/ 安装版本 / 修复版本 / 描述,空字段以 - 兜底。加载中显示 加载漏洞报告...;尚未扫描/未选制品的提示文案:选择制品后点击"触发扫描"生成漏洞报告;切换制品会自动加载已有报告。 后端对「未扫描」返回 404(制品尚未扫描,请先调用 POST /artifacts/:id/scan),页面提示 加载漏洞报告失败:{msg} 并把报告置空。
4.6 Tab2:项目漏洞报告(项目 #1)
卡片标题 项目漏洞报告(项目 #{{ PROJECT_ID }}),数据来自 onMounted 的 fetchProjVulns(GET /projects/1/vulnerabilities)。空态文案 暂无漏洞报告,先去 Tab 制品管理登记并扫描制品。;有数据渲染表格列:制品(artifactLabel + #id)/ 状态 / 扫描器 / Critical / High / Medium / Low / 扫描时间,各分级列空值显示 0。加载失败提示 加载项目漏洞报告失败:{msg}。 已知缺陷(前端解析与后端契约错位):后端该接口返回 {project_id, artifact_count, vulnerable_count, totals:{critical,high,medium,low}, artifacts:[{artifact_id,name,version,scan_status,report?,findings?}]},但页面 fetchProjVulns 依次尝试 toList(data)、data.reports、data.report,从未检查 data.artifacts 键——因此项目漏洞汇总表在有数据时也恒为空态(除非恰好数组形态)。这是前端解析缺陷,详见第 8 章。
4.7 Tab3:安全策略表
卡片标题 安全策略({{policies.length}}),右上内嵌 新建安全策略(btn-sm)。加载失败提示 加载安全策略失败:{msg};空态 暂无安全策略,点击"新建安全策略"或从下方模板快速应用(项目 #1)。。表格列:ID / 名称 / 分类 / 规则类型 / 级别 / 生效环境 / 描述 / 启用 / 操作:
- 分类徽标
categoryLabel(category):image(镜像)/ config(配置)/ compliance(合规)/ deploy(部署),样式固定映射(image 蓝/config 橙/compliance 红/deploy 绿,未识别灰)。 - 规则类型列
ruleTypeLabel(rule_type)显示中文长名(见附录 A RULE_TYPES)。 - 级别徽标:deny 红 / warn 橙。
- 生效环境列
envSummary(environments):JSON 数组转逗号串;空/null 显示全部环境。 - 启用列开关按钮:
p.enabled ? '已启用'(btn-success) : '已停用'(btn-outline),!busy才可点。
| 控件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|
| 启停开关 | 点击即切换:先打接口,成功后按响应 enabled(缺省按操作方向)提示 安全策略 #id 已启用/停用 并刷新策略列表与合规汇总;失败提示 启用/停用安全策略 #id 失败:{msg} | POST /security-policies/{id}/enable|disable | 无二次确认,点击即生效;按钮文案反映的是当前态,点击行为是切到反态 |
| 「编辑」 | 打开编辑弹窗回填当前行字段 | 无(弹窗) | 见 4.9;保存走 PUT |
| 「删除」 | 先 confirm(见附录 A)再调接口;成功后提示 安全策略已删除 并刷新两表 | DELETE /security-policies/{id} | confirm 文案含策略名与 id;删除不可恢复 |
4.8 Tab3:内置策略模板与「应用」
卡片标题 内置策略模板(5 类安全基线)。数据来自 onMounted 的 fetchTemplates(GET /security-policies/templates),空态 暂无模板。。表格列:模板名称 / 分类 / 规则类型 / 级别 / 描述 / 操作(应用)。5 类模板逐字名称与描述见附录 B(源码 security.ListTemplates())。
| 控件 | 含义 | 必填/默认/可用条件 | 操作效果 | 触发后端调用 | 边界与细节 |
|---|---|---|---|---|---|
| 「应用」 | 把模板实例化为一条策略 | !busy 才可点 | 先 confirm(文案见附录 A),再调接口;成功提示 模板 "{name}" 应用成功(策略 id=..) 并刷新策略表与合规卡;失败 应用模板 "{name}" 失败:{msg} | POST /security-policies/templates/{encodeURIComponent(name)}/apply | 后端按 ?project_id 决定策略归属,缺省 project_id=0(全局策略);页面调用未带 project_id → 模板总是落地为全局策略,而 confirm 文案却写「到项目 #1」,提示与实际归属不一致(前端缺陷,见第 8 章) |
卡片底部 muted:应用模板按 (project_id, name) 幂等创建策略并默认启用。——同模板重复「应用」不会建第二条,而是返回已存在的策略(HTTP 201 Created,幂等命中视为已就绪)。
4.9 新建/编辑安全策略弹窗
弹窗头:{{ mode==='create' ? '新建安全策略' : '编辑安全策略 #'+policy.id }}(项目 #{{ PROJECT_ID }})。字段:
| 字段 | 控件/校验 | 说明 |
|---|---|---|
| 名称(name)* | input,required | placeholder 如 禁止生产 latest 标签;保存前 trim 后判空,空则 showAlert('策略名称(name)必填','alert-error') |
| 分类(category)* | select | image/config/compliance/deploy 四选一;默认 image |
| 规则类型(rule_type)* | select | 六类 RULE_TYPES;默认 deny_latest_tag |
| 级别(severity,deny/warn)* | select | 默认 deny;选项文案 deny(拒绝,硬拦截) / warn(告警,不拦截) |
| 生效环境(environments JSON,可选) | input | placeholder ["prod","staging"],留空则全部环境;保存时先 JSON.parse 成功且数组非空才带 payload.environments,parse 失败则把逗号分隔输入 split 成数组兜底 |
| 描述(description) | textarea rows=3 | placeholder 如:制品/镜像不得使用 latest 等可变标签,强制不可变版本溯源;trim 后非空才带 |
底部按钮:取消(btn-outline,仅关弹窗)与提交按钮(busy==='savePolicy' 显示 保存中...;否则 create 模式显示 创建、edit 模式显示 保存)。提交效果:
| 场景 | 操作效果 | 触发后端调用 |
|---|---|---|
| 新建 | 成功后按返回 id 提示 安全策略创建成功(id=..),关弹窗,刷新策略表与合规卡 | POST /projects/1/security-policies |
| 编辑 | 成功后提示 安全策略 #id 更新成功,关弹窗,刷新两表 | PUT /security-policies/{id} |
| 失败 | 保存安全策略失败:{msg}(如 name 冲突 409) | 以上任一 |
弹窗为本地模态,点遮罩(@click.self)或「关闭」可放弃未保存内容,不触发请求。
5. 后端关联
5.1 API 客户端
action/web/src/api/apolloClient.js:axios 实例 baseURL /apollo-api/v1、timeout 30000ms、请求拦截器对所有请求附加 Authorization: Bearer <token>(token 取 apollo_token,无则回退 aip_token)。响应拦截器:HTTP 2xx 且 code===0 时把 response.data 解包为业务数据;HTTP 2xx 但 code!==0 拒绝;4xx/5xx 提取 body.message || body.error 并把 message 别名写入 error.response.data.error 后 reject——401 清 token 并跳 /apollo/login(登录页自身 401 不跳,避免死循环),403 alert('无权限执行该操作')。页面各 catch 统一以 err.response?.data?.message || err.response?.data?.error || err.message(errMsg)展示后端错误。 403 双 alert 是本页常态:门禁 deny 的 HTTP 403 先触发拦截器系统级 alert('无权限执行该操作'),再被页面 catch 提示 安全门禁评估失败:部署被安全策略拒绝:...——演示时看到两个弹窗(一个是浏览器原生,一个是页内 alert 条)是预期现象。
5.2 端点表
以下端点后端路由见 action/products/apollo/server/server.go(security 段约 944-961 行),全部带权限中间件(第 6 章详述)。前端统一用 apolloClient,因此 URL 前缀为 /apollo-api/v1;下表写后端实际路径(前缀 /api/v1)。
| 方法 | 路径 | 权限 | 请求体/Query | 页面触发点 |
|---|---|---|---|---|
| GET | /projects/:id/compliance-report | security:read | - | onMounted + 策略写成功后 |
| POST | /security-gate/evaluate | security:execute | {project_id, resource_type, resource_id, resource_data} | 「执行评估」 |
| GET | /projects/:id/artifacts | artifact:read | - | 制品下拉 |
| POST | /artifacts/:id/sbom | security:execute | {format:'spdx'} | 「生成 SBOM」 |
| POST | /artifacts/:id/scan | security:execute | - | 「触发扫描」 |
| GET | /artifacts/:id/vulnerabilities | security:read | - | 漏洞报告(watch/扫描后自动) |
| GET | /projects/:id/vulnerabilities | security:read | - | 项目漏洞报告列表 |
| GET/POST | /projects/:id/security-policies | security:read/write | POST body {name,category,rule_type,severity,description?,environments?} | 策略表加载 / 新建 |
| GET/PUT/DELETE | /security-policies/:id | read/write/write | PUT body 同上 | 编辑/删除 |
| POST | /security-policies/:id/enable | security:execute | - | 启停开关 enable |
| POST | /security-policies/:id/disable | security:execute | - | 启停开关 disable |
| GET | /security-policies/templates | security:read | - | 模板表加载 |
| POST | /security-policies/templates/:name/apply | security:execute | Query ?project_id=(缺省 0=全局) | 模板「应用」 |
5.3 响应结构示例
以下示例取自后端 handler 构造与既有测试期望(security_handlers.go、security/ 包、stage_d_smoke_test.go),页面看到的实际 JSON 即 envelope 解包后的 data。 GET /projects/1/compliance-report(envelope 解包后):
{
"project_id": 1,
"total_policies": 6,
"enabled_policies": 5,
"evaluations_30d": 12,
"allow_count": 7,
"denied_count": 3,
"warn_count": 2,
"scanned_artifacts": 4,
"vulnerable_artifacts": 2,
"vuln_count": 5
}
字段即九张 summary 卡的取值;total_policies/enabled_policies 统计口径为 project_id=? OR project_id=0(项目 + 全局合并),其余计数取自项目维度。前端 fetchCompliance 对每个字段做别名兜底(total_policies→policy_count→policies 等),当前后端字段与之匹配。 POST /security-gate/evaluate(决策 allow/warn,HTTP 200):
{
"project_id": 1,
"resource_type": "artifact",
"resource_id": "artifact-3",
"decision": "warn",
"policy_count": 5,
"violations": [
{
"policy_name": "部署必须签名",
"rule_type": "require_signature",
"severity": "warn",
"reason": "资源缺少 signer_id,无法验证签名"
}
],
"evaluated_at": "2026-09-07T05:00:00.123Z"
}
decision 取 deny(任一 deny 级违规命中)> warn(仅 warn 级违规)> allow(全部通过)。policy_count 是参与求值的启用策略数(含环境过滤前计数),violations 为命中违规数组。 POST /security-gate/evaluate(决策 deny,HTTP 403 + fail envelope):
{
"code": 40301,
"message": "部署被安全策略拒绝: 禁止生产 latest 标签: 制品使用了 latest 标签,禁止部署",
"data": {
"decision": "deny",
"policy_count": 5,
"violations": [
{
"policy_name": "禁止生产 latest 标签",
"rule_type": "deny_latest_tag",
"severity": "deny",
"reason": "制品使用了 latest 标签,禁止部署"
}
],
"evaluated_at": "2026-09-07T05:01:00.123Z"
},
"request_id": "req_..."
}
这是本页最需要理解的形态差异:deny 决策不放在 HTTP 200 的 envelope 里,而是用 HTTP 403 承载(code 与 middleware 的 40301 一致),data 中照常携带完整评估明细。页面拦截器因此走 403 分支(系统 alert + 走 catch),不把 data 写入 gateResult——见 4.3 与第 8 章。 GET /artifacts/3/vulnerabilities(envelope 解包后,report 与 findings 为同级字段):
{
"artifact_id": 3,
"report": {
"id": 11,
"artifact_id": 3,
"scanner": "zy_builtin",
"status": "vulnerable",
"total_critical": 1,
"total_high": 1,
"total_medium": 0,
"total_low": 1,
"scanned_at": "2026-09-07T05:02:00.123Z",
"created_at": "2026-09-07T05:02:00.123Z"
},
"findings": [
{
"id": 31,
"report_id": 11,
"vuln_id": "CVE-2024-27080",
"severity": "critical",
"package_name": "nginx",
"installed_version": "1.22.0",
"fixed_version": "1.24.1",
"description": ""
},
{
"id": 32,
"report_id": 11,
"vuln_id": "RULE-LATEST-TAG",
"severity": "low",
"package_name": "demo-web",
"installed_version": "1.2.0",
"description": "制品使用 latest 标签,无法溯源到不可变版本"
}
]
}
关键形态点:findings 与 report 平级,而非嵌套在 report 里。页面 parseVulnPayload 期望的是「report+findings 嵌套」/「扁平」/「报告数组」三态,对当前平级形态的判读是:命中 data.status || data.scanner || ... 分支把整个 data 当 report,findings 取 data.findings(平级存在所以读得到)——本次平级形态恰好能被兜底解析到 findings,但 vulnReport 成了含 artifact_id/findings 的大对象,status/count 字段仍能正常显示,findings 表也能渲染(见 4.5)。report.status 枚举 scanning/clean/vulnerable/error;report.scan_result_path(omitempty,扫描报告落盘路径)未扫描或为 error 时可缺省。未扫描访问本接口后端 404:{code:40400..., message:"制品尚未扫描,请先调用 POST /artifacts/:id/scan"}。 GET /projects/1/vulnerabilities(envelope 解包后,汇总对象):
{
"project_id": 1,
"artifact_count": 4,
"vulnerable_count": 2,
"totals": { "critical": 1, "high": 1, "medium": 0, "low": 1 },
"artifacts": [
{
"artifact_id": 3,
"name": "demo-web",
"version": "1.2.0",
"scan_status": "vulnerable",
"report": { "id": 11, "scanner": "zy_builtin", "status": "vulnerable" },
"findings": [ { "id": 31, "vuln_id": "CVE-2024-27080", "severity": "critical", "package_name": "nginx" } ]
}
]
}
页面 fetchProjVulns 未解析 artifacts 键(缺陷见 4.6 与第 8 章);artifact_id/name/version/scan_status 与表头「制品/状态/…」并不一一对应(后端无单行 status 平铺字段,report 嵌套)。修复时建议把 artifacts 数组元素映射为行(status 取 report.status || scan_status)。 GET /projects/1/security-policies(数组,单项 SecurityPolicy) 与 GET /security-policies/templates(数组) 见 5.4 后的模板清单;策略行关键字段:id/project_id/name/category/rule_type/severity/description/environments/enabled/created_at/updated_at(rule_config/environments omitempty,environments 空数组时页面显示「全部环境」)。
5.4 关键机制
六类规则与三档决策聚合。策略引擎(security/policy.go)把启用的策略逐条对资源数据求值:deny_latest_tag(tags 含可变标签即违规)、require_signature(signer_id 与 digest 须非空)、critical_vuln(vulns 中含 critical 级即违规)、no_plaintext_secret(config 文本含 password=/secret=/api_key= 模式即违规)、require_resource_limits(config 未声明 resources.limits 即违规)、custom(正则/匹配模式自定义,见 policy.go evaluateRule)。每条命中生成一条 Violation{policy_name,rule_type,severity,reason};聚合规则是任一 deny 级违规→deny;否则任一 warn 级违规→warn;全过→allow。参与求值的策略 = 项目 + 全局(project_id=? OR project_id=0)中 enabled=true 者,并按资源 environment 做环境过滤(策略 environments 空 = 全部环境生效)。注意 project_id=0(全局)查询时只取全局策略——这正是门禁评估/激活路径与页面项目 #1 语义不同的根源(见下文)。 每次评估都落审计与历史。Evaluate 尾部调 persistEvaluation:把评估结果(project/resource/decision/违规)写入 policy_evaluation_results 表——合规总览的近 30 天计数与决策分布即由此统计。门禁 deny 还会在安全门禁的两种入口(手动 evaluate、部署链路)各落一条审计动作(SECURITY_GATE_EVALUATE denied / SECURITY_POLICY_APPLY_TEMPLATE 等,见第 6 章),与「审计日志」页贯通。 部署链路上的真实门禁(左移门禁)。后端把同一策略引擎接进期望状态激活动作,形成「先证安全再上线」的强制检查点:gitops.SyncEngine(gitops/service.go,激活新声明 draft→active 前,约 202 行)与期望状态激活 handler(server/handlers.go 的 handleActivateDesiredState,draft→active 前,286 行)都会调用 server 层注入的 apolloSecurityGate.EvaluateDeployment(server/server.go 166-232 行)。该适配器把声明组装成 EvaluateRequest:ResourceType=deployment、ResourceID="{projectID}:{声明名}"(如 1:order-api,跨项目同名声明审计可区分)、ResourceData 携带 digest/signer_id/config(整份声明)/vulns——其中 vulns 按 decl.Digest 匹配 artifacts.sha256 或 storage_path="bundle://{digest}" 关联的漏洞严重级(findVulnsByDigest),查不到扫描结果不误拦。决策 deny → 返回 403 AUTHZ_ERROR,激活被拒、声明保持 draft(Sync 场景整次 sync 置 failed 并把已处理声明并入失败历史供回滚);warn 不拦截。该门禁还覆盖审批语义的分工:require_approval 通道的审批是「软门禁」(未过审不激活但 sync 成功),deny 是「硬门禁」(整次 sync 失败)。注意激活 handler 用 projectID=0 评估(DesiredState 为全局资源),故只有全局策略(含经模板「应用」落地的 5 类基线)参与——在页面「新建安全策略」创建的项目 #1 策略不会拦截手动激活路径(见第 6 章边界)。回滚(Rollback)属应急恢复不走门禁。 SBOM 与扫描的实现。SBOM 生成器产出 SPDX-2.3(请求 format 目前恒 spdx)JSON,写入 sbom 表(字段见 5.3),同制品单条幂等覆盖。扫描器 zy_builtin(security/scanner.go)是内置模拟实现(非真实 Trivy/Syft):先建 report(status=scanning)并回写制品 vulnerability_scan_status=scanning,随后跑四条内置规则——RULE-PLAINTEXT-SECRET(metadata/storage_path/tags 含明文密钥模式,high)、RULE-LATEST-TAG(tags 含 latest,low)、RULE-NO-SHA256(缺 sha256,low)、RULE-UNSIGNED-BUNDLE(bundle 制品 metadata 无 signer_id,medium)——再按制品 metadata 声明的 packages 依赖列表对 7 条小型 CVE 库(nginx CVE-2024-27080、log4j-core CVE-2021-44228、openssl CVE-2023-0286、spring-core CVE-2022-22965、jackson-databind CVE-2020-36518、guava CVE-2023-2976、mysql CVE-2022-27456,均为 critical/high/medium/low 样例)做版本命中;完成把 report 置 clean/vulnerable 并回写制品状态、扫描报告落盘 data/security/reports。也就是说「触发扫描」得到的漏洞报告内容是规则命中 + 样例库命中的合成物,可复现但非真实漏洞库。 模板应用的幂等与策略命名空间。POST /security-policies/templates/{name}/apply 后端(handleApplyPolicyTemplate)按 ?project_id(缺省 0)解析归属;ApplyTemplate 按 (project_id,name) 唯一约束创建策略并 enabled=true,已存在则幂等返回现有策略(HTTP 201)。模板策略的 default_config 会写入 rule_config(如 deny_latest_tag 的 {tag:"latest", message:"制品使用了 latest 标签,禁止部署"}),供 evaluateRule 取用。页面不传 project_id → 全部落地全局(project_id=0)。 compliance-report 统计口径:策略数统计 project_id=? OR project_id=0(合并项目+全局);scanned_artifacts 按项目下已扫描制品计数、vulnerable_artifacts 按检出漏洞的制品计数、vuln_count 累加漏洞条目。总览卡因此会比「安全策略(项目 #1)」表内的行数多出全局策略数——两处数字不必一致,属设计而非缺陷。
6. 权限与安全
- 认证分层:全部端点走独立
apollo_token(Bearer),列表/写接口按权限点security:read/write/execute、制品读取用artifact:read鉴权;未登录 40101、无权限 40301,均由拦截器统一处理。 - 部署门禁是强制检查点:期望状态激活(draft→active)前与 GitOps sync 激活路径都会执行策略引擎,deny 硬拦截(HTTP 403)——它不依赖本页登录态,属后端进程内调用,无法从页面绕过。
- 全局 vs 项目策略边界:模板「应用」默认落地全局策略(project_id=0),页面「新建安全策略」落地项目 #1;激活门禁以 project_id=0 评估只查全局策略——想用 deny 基线拦截所有手动激活,应使用模板应用或确认策略落在全局;项目 #1 级策略只在门禁手动评估与 Sync 目标项目路径(projectID>0)上生效。
- 敏感信息:资源快照与策略描述按用户输入透传展示;漏洞报告/合规统计只含元数据与计数,不含明文密钥。门禁评估与模板应用每次调用落审计(含 denied/failed),越权探测与拒绝原因全程留痕,可在「审计日志」页查到 SECURITY_GATE_EVALUATE / SECURITY_POLICY_APPLY_TEMPLATE 等动作。
- 写操作防护:删除策略有 confirm;启停无 confirm 但单次点击只切反态且成功后即刷新,无级联副作用;
busy全局互斥防并发写。deny 的 403 双弹窗(系统 alert + 页内错误条)是拦截器既有行为,非页面单独实现。
7. 常见问题与排错
以下问题均能从 SecurityPage.vue 的 catch/alert 文案、后端 handler/策略引擎代码与既有测试复现。 1. 点「执行评估」先弹系统 alert「无权限执行该操作」、再提示评估失败:原因是决策为 deny 时后端用 HTTP 403 返回(含 data 明细),拦截器对 403 统一弹「无权限」。处理:这是预期行为不是权限问题——看页内错误条里的 message(部署被安全策略拒绝: {策略名}: {原因})判断命中策略;要看违规表需让评估只命中 warn 级策略,或改 curl 直调 /apollo-api/v1/security-gate/evaluate 读 403 body 的 data(见第 8 章缺陷)。 2. 评估总是 allow,期望被 deny 却没有:原因可能是①目标策略未启用;②策略 environments 限定了环境而资源快照的 environment 不匹配;③策略挂在项目 #1 而快照求值时…——注意页面评估 payload 恒带 project_id=1,后端会加载项目+全局策略,若你在「安全策略」Tab 建的策略 enabled=false 则不参与。处理:确认策略 enabled 开关为「已启用」、environments 留空或与快照 environment 匹配、severity 为 deny;再用 {"tags":["latest"],"environment":"prod"} 命中 deny_latest_tag 模板复现。 3. 合规总览的「策略数」比 Tab3 表格行数多:原因是总览统计口径是「项目 + 全局策略」合并(project_id=1 OR project_id=0),而 Tab3 只列项目 #1;模板「应用」落地的全局策略会计入总览但不显示在策略表。处理:属设计差异,可用「安全策略(n)」表的 n 与总览对照确认全局策略数 = 差值。 4. 漏洞报告显示为空态或扫描进行中,刷新也不变:status=scanning 时报告尚未生成(扫描进行中,请稍后刷新查看结果。),clean 时本就无条目(暂无漏洞条目(clean)。)。处理:等后端扫描完成(本页扫描器同步执行,通常立即完成)后重进制品或再点「触发扫描」刷新;仍为空则确认制品下拉已选中、且该制品确实执行过扫描(未扫描后端 404)。 5. 「漏洞报告」「项目漏洞报告」明明有数据却恒为空:Tab2 制品漏洞报告需 report 与 findings 恰好能被 parseVulnPayload 兜底解析;项目漏洞报告列表从不解析后端 artifacts 键,有制品也显示空态。处理:属前端解析缺陷(第 8 章);临时查看可用 curl 调 /apollo-api/v1/projects/1/vulnerabilities 看 artifacts 数组内容,制品级报告切换制品下拉查看。 6. 「应用」模板提示失败:原因是模板名经 encodeURIComponent 编码后路径不匹配(含中文等需编码字符但后端未按编码解)或后端 security 服务未接线返回 404/500。处理:确认后端路由 POST /security-policies/templates/:name/apply 存在(server.go 954 行);重复应用为幂等返回既有策略(仍 201),可在策略表确认是否已创建;注意落地的是全局策略(见第 8 章缺陷 c)。 7. 删除/启停策略按钮点了没反应或报「无权限」:原因是 busy 非空(有其它写操作进行中)时按钮禁用不可点;或当前 apollo_token 主体缺 security:write/execute 权限点。处理:等其它操作完成再看按钮态;到「角色管理」页把 security:* 权限点授给当前角色后重进。 8. 扫描后制品状态与报告不一致:扫描是同步的——POST /scan 返回时 report 已定态(clean/vulnerable)。若 Tab2 漏洞报告状态徽标还是旧值,多为 fetchVulnReport 早于扫描写回。处理:再点一次「触发扫描」(成功后自动重拉报告),或刷新页面看制品 vulnerability_scan_status。 9. 想验证真实部署也会被 deny 拦截:在制品管理登记制品并扫描出 critical 漏洞(或声明 digest 关联 bundle),应用「Critical 漏洞拒绝」模板到全局,再到「期望状态/部署与漂移」页激活引用该 digest 的声明。处理:激活应被 403 拒绝并保持 draft;在审计日志搜 SECURITY_GATE_EVALUATE(denied)确认拦截,而非只看本页手动评估。 10. curl 直调门禁接口怎么写:curl -X POST http://127.0.0.1:18082/api/v1/security-gate/evaluate -H "Authorization: Bearer {apollo_token}" -H "Content-Type: application/json" -d '{"project_id":1,"resource_type":"artifact","resource_id":"demo","resource_data":{"tags":["latest"]}}'——deny 时返回 403 的 envelope(data 含 violations),allow/warn 返回 200。
8. 已知缺陷与边界
| 缺陷/边界 | 说明 |
|---|---|
| 门禁 deny 不展示违规表 | 后端 deny 用 HTTP 403(data 含 violations)返回;拦截器先弹系统 alert,页面 catch 不解析 403 body 的 data,gateResult 保持 null——违规清单只对 allow/warn 可见。web/src 可修:catch 分支读取 err.response?.data?.data 赋给 gateResult(并把决策映射为 deny) |
| 项目漏洞报告恒为空 | 后端 GET /projects/1/vulnerabilities 返回 artifacts:[...] 汇总数组,前端 fetchProjVulns 只查 arrays/reports/report、从不读 data.artifacts。web/src 可修:list = toList(data.artifacts),行映射 status 用 report.status || scan_status |
| 应用模板落地全局策略 | vue 调 apply 不带 ?project_id,后端缺省 0=全局,而 confirm 文案写「到项目 #1」,提示与实际归属不一致。web/src 可修:调用带 ?project_id=${PROJECT_ID} 或改 confirm 文案为「全局」 |
| 页面「新建安全策略」只挂项目 #1 | PROJECT_ID=1 常量硬编码;后续才支持 URL 参数。项目级策略不参与激活门禁(激活按 projectID=0 只查全局)——要拦截手动激活需用模板应用(全局) |
| 扫描器为内置模拟 | zy_builtin 是规则命中 + 7 条样例 CVE 库,非真实 Trivy/Syft;漏洞库小、无依赖图、无历史增量。SBOM 为 SPDX 格式的 JSON 文本,本页以 pre 展示不语法着色 |
| 门禁评估的「无权限」误报 | deny 的 HTTP 403 被拦截器当作权限拒绝弹系统级 alert('无权限执行该操作'),与真实 40301 无法区分;演示场景需人工识别。属拦截器与门禁语义耦合(后端已注释说明保留 403 语义) |
| 无分页/无导出 | 策略、模板、项目漏洞汇总均为全量返回;findings 表不分页;扫描结果与 SBOM 无下载按钮(需脚本调接口) |
| 合规卡失败静默 | fetchCompliance 失败仅保留旧值不提示,首次加载失败时九张卡恒 0,无重试入口(刷新页面可重拉) |
附录 A:页面文案速查(与 SecurityPage.vue 逐字一致)
| 场景 | 源码取值/文案 |
|---|---|
| 页面标题 | Apollo 安全合规 |
| 顶部说明 | 安全合规(TAD-10):对制品生成 SBOM 物料清单、执行漏洞扫描;安全策略引擎按 deny_latest_tag / require_signature / critical_vuln / no_plaintext_secret / require_resource_limits / custom 规则在部署前强制安全门禁,合规总览汇总项目安全态势。 |
| Tab | 合规总览 / 扫描与漏洞 / 安全策略 |
| 门禁卡标题/说明 | 安全门禁手动评估(Security Gate);模拟部署前门禁检查:提交资源快照(tags / digest / signer_id / config / vulns / environment),由启用策略求值汇总 allow / warn / deny 决策并记录审计轨迹。 |
| 门禁表单标签 | 项目 id(project_id) / 资源类型(resource_type)* / 资源 id(resource_id) / 资源数据(resource_data JSON);resource_data placeholder 见 4.3 |
| resource_data 支持键 | tags / sha256 / digest / signer_id / config(对象或字符串)/ vulns / environment |
| 资源类型选项 | artifact(制品) / deployment(部署) / config(配置) |
| 评估按钮/文案 | 执行评估 / 评估中...;成功 安全门禁评估完成:decision={dec};失败 安全门禁评估失败:{msg};JSON 错 resource_data 不是合法 JSON:{e.message} |
| 评估结果区 | 评估结果:;命中启用策略 {n} 条 · 评估时间 {t};全部启用策略通过,未命中违规。 |
| 扫描卡标题 | 制品漏洞扫描(SBOM / Scan);空态 暂无制品(Tab 制品管理登记后此处可选) |
| 扫描按钮 | 生成 SBOM / 生成中... / 触发扫描 / 扫描中...;SBOM 成功 SBOM 生成成功(format=spdx) |
| 空态文案 | 报告 扫描进行中,请稍后刷新查看结果。 / 暂无漏洞条目(clean)。;项目列表 暂无漏洞报告,先去 Tab 制品管理登记并扫描制品。;提示 选择制品后点击"触发扫描"生成漏洞报告;切换制品会自动加载已有报告。 |
| 策略表空态 | 暂无安全策略,点击"新建安全策略"或从下方模板快速应用(项目 #1)。 |
| 启停/删除 | confirm 确定删除安全策略 "{name}"(#{id})吗?;成功 安全策略 #id 已启用/停用 / 安全策略已删除 |
| 模板 | 卡标题 内置策略模板(5 类安全基线);confirm 确定应用策略模板 "{name}" 到项目 #1 吗?(幂等创建并默认启用);成功 模板 "{name}" 应用成功(策略 id=..);底部 应用模板按 (project_id, name) 幂等创建策略并默认启用。 |
| 新建/编辑弹窗 | 新建安全策略 / 编辑安全策略 #{id}(项目 #1);必填校验 策略名称(name)必填;创建 安全策略创建成功(id=..);保存 安全策略 #{id} 更新成功 / 保存安全策略失败:{msg} |
附录 B:5 类内置策略模板(与 security.ListTemplates() 一致)
| 模板名称 | 分类 | 规则类型 | 级别 | 描述 |
|---|---|---|---|---|
| 禁止生产 latest 标签 | image | deny_latest_tag | deny | 制品/镜像不得使用 latest 等可变标签,强制不可变版本溯源 |
| 部署必须签名 | deploy | require_signature | warn | 部署的 bundle/制品必须携带签名(digest 与 signer_id 均非空) |
| Critical 漏洞拒绝 | compliance | critical_vuln | deny | 制品存在 critical 级漏洞时拒绝部署 |
| 禁止明文密钥 | config | no_plaintext_secret | deny | 部署配置不得包含 password=/secret=/api_key= 明文密钥 |
| 部署必须设置资源限制 | deploy | require_resource_limits | warn | 部署配置必须声明 resources.limits(CPU/内存上限) |
分类取值:image / config / compliance / deploy;RULE_TYPES 六项长名(前端下拉逐字):deny_latest_tag(禁止 latest 标签) / require_signature(部署必须签名) / critical_vuln(Critical 漏洞拒绝) / no_plaintext_secret(禁止明文密钥) / require_resource_limits(必须设置资源限制) / custom(自定义模式匹配)。