49 lines
4.4 KiB
PHP
49 lines
4.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
$root = dirname(__DIR__);
|
|
$controller = file_get_contents($root . '/app/adminapi/controller/tcm/FollowupAudioController.php');
|
|
$logic = file_get_contents($root . '/app/adminapi/logic/tcm/FollowupAudioLogic.php');
|
|
$access = file_get_contents($root . '/app/common/service/followupaudio/FollowupAudioAccess.php');
|
|
$middleware = file_get_contents($root . '/app/adminapi/http/middleware/AuthMiddleware.php');
|
|
$stream = file_get_contents($root . '/app/common/service/followupaudio/FollowupAudioStream.php');
|
|
$console = file_get_contents($root . '/config/console.php');
|
|
$apply = file_get_contents($root . '/app/common/service/followupaudio/FollowupAudioApply.php');
|
|
$gate = file_get_contents($root . '/app/common/service/followupaudio/FollowupAudioGate.php');
|
|
$checks = 0;
|
|
function expectEndpoint(bool $condition, string $message): void
|
|
{
|
|
global $checks;
|
|
if (!$condition) { fwrite(STDERR, "FAIL: {$message}\n"); exit(1); }
|
|
$checks++;
|
|
}
|
|
expectEndpoint(strpos($middleware, "str_starts_with(\$accessUri, 'tcm.followupaudio/')") < strpos($middleware, '// 全部路由'), 'route group precedes unregistered-route bypass');
|
|
expectEndpoint(str_contains($middleware, "in_array('tcm.diagnosis/edit', \$uris, true)"), 'explicit page permission');
|
|
expectEndpoint(str_contains($access, "Db::name('admin')->where('id', \$actor)->whereNull('delete_time')->where('disable', 0)->lock(true)"), 'active actor refresh');
|
|
expectEndpoint(str_contains($access, 'MyPatientLogic::applyScope($query, $actor, $info)') && str_contains($access, '$query->lock(true)->find()'), 'row management guard');
|
|
expectEndpoint(str_contains($access, 'tcm.diagnosis/dailyRecord'), 'daily permission guard');
|
|
expectEndpoint(str_contains($access, "\$task['patient_id'] !== (int) (\$diagnosis['patient_id']"), 'historical patient rebind guard');
|
|
expectEndpoint(str_contains($controller, 'array_diff(array_keys($params), $allowed)'), 'request field allowlist');
|
|
expectEndpoint(str_contains($controller, 'count($items) > 500'), 'bounded review items');
|
|
expectEndpoint(str_contains($controller, "'code' => 'FOLLOWUP_AUDIO_STALE_REVIEW'"), 'typed stale review response');
|
|
expectEndpoint(substr_count($controller, 'Logic::requireEnabled(true)') >= 3, 'upload capability gate');
|
|
expectEndpoint(str_contains($logic, "Store::ready(\$p['model_key'])"), 'per-model mode-aware readiness gate');
|
|
expectEndpoint(str_contains($logic, "'models' => \$enabled ? ProviderConfig::readyModels() : []"), 'only scoped fingerprint-ready server labels advertised');
|
|
expectEndpoint(str_contains($logic, '$verified = Store::verified()') && str_contains($logic, "'audio_verified' => \$verified"), 'full accuracy verification remains separate');
|
|
expectEndpoint(str_contains($logic, "'preview_only' => \$preview") && str_contains($logic, "'can_review' => \$enabled && \$ready"), 'explicit preview and review capabilities');
|
|
expectEndpoint(strpos($apply, 'FollowupAudioGate::assertMayApply();') < strpos($apply, '$connection = Db::connect()'), 'preview hard denial before any apply DB access');
|
|
expectEndpoint(strpos($apply, 'FollowupAudioGate::assertMayApply($task);') < strpos($apply, "if (\$task['status'] === 'applied')"), 'persistent preview origin denied before idempotent applied path');
|
|
expectEndpoint(str_contains($gate, 'FOLLOWUP_AUDIO_PREVIEW_ONLY') && str_contains($gate, 'FOLLOWUP_AUDIO_PREVIEW_SCOPE_DENIED'), 'stable fail-closed preview errors');
|
|
expectEndpoint(!str_contains($logic, 'Dify 音频能力') && !str_contains($logic, 'api_key') && !str_contains($logic, 'base_url'),
|
|
'provider-neutral public capabilities never expose credentials or addresses');
|
|
expectEndpoint(str_contains($logic, "(int) \$upload['diagnosis_id'] !== \$p['diagnosis_id']"), 'upload/diagnosis binding');
|
|
expectEndpoint(str_contains($logic, "new \\DateTimeZone('Asia/Shanghai')"), 'explicit local date anchor timezone');
|
|
expectEndpoint(str_contains($stream, 'private, no-store, max-age=0'), 'private playback response');
|
|
expectEndpoint(str_contains($stream, 'fread($stream, 1048576)') && !str_contains($stream, 'file_get_contents'), 'bounded playback memory');
|
|
expectEndpoint(str_contains($controller, "['id'], function (array \$p): FollowupAudioStream"), 'playback no query token');
|
|
foreach (['work', 'probe', 'cleanup'] as $command) {
|
|
expectEndpoint(str_contains($console, "followup-audio:{$command}"), 'independent command ' . $command);
|
|
}
|
|
echo "Followup audio endpoints: {$checks} source-contract checks passed\n";
|