134 lines
8.5 KiB
PowerShell
134 lines
8.5 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
|
|
function Read-ProjectFile {
|
|
param([string]$Path)
|
|
$fullPath = Join-Path $root $Path
|
|
if (-not (Test-Path -LiteralPath $fullPath)) { throw "缺少 TAC 集成文件:$Path" }
|
|
return Get-Content -LiteralPath $fullPath -Raw -Encoding UTF8
|
|
}
|
|
|
|
function Require-Text {
|
|
param([string]$Content, [string]$Text, [string]$Label)
|
|
if (-not $Content.Contains($Text)) { throw "$Label 缺少:$Text" }
|
|
}
|
|
|
|
function Reject-Text {
|
|
param([string]$Content, [string]$Text, [string]$Label)
|
|
if ($Content.Contains($Text)) { throw "$Label 仍保留:$Text" }
|
|
}
|
|
|
|
$owner = Read-ProjectFile 'utils/auth-verification.js'
|
|
$adapter = Read-ProjectFile 'static/tac/js/jiapu-tac-adapter.js'
|
|
$component = Read-ProjectFile 'components/TacVerification.vue'
|
|
$api = Read-ProjectFile 'utils/api.js'
|
|
$a01 = Read-ProjectFile 'pages/auth/a01-entry.vue'
|
|
$a04 = Read-ProjectFile 'pages/auth/a04-register.vue'
|
|
$a05 = Read-ProjectFile 'pages/auth/a05-reset-password.vue'
|
|
|
|
$vendorAssets = [ordered]@{
|
|
'static/tac/css/tac.css' = '181694518971a9f991d551b6a6e6dab2bf750f940bfc1673a158213f92eedbe0'
|
|
'static/tac/js/tac.min.js' = '505f73c051908d7b805db458990790be3e91f792c4001cec0ea9377d7d302b55'
|
|
'static/tac/images/icon.png' = '53e37ffc5bb81c46e6306b7d61d2eaa3de57e47ca6cdb8d5210022ae815c21c2'
|
|
'static/tac/images/dun.jpeg' = 'd9178a8c4cca36e3df6c3acd7e895ce9d34dd60ef3f1cf4a70c94d4324ed96e7'
|
|
}
|
|
foreach ($entry in $vendorAssets.GetEnumerator()) {
|
|
$absolutePath = Join-Path $root $entry.Key
|
|
if (-not (Test-Path -LiteralPath $absolutePath)) { throw "缺少用户提供的 TAC 资产:$($entry.Key)" }
|
|
$actualHash = (Get-FileHash -LiteralPath $absolutePath -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
if ($actualHash -ne $entry.Value) { throw "用户提供的 TAC 供应商资产发生漂移:$($entry.Key)" }
|
|
}
|
|
|
|
foreach ($token in @('lang="renderjs"', './static/tac/css/tac.css', './static/tac/js/tac.min.js', './static/tac/js/jiapu-tac-adapter.js', 'window.TAC', 'window.CaptchaConfig', 'window.JiapuTacAdapter', 'xhr.status >= 200 && xhr.status < 300', '$ownerInstance.callMethod', 'activeXhr', 'xhr.timeout = 15000', 'xhr.ontimeout', 'xhr.abort()', 'config.doSendRequest = (options) => this.sendStrictRequest(options)', 'this.tac = new window.TAC(config);')) {
|
|
Require-Text -Content $component -Text $token -Label 'TacVerification'
|
|
}
|
|
Reject-Text -Content $component -Text 'config.doSendRequest = this.sendStrictRequest' -Label '失去 renderjs 实例上下文的传输函数'
|
|
$staleGuard = 'if (generation !== this.generation || !this.requestContext || this.requestContext.visible !== true) return;'
|
|
if ([regex]::Matches($component, [regex]::Escape($staleGuard)).Count -lt 2) {
|
|
throw 'TacVerification 必须在资源加载成功与失败两条分支都拒绝过期代次'
|
|
}
|
|
Require-Text -Content $adapter -Text 'payload: { track:' -Label 'TAC payload.track 适配器'
|
|
foreach ($unsafe in @('code === 200 && response.data', 'passed !== false', "validToken: 'mock", 'mock-valid-token')) {
|
|
Reject-Text -Content ($owner + $adapter + $component + $api) -Text $unsafe -Label 'TAC 安全合同'
|
|
}
|
|
foreach ($customVisual in @('tac-panel', 'tac-heading', 'tac-tool', 'logoUrl:', 'i18n:', 'new window.TAC(config, {')) {
|
|
Reject-Text -Content $component -Text $customVisual -Label 'TAC 供应商原生呈现'
|
|
}
|
|
|
|
foreach ($method in @('getCaptchaRequirement', 'sendSmsCode', 'loginWithPassword', 'loginWithSms', 'registerWithPassword', 'resetPassword')) {
|
|
Require-Text -Content $api -Text "async $method" -Label '认证 API'
|
|
}
|
|
$authApiMatch = [regex]::Match($api, '(?s)async getCaptchaRequirement.*?(?=\s+async getProfile)')
|
|
if (-not $authApiMatch.Success) { throw '无法定位唯一认证 API 区段' }
|
|
Reject-Text -Content $authApiMatch.Value -Text "return { success: true }" -Label '认证短信 mock'
|
|
Reject-Text -Content $authApiMatch.Value -Text "mock-session-token" -Label '认证会话 mock'
|
|
Require-Text -Content $api -Text 'operationCode, phone, validToken' -Label '短信票据请求'
|
|
foreach ($token in @('const requestAuth =', 'strictEnvelope: true', 'expectedStatus: 200', "hasOwnProperty.call(data, 'data')")) {
|
|
Require-Text -Content $api -Text $token -Label '认证严格响应 owner'
|
|
}
|
|
foreach ($token in @('const REQUEST_TIMEOUT_MS = 15000', 'export const createRequestController', "error?.code === 'REQUEST_CANCELLED'", 'task?.abort?.()')) {
|
|
Require-Text -Content $api -Text $token -Label '认证请求生命周期 owner'
|
|
}
|
|
|
|
foreach ($page in @($a01, $a04, $a05)) {
|
|
Require-Text -Content $page -Text '<TacVerification' -Label '认证页面'
|
|
Require-Text -Content $page -Text 'normalizeTacSuccess' -Label '认证页面'
|
|
Reject-Text -Content $page -Text '滑动验证待接口接入' -Label '认证页面占位'
|
|
Reject-Text -Content $page -Text '行为验证接口待接入' -Label '认证页面占位'
|
|
Reject-Text -Content $page -Text 'verification-layer' -Label '旧假验证浮层'
|
|
Require-Text -Content $page -Text 'isAuthPhone' -Label '认证手机号唯一校验器'
|
|
Reject-Text -Content $page -Text '/^1\d{10}$/' -Label '认证页面重复手机号规则'
|
|
Require-Text -Content $page -Text 'submitting: submitting.value || sendingCode.value' -Label '认证异步返回守卫'
|
|
Require-Text -Content $page -Text '"block-submitting"' -Label '认证异步返回守卫'
|
|
Require-Text -Content $page -Text 'let pageActive = true' -Label '认证页面卸载代次'
|
|
Require-Text -Content $page -Text 'pageActive = false' -Label '认证页面卸载代次'
|
|
Require-Text -Content $page -Text 'createRequestController' -Label '认证页面可取消请求'
|
|
Require-Text -Content $page -Text 'isRequestCancelled' -Label '认证页面取消静默处理'
|
|
Require-Text -Content $page -Text 'authRequestController.abort()' -Label '认证页面离页中止请求'
|
|
if ([regex]::Matches($page, [regex]::Escape('{ requestController: authRequestController }')).Count -lt 3) {
|
|
throw '认证页面的策略、短信与最终提交必须都绑定页面请求控制器'
|
|
}
|
|
Require-Text -Content $page -Text 'const requestedPhone = phone.value' -Label '认证手机号请求快照'
|
|
Require-Text -Content $page -Text 'subject: requestedPhone' -Label '认证手机号请求快照'
|
|
$phoneLock = if ($page -eq $a01) {
|
|
':disabled="sendingCode || submitting || tacVisible || authenticationCommitted || (cooldownSeconds > 0 && phone.length > 0)"'
|
|
} elseif ($page -eq $a04) {
|
|
':disabled="sendingCode || submitting || tacVisible || registrationCommitted || (cooldownSeconds > 0 && phone.length > 0)"'
|
|
} else {
|
|
':disabled="sendingCode || submitting || (cooldownSeconds > 0 && phone.length > 0)"'
|
|
}
|
|
Require-Text -Content $page -Text $phoneLock -Label '短信流程手机号锁定'
|
|
if ([regex]::Matches($page, [regex]::Escape('if (!pageActive) return;')).Count -lt 3) {
|
|
throw '认证页面必须在策略、短信与最终提交的异步回流前拒绝卸载后的旧结果'
|
|
}
|
|
}
|
|
foreach ($pageContract in @(
|
|
@{ Content = $a01; Loading = '登录中…' },
|
|
@{ Content = $a04; Loading = '注册中…' },
|
|
@{ Content = $a05; Loading = '提交中…' }
|
|
)) {
|
|
Require-Text -Content $pageContract.Content -Text '请求中…' -Label '认证短信加载文案'
|
|
Require-Text -Content $pageContract.Content -Text $pageContract.Loading -Label '认证提交加载文案'
|
|
}
|
|
foreach ($token in @('const blockBusyAction = () =>', 'if (blockBusyAction()) return;', 'const prepareForgotPassword = () => {', 'const prepareRegister = () => {')) {
|
|
Require-Text -Content $a01 -Text $token -Label 'A01 忙碌动作门禁'
|
|
}
|
|
|
|
foreach ($token in @('AUTH_VERIFICATION_OPERATION.PASSWORD_LOGIN', 'AUTH_VERIFICATION_OPERATION.SMS_LOGIN', 'appApi.loginWithPassword', 'appApi.loginWithSms', 'calcMD5(password.value)', 'const preparePasswordLogin = async () =>', '/^\d{4}$/')) {
|
|
Require-Text -Content $a01 -Text $token -Label 'A01'
|
|
}
|
|
Reject-Text -Content $a01 -Text 'PASSWORD_TAC_BLOCKED_MESSAGE' -Label 'A01 旧密码登录硬关闭'
|
|
Reject-Text -Content $a01 -Text '/^\d{6}$/' -Label 'A01 六位短信码'
|
|
|
|
foreach ($token in @('AUTH_VERIFICATION_OPERATION.REGISTER', 'v-model.trim="verificationCode"', 'appApi.registerWithPassword', 'calcMD5(password.value)', '/^\d{4}$/', 'goRoot("G01")')) {
|
|
Require-Text -Content $a04 -Text $token -Label 'A04'
|
|
}
|
|
|
|
foreach ($token in @('AUTH_VERIFICATION_OPERATION.FORGOT_PASSWORD', 'appApi.resetPassword', 'calcMD5(password.value)', '/^\d{4}$/', 'await appApi.resetPassword')) {
|
|
Require-Text -Content $a05 -Text $token -Label 'A05'
|
|
}
|
|
Reject-Text -Content $a05 -Text '/^\d{6}$/' -Label 'A05 六位短信码'
|
|
|
|
Write-Output 'AUTH-TAC-INTEGRATION-CONTRACT PASS'
|