From 37c94b6713fb383ed5b6f924380dbdccc704ad18 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:45:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=9A=E9=99=90=E5=AE=9A?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=9D=83=E9=99=90=E9=97=A8=E7=A6=81=E6=8E=A5?= =?UTF-8?q?=E6=94=B6=E8=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_api_permission_contract.py | 21 +++++++++++++++++-- .../scripts/verify-business-contract.mjs | 17 ++++++++++++++- 2 files changed, 35 insertions(+), 3 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 19d0778..826eb46 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 @@ -314,10 +314,11 @@ def _extract_static_has_permi_and(expression: str) -> list[str]: permission_tokens = list(re.finditer(r'\bhasPermi(?:And|Or)\b', structure)) if any(token.group(0) == 'hasPermiOr' for token in permission_tokens): raise AssertionError('禁止使用 proxy.$auth.hasPermiOr,权限门禁必须使用约定的静态 AND 语义') - call_tokens = list(re.finditer(r'proxy\.\$auth\.hasPermiAnd\s*\(', structure)) + canonical_call_prefix = r'(?\[[^\[\]]*\])\s*\)') + call_pattern = re.compile(canonical_call_prefix + r'\s*(?P\[[^\[\]]*\])\s*\)') calls = list(call_pattern.finditer(structure)) if len(calls) != len(call_tokens): raise AssertionError('hasPermiAnd 必须直接传入静态字符串数组') @@ -517,6 +518,22 @@ class ApiPermissionContractTest(unittest.TestCase): ): _extract_guarded_vue_permissions(source) + def test_vue_guard_parser_rejects_noncanonical_permission_receivers(self) -> None: + valid_guard = '' + invalid_calls = ( + "myproxy.$auth.hasPermiAnd(['master:strain:query'])", + "foo.proxy.$auth.hasPermiAnd(['master:strain:query'])", + "$proxy.$auth.hasPermiAnd(['master:strain:query'])", + "this.proxy.$auth.hasPermiAnd(['master:strain:query'])", + ) + for invalid_call in invalid_calls: + source = f'{valid_guard}' + with ( + self.subTest(invalid_call=invalid_call), + self.assertRaisesRegex(AssertionError, 'canonical'), + ): + _extract_guarded_vue_permissions(source) + def test_independent_expected_permissions_reject_four_layers_missing_same_permission(self) -> None: missing = 'master:strain:query' mutated = EXPECTED_PERMISSIONS - {missing} diff --git a/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs b/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs index 5499059..da5070c 100644 --- a/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs +++ b/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs @@ -546,7 +546,7 @@ function extractStaticHasPermiAnd(expression) { !permissionTokens.some((token) => token[0] === 'hasPermiOr'), '禁止使用 proxy.$auth.hasPermiOr,权限门禁必须使用约定的静态 AND 语义' ) - const callPattern = /proxy\.\$auth\.hasPermiAnd\s*\(/g + const callPattern = /(? { + const validGuard = `` + const invalidCalls = [ + `myproxy.$auth.hasPermiAnd(['master:strain:query'])`, + `foo.proxy.$auth.hasPermiAnd(['master:strain:query'])`, + `$proxy.$auth.hasPermiAnd(['master:strain:query'])`, + `this.proxy.$auth.hasPermiAnd(['master:strain:query'])` + ] + + for (const invalidCall of invalidCalls) { + const source = `${validGuard}` + assert.throws(() => extractGuardedVuePermissions(source), /canonical/) + } +}) + test('静态权限数组拒绝稀疏元素且仅允许单个尾逗号', () => { const sparseArrays = [ `[, 'master:strain:list']`,