fix(security): retire check-governance + scope /reports/dupont to session entity

- data_quality.py: remove GET /check-governance (no caller repo-wide; its
  entity_id param had no session auth -> cross-tenant read; duplicate of
  governance-check) -> the path now returns 404
- reports.py: /reports/dupont resolves the analysis target from the session
  account (assert_entity_slug_scope / resolve_session_slug); explicit
  cross-entity entity param -> 403, illegal value -> 400; the 2026-08-31
  cross-entity exemption is revoked (docstring updated)
- frontend: drop the checkGovernance api definition; DupontAnalysis.vue
  entity radio group -> read-only session entity tag, request without param
- tests: TestCheckGovernanceRetired (404 x2), TestDupontSessionScope (3),
  TestDupont reworked to account-bound tokens (8 tests)
- docs/api-verification-split.md: drop the retired row, note 334/404 -> 333/403

pytest: 10 failed / 957 passed / 1 xfailed vs HEAD baseline 10 failed /
951 passed / 1 xfailed (identical failure set -> zero regression)

(cma-residual-cross-entity-cleanup-20260914)
This commit is contained in:
Hermes CI Fix
2026-09-14 12:09:04 +08:00
parent 72c1bbce16
commit aac1bd1df1
7 changed files with 147 additions and 102 deletions
+3 -3
View File
@@ -362,9 +362,9 @@ export const dataQualityApi = {
logs: (params?: any) => api.get('/data-quality/logs', { params }),
updateLog: (id: number, data: any) => api.put(`/data-quality/logs/${id}`, data),
deleteLog: (id: number) => api.delete(`/data-quality/logs/${id}`),
// DAMA数据治理规则检查(财务七规则)
checkGovernance: (params?: any) => api.get('/data-quality/check-governance', { params }),
// 财务七规则检查(最终方案:评分+7规则+实体筛选,只读幂等)
// DAMA数据治理规则检查(财务七规则)— /check-governance 已于 2026-09-14 下线
// (cma-residual-cross-entity-cleanup-20260914:无调用方 + entity_id 参数无会话鉴权跨租户)
// 财务七规则检查(唯一入口:评分+7规则+实体筛选,只读幂等;实体按会话账套)
governanceCheck: (params?: any) => api.get('/data-quality/governance-check', { params }),
}
+6 -7
View File
@@ -3,13 +3,10 @@
<div class="page-header">
<h3 class="page-title">杜邦分析</h3>
</div>
<!-- 顶部控制 -->
<!-- 顶部控制:企业只读标识(不可点)——账套由登录时选定,页内无切换入口 -->
<div class="flex-row" style="gap:12px;margin-bottom:16px;align-items:center;">
<span style="font-weight:600;font-size:14px;">分析对象:</span>
<el-radio-group v-model="entity" @change="fetchDupont">
<el-radio value="bohai">陕西博海科技(IT服务)</el-radio>
<el-radio value="hanke">陕西酣客(白酒经销)</el-radio>
</el-radio-group>
<el-tag size="small" type="primary" effect="plain">{{ entityLabel }}</el-tag>
<el-button type="primary" size="small" @click="fetchDupont" :loading="loading">刷新</el-button>
</div>
@@ -116,9 +113,10 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import axios from 'axios'
import { sessionEntityName } from '../utils/entity'
const loading = ref(false)
const entity = ref('bohai')
const entityLabel = ref(sessionEntityName())
const data = ref<any>({})
const api = axios.create({ baseURL: '/api/cma', timeout: 15000 })
@@ -151,7 +149,8 @@ function formatMoney(val: number): string {
async function fetchDupont() {
loading.value = true
try {
const r: any = await api.get('/reports/dupont', { params: { entity: entity.value } })
// 分析对象由会话账套决定(后端按 token 解析),不再传 entity 参数
const r: any = await api.get('/reports/dupont')
data.value = r.data || r
} catch (e) {
console.error('杜邦分析加载失败:', e)