修复
This commit is contained in:
@@ -74,8 +74,18 @@ namespace app\common\service {
|
||||
$original = $options;
|
||||
$url = (string) ($options[CURLOPT_URL] ?? '');
|
||||
if (str_starts_with($url, 'https://storage.synthetic.test/')) {
|
||||
$options[CURLOPT_URL] = 'http://127.0.0.1:' . $GLOBALS['attachmentFixturePort'] . substr($url, strlen('https://storage.synthetic.test'));
|
||||
unset($options[CURLOPT_RESOLVE]);
|
||||
$suffix = substr($url, strlen('https://storage.synthetic.test'));
|
||||
if (!empty($GLOBALS['attachmentExerciseAddressFailover'])) {
|
||||
// Preserve the production address count/order while mapping all
|
||||
// network I/O to loopback. The first loopback has no listener.
|
||||
$configured = explode(',', explode(':', $original[CURLOPT_RESOLVE][0], 3)[2]);
|
||||
if (count($configured) !== 2) throw new \RuntimeException('Expected both validated failover addresses');
|
||||
$options[CURLOPT_URL] = 'http://storage.synthetic.test:' . $GLOBALS['attachmentFixturePort'] . $suffix;
|
||||
$options[CURLOPT_RESOLVE] = ['storage.synthetic.test:' . $GLOBALS['attachmentFixturePort'] . ':127.0.0.2,127.0.0.1'];
|
||||
} else {
|
||||
$options[CURLOPT_URL] = 'http://127.0.0.1:' . $GLOBALS['attachmentFixturePort'] . $suffix;
|
||||
unset($options[CURLOPT_RESOLVE]);
|
||||
}
|
||||
} elseif (!str_starts_with($url, 'http://127.0.0.1:' . $GLOBALS['attachmentFixturePort'] . '/')) {
|
||||
throw new \RuntimeException('The synthetic test forbids external connections');
|
||||
}
|
||||
@@ -527,8 +537,8 @@ namespace {
|
||||
$downloaded = $run($remote);
|
||||
attachmentExpect(!empty($downloaded['ok']) && count($requests()) === 2 && $requests()[0]['kind'] === 'download'
|
||||
&& $requests()[0]['authorization'] === '' && $requests()[1]['authorization'] === 'Bearer fixture-only-key', 'remote bytes upload without sharing Dify key with storage');
|
||||
attachmentExpect($attachmentCurlOptions[0][CURLOPT_RESOLVE] === ['storage.synthetic.test:443:8.8.8.8']
|
||||
&& $attachmentCurlOptions[0][CURLOPT_FOLLOWLOCATION] === false, 'validated DNS address is pinned and redirect following disabled');
|
||||
attachmentExpect($attachmentCurlOptions[0][CURLOPT_RESOLVE] === ['storage.synthetic.test:443:8.8.8.8,[2606:4700:4700::1111]']
|
||||
&& $attachmentCurlOptions[0][CURLOPT_FOLLOWLOCATION] === false, 'all validated DNS addresses are pinned and redirect following disabled');
|
||||
$clear(); $dnsCalls = 0;
|
||||
$rebound = $run($remote, ['resolve_host' => static function () use (&$dnsCalls): array { return [++$dnsCalls === 1 ? '8.8.8.8' : '127.0.0.1']; }]);
|
||||
attachmentExpect(!empty($rebound['ok']) && $dnsCalls === 1
|
||||
@@ -540,7 +550,7 @@ namespace {
|
||||
'resolve_host' => static function () use (&$dnsCalls): array { ++$dnsCalls; return ['198.18.0.1']; }]);
|
||||
attachmentExpect(!empty($pinned['ok']) && $dnsCalls === 0 && $attachmentNativeDnsCalls === []
|
||||
&& count($requests()) === 2, 'exact server pin bypasses both unsafe callable and native DNS');
|
||||
attachmentExpect($attachmentCurlOptions[0][CURLOPT_RESOLVE] === ['storage.synthetic.test:443:8.8.8.8']
|
||||
attachmentExpect($attachmentCurlOptions[0][CURLOPT_RESOLVE] === ['storage.synthetic.test:443:8.8.8.8,[2606:4700:4700::1111]']
|
||||
&& $attachmentCurlOptions[0][CURLOPT_URL] === $remote[0]['url']
|
||||
&& $attachmentCurlOptions[0][CURLOPT_SSL_VERIFYPEER] === true
|
||||
&& $attachmentCurlOptions[0][CURLOPT_SSL_VERIFYHOST] === 2
|
||||
@@ -550,6 +560,31 @@ namespace {
|
||||
attachmentExpect(!isset($attachmentCurlOptions[1][CURLOPT_RESOLVE]), 'storage pin never alters the upload endpoint');
|
||||
$clean();
|
||||
|
||||
foreach ([false, true] as $serverPinned) {
|
||||
$clear(); $dnsCalls = 0;
|
||||
$attachmentExerciseAddressFailover = true;
|
||||
$addresses = ['8.8.8.8', '1.1.1.1'];
|
||||
try {
|
||||
$failover = $run($remote, [
|
||||
'resolved_hosts' => $serverPinned ? ['storage.synthetic.test' => $addresses] : [],
|
||||
'resolve_host' => static function () use (&$dnsCalls, $addresses): array { ++$dnsCalls; return $addresses; },
|
||||
]);
|
||||
} finally { $attachmentExerciseAddressFailover = false; }
|
||||
attachmentExpect(!empty($failover['ok']) && $failover['uploaded_indices'] === [1]
|
||||
&& $failover['skipped'] === [] && $dnsCalls === ($serverPinned ? 0 : 1),
|
||||
'unreachable first address falls through to the next validated address without a fresh DNS lookup');
|
||||
attachmentExpect($attachmentCurlOptions[0][CURLOPT_RESOLVE] === ['storage.synthetic.test:443:8.8.8.8,1.1.1.1']
|
||||
&& array_column($requests(), 'kind') === ['download', 'upload']
|
||||
&& $requests()[0]['authorization'] === '' && $requests()[1]['authorization'] === 'Bearer fixture-only-key',
|
||||
'address failover downloads and uploads the attachment once without leaking upload credentials');
|
||||
attachmentExpect($requests()[1]['hash'] === hash_file('sha256', $root . '/public/uploads/first.png')
|
||||
&& $attachmentCurlOptions[0][CURLOPT_SSL_VERIFYPEER] === true
|
||||
&& $attachmentCurlOptions[0][CURLOPT_SSL_VERIFYHOST] === 2
|
||||
&& $attachmentCurlOptions[0][CURLOPT_FOLLOWLOCATION] === false,
|
||||
'failover preserves original bytes, TLS verification and disabled redirects');
|
||||
$clean();
|
||||
}
|
||||
|
||||
$clear();
|
||||
$missingPinnedSource = $run(array_merge($remote, [['type' => 'image',
|
||||
'url' => 'https://storage.synthetic.test/missing-object']]),
|
||||
|
||||
Reference in New Issue
Block a user