From 6f751486e408ceef89340ce2dba2a00fc440f3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=84=A6=E9=BE=99=E8=A8=80?= Date: Mon, 27 Jul 2026 15:11:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=9A=E6=8B=92=E7=BB=9D?= =?UTF-8?q?=E9=9D=9E=E7=BA=A6=E5=AE=9A=E5=89=8D=E7=AB=AF=E6=9D=83=E9=99=90?= =?UTF-8?q?=E9=97=A8=E7=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_api_permission_contract.py | 22 ++++++++++++++----- .../scripts/verify-business-contract.mjs | 21 +++++++++++++++--- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/shierfeng-fastapi-backend/tests/module_business/test_api_permission_contract.py b/shierfeng-fastapi-backend/tests/module_business/test_api_permission_contract.py index 1590218..3e95460 100644 --- a/shierfeng-fastapi-backend/tests/module_business/test_api_permission_contract.py +++ b/shierfeng-fastapi-backend/tests/module_business/test_api_permission_contract.py @@ -311,6 +311,8 @@ def _parse_static_permission_array(expression: str) -> list[str]: def _extract_static_has_permi_and(expression: str) -> list[str]: code, structure = _sanitize_vue_source(expression) + if re.search(r'proxy\.\$auth\.hasPermiOr\s*\(', structure): + raise AssertionError('禁止使用 proxy.$auth.hasPermiOr,权限门禁必须使用约定的静态 AND 语义') call_tokens = list(re.finditer(r'proxy\.\$auth\.hasPermiAnd\s*\(', structure)) call_pattern = re.compile(r'proxy\.\$auth\.hasPermiAnd\s*\(\s*(?P\[[^\[\]]*\])\s*\)') calls = list(call_pattern.finditer(structure)) @@ -479,12 +481,20 @@ class ApiPermissionContractTest(unittest.TestCase): with self.subTest(source=source), self.assertRaises(AssertionError): _extract_guarded_vue_permissions(source) - guarded = _extract_guarded_vue_permissions( - '' - ) - self.assertEqual(frozenset(), guarded) - with self.assertRaises(AssertionError): - _assert_exact_permission_layers(self, {'vue': guarded}) + with self.assertRaisesRegex(AssertionError, 'hasPermiOr'): + _extract_guarded_vue_permissions( + '' + ) + + def test_vue_guard_parser_rejects_extra_has_permi_or_after_valid_guards(self) -> None: + source = """ + + + + """ + + with self.assertRaisesRegex(AssertionError, 'hasPermiOr'): + _extract_guarded_vue_permissions(source) def test_independent_expected_permissions_reject_four_layers_missing_same_permission(self) -> None: missing = 'master:strain:query' diff --git a/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs b/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs index e74eb19..e9a7e07 100644 --- a/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs +++ b/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs @@ -536,6 +536,11 @@ function parseStaticPermissionArray(expression) { function extractStaticHasPermiAnd(expression) { const sanitized = sanitizeSource(expression, { allowTemplateInterpolation: true }) + assert.doesNotMatch( + sanitized.structure, + /proxy\.\$auth\.hasPermiOr\s*\(/, + '禁止使用 proxy.$auth.hasPermiOr,权限门禁必须使用约定的静态 AND 语义' + ) const callPattern = /proxy\.\$auth\.hasPermiAnd\s*\(/g const permissions = [] let match @@ -833,11 +838,11 @@ test('真实权限门禁解析拒绝注释常量、hasPermiOr 和动态数组假 ` assert.deepEqual(extractGuardedVuePermissions(staleSource), []) - assert.deepEqual( - extractGuardedVuePermissions( + assert.throws( + () => extractGuardedVuePermissions( `` ), - [] + /hasPermiOr/ ) assert.throws( () => extractGuardedVuePermissions(''), @@ -859,6 +864,16 @@ test('真实权限门禁解析拒绝注释常量、hasPermiOr 和动态数组假 ) }) +test('合法门禁混入额外 hasPermiOr 时直接 fail-closed', () => { + const source = ` + + + + ` + + assert.throws(() => extractGuardedVuePermissions(source), /hasPermiOr/) +}) + test('17 个 SQL component 正好映射 17 个 page contract 普通文件', async () => { const componentEntries = Object.entries(expectedPageComponents) const pageRelativePaths = [