Files
xuetang/server/tests/PrescriptionAiSecretSafetyTest.php
2026-09-08 11:40:15 +08:00

90 lines
3.3 KiB
PHP

<?php
declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
use app\common\service\DifyChatService;
$app = new think\App(dirname(__DIR__));
$app->initialize();
function assertSecretSafe(array $result, string $secret, string $message): void
{
$serialized = json_encode($result, JSON_UNESCAPED_UNICODE) ?: '';
if (str_contains($serialized, $secret)) {
fwrite(STDERR, "FAIL: {$message}\n");
exit(1);
}
}
$qwenSecret = 'unit-test-qwen-sensitive-placeholder';
$openAiSecret = 'unit-test-openai-sensitive-placeholder';
$baseConfig = [
'enable' => false,
'base_url' => 'https://ai.example.test/v1',
'timeout' => 90,
'models' => [
'qwen' => ['name' => 'qwen-test', 'label' => 'Qwen', 'api_key' => $qwenSecret],
'openai' => ['name' => 'openai-test', 'label' => 'OpenAI', 'api_key' => $openAiSecret],
],
];
function assertAllSecretsSafe(array $result, array $secrets, string $message): void
{
foreach ($secrets as $secret) {
assertSecretSafe($result, $secret, $message);
}
}
$secrets = [$qwenSecret, $openAiSecret];
$resolveProfile = (new ReflectionClass(DifyChatService::class))->getMethod('resolveProfileConfig');
$resolvedQwen = $resolveProfile->invoke(null, $baseConfig, 'qwen');
$resolvedOpenAi = $resolveProfile->invoke(null, $baseConfig, 'openai');
if (
!is_array($resolvedQwen)
|| !is_array($resolvedOpenAi)
|| ($resolvedQwen['api_key'] ?? null) !== $qwenSecret
|| ($resolvedOpenAi['api_key'] ?? null) !== $openAiSecret
) {
fwrite(STDERR, "FAIL: each model key must resolve only its own server credential\n");
exit(1);
}
if ($resolveProfile->invoke(null, $baseConfig, 'other') !== null) {
fwrite(STDERR, "FAIL: non-whitelisted profile must not resolve server configuration\n");
exit(1);
}
config($baseConfig, 'prescription_ai');
$disabled = DifyChatService::chat('qwen', [], 'test', 'test-user');
assertAllSecretsSafe($disabled, $secrets, 'disabled response must not expose credentials');
$enabledConfig = $baseConfig;
$enabledConfig['enable'] = true;
config($enabledConfig, 'prescription_ai');
foreach (['other', 'QWEN', ' openai', 'gpt-5.6-sol'] as $invalidProfile) {
$invalid = DifyChatService::chat($invalidProfile, [], 'test', 'test-user');
if (($invalid['error_code'] ?? '') !== 'INVALID_PROFILE') {
fwrite(STDERR, "FAIL: invalid profile must be rejected before upstream work\n");
exit(1);
}
assertAllSecretsSafe($invalid, $secrets, 'invalid-profile response must not expose credentials');
}
$invalidUrlConfig = $baseConfig;
$invalidUrlConfig['enable'] = true;
$invalidUrlConfig['base_url'] = 'file:///not-allowed';
config($invalidUrlConfig, 'prescription_ai');
$invalidUrl = DifyChatService::chat('qwen', [], 'test', 'test-user');
assertAllSecretsSafe($invalidUrl, $secrets, 'invalid URL response must not expose credentials');
$headerInjectionConfig = $baseConfig;
$headerInjectionConfig['enable'] = true;
$headerInjectionConfig['models']['qwen']['api_key'] = $qwenSecret . "\r\nInjected: value";
config($headerInjectionConfig, 'prescription_ai');
$headerInjection = DifyChatService::chat('qwen', [], 'test', 'test-user');
assertAllSecretsSafe($headerInjection, $secrets, 'invalid credential response must not expose credentials');
echo "Prescription AI secret safety: OK\n";