豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit ebe7e85

Browse files
committed
1 parent f04db79 commit ebe7e85

File tree

4 files changed

+89
-42
lines changed

4 files changed

+89
-42
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
## Unreleased
44

5+
- Added `craft\helpers\App::resourcePathByUri()`.
56
- Fixed a bug where global set GraphQL query caches weren’t getting invalidated when global sets were updated. ([#18479](https://github.com/craftcms/cms/issues/18479))
67
- Fixed a bug where `users/suspend-user` and `users/unsuspend-user` actions required that the logged-in user have control panel access. ([#18485](https://github.com/craftcms/cms/issues/18485))
78
- Fixed a bug where flipping an image within the Image Editor didn’t always work. ([#18486](https://github.com/craftcms/cms/issues/18486))
89
- Fixed a bug where SVG files missing their `width` and `height` attributes weren’t getting them set as expected.
910
- Fixed an error that occurred if a template referenced a preloaded Single entry followed by a null coalescing operator. ([#18503](https://github.com/craftcms/cms/issues/18503))
1011
- Fixed a bug where links within Redactor fields were getting `target="_blank"` added to them. ([#18500](https://github.com/craftcms/cms/issues/18500))
11-
- Fixed a [moderate-severity](https://github.com/craftcms/cms/security/policy#severity--remediation) SSRF vulnerability. (GHSA-3m9m-24vh-39wx)
12+
- Fixed [moderate-severity](https://github.com/craftcms/cms/security/policy#severity--remediation) SSRF vulnerabilities. (GHSA-3m9m-24vh-39wx, GHSA-95wr-3f2v-v2wh)
1213

1314
## 4.17.8 - 2026-02-25
1415

src/controllers/AppController.php

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use craft\helpers\Html;
2424
use craft\helpers\Json;
2525
use craft\helpers\Session;
26+
use craft\helpers\StringHelper;
2627
use craft\helpers\Update as UpdateHelper;
2728
use craft\helpers\UrlHelper;
2829
use craft\models\Update;
@@ -32,6 +33,7 @@
3233
use craft\web\ServiceUnavailableHttpException;
3334
use DateInterval;
3435
use Throwable;
36+
use yii\base\InvalidArgumentException;
3537
use yii\base\InvalidConfigException;
3638
use yii\web\BadRequestHttpException;
3739
use yii\web\Cookie;
@@ -110,17 +112,23 @@ public function actionResourceJs(string $url): Response
110112
{
111113
$this->requireCpRequest();
112114

113-
if (!str_starts_with($url, Craft::$app->getAssetManager()->baseUrl)) {
115+
$assetManager = Craft::$app->getAssetManager();
116+
$baseUrl = StringHelper::ensureRight($assetManager->baseUrl, '/');
117+
if (!str_starts_with($url, $baseUrl)) {
114118
throw new BadRequestHttpException("$url does not appear to be a resource URL");
115119
}
116120

117-
// Close the PHP session in case this takes a while
118-
Session::close();
121+
$resourceUri = preg_replace('/^(.*)\?.*/', '$1', substr($url, strlen($baseUrl)));
119122

120-
$response = Craft::createGuzzleClient()->get($url);
121-
$this->response->setCacheHeaders();
122-
$this->response->getHeaders()->set('content-type', 'application/javascript');
123-
return $this->asRaw($response->getBody());
123+
try {
124+
$publishedPath = App::resourcePathByUri($resourceUri);
125+
} catch (InvalidArgumentException $e) {
126+
throw new BadRequestHttpException($e->getMessage(), previous: $e);
127+
}
128+
129+
return $this->response->sendFile($publishedPath, null, [
130+
'inline' => true,
131+
]);
124132
}
125133

126134
/**

src/helpers/App.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use craft\db\Connection;
1616
use craft\db\mysql\Schema as MysqlSchema;
1717
use craft\db\pgsql\Schema as PgsqlSchema;
18+
use craft\db\Query;
19+
use craft\db\Table;
1820
use craft\elements\User;
1921
use craft\enums\LicenseKeyStatus;
2022
use craft\errors\InvalidPluginException;
@@ -41,6 +43,7 @@
4143
use yii\base\Exception;
4244
use yii\base\InvalidArgumentException;
4345
use yii\base\InvalidValueException;
46+
use yii\db\Exception as DbException;
4447
use yii\helpers\Inflector;
4548
use yii\mutex\FileMutex;
4649
use yii\mutex\MysqlMutex;
@@ -1483,4 +1486,69 @@ public static function configure(object $object, array $properties): void
14831486
$object->$name = $value;
14841487
}
14851488
}
1489+
1490+
/**
1491+
* Returns the path for a CP resource by its URI.
1492+
*
1493+
* @param string $uri
1494+
* @return string|null
1495+
* @throws InvalidArgumentException
1496+
* @since 4.17.9
1497+
*/
1498+
public static function resourcePathByUri(string $uri): ?string
1499+
{
1500+
if (!Path::ensurePathIsContained($uri)) {
1501+
throw new InvalidArgumentException("Invalid resource: $uri");
1502+
}
1503+
1504+
$assetManager = Craft::$app->getAssetManager();
1505+
1506+
// If the file already exists, return that
1507+
$path = "$assetManager->basePath/$uri";
1508+
if (file_exists($path)) {
1509+
return $path;
1510+
}
1511+
1512+
// Otherwise, publish it
1513+
$slash = strpos($uri, '/');
1514+
$hash = substr($uri, 0, $slash);
1515+
$sourcePath = self::resourceSourcePathByHash($hash, $assetManager);
1516+
1517+
if (!$sourcePath) {
1518+
return null;
1519+
}
1520+
1521+
$filePath = substr($uri, strlen($hash) + 1);
1522+
1523+
// Publish the directory
1524+
[$publishedDir] = $assetManager->publish(Craft::getAlias($sourcePath));
1525+
1526+
$publishedPath = $publishedDir . DIRECTORY_SEPARATOR . $filePath;
1527+
if (!file_exists($publishedPath)) {
1528+
throw new InvalidArgumentException("$filePath does not exist.");
1529+
}
1530+
1531+
return $publishedPath;
1532+
}
1533+
1534+
/**
1535+
* Returns the source path for a CP resource by its hash.
1536+
*
1537+
* @param string $hash
1538+
* @return string|false
1539+
* @since 4.17.9
1540+
*/
1541+
private static function resourceSourcePathByHash(string $hash, AssetManager $assetManager): string|false
1542+
{
1543+
try {
1544+
return (new Query())
1545+
->select(['path'])
1546+
->from(Table::RESOURCEPATHS)
1547+
->where(['hash' => $hash])
1548+
->scalar();
1549+
} catch (DbException) {
1550+
// Craft isn't installed yet. See if it's cached as a fallback.
1551+
return Craft::$app->getCache()->get($assetManager->getCacheKeyForPathHash($hash));
1552+
}
1553+
}
14861554
}

src/web/Application.php

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
use Craft;
1111
use craft\base\ApplicationTrait;
12-
use craft\db\Query;
1312
use craft\db\Table;
1413
use craft\debug\DeprecatedPanel;
1514
use craft\debug\DumpPanel;
@@ -38,7 +37,6 @@
3837
use yii\base\InvalidArgumentException;
3938
use yii\base\InvalidConfigException;
4039
use yii\base\InvalidRouteException;
41-
use yii\db\Exception as DbException;
4240
use yii\debug\Module as YiiDebugModule;
4341
use yii\debug\panels\AssetPanel;
4442
use yii\debug\panels\DbPanel;
@@ -523,25 +521,11 @@ private function _processResourceRequest(): void
523521
}
524522

525523
$resourceUri = substr($requestPath, strlen($resourceBaseUri));
526-
$slash = strpos($resourceUri, '/');
527-
$hash = substr($resourceUri, 0, $slash);
528-
$sourcePath = $this->resourceSourcePathByHash($hash);
529524

530-
if (!$sourcePath) {
531-
return;
532-
}
533-
534-
$filePath = substr($resourceUri, strlen($hash) + 1);
535-
if (!Path::ensurePathIsContained($filePath)) {
536-
throw new BadRequestHttpException('Invalid resource path: ' . $filePath);
537-
}
538-
539-
// Publish the directory
540-
[$publishedDir] = $this->getAssetManager()->publish(Craft::getAlias($sourcePath));
541-
542-
$publishedPath = $publishedDir . DIRECTORY_SEPARATOR . $filePath;
543-
if (!file_exists($publishedPath)) {
544-
throw new NotFoundHttpException("$filePath does not exist.");
525+
try {
526+
$publishedPath = App::resourcePathByUri($resourceUri);
527+
} catch (InvalidArgumentException $e) {
528+
throw new BadRequestHttpException($e->getMessage(), previous: $e);
545529
}
546530

547531
$response = $this->getResponse();
@@ -558,20 +542,6 @@ private function _processResourceRequest(): void
558542
$this->end();
559543
}
560544

561-
private function resourceSourcePathByHash(string $hash): string|false
562-
{
563-
try {
564-
return (new Query())
565-
->select(['path'])
566-
->from(Table::RESOURCEPATHS)
567-
->where(['hash' => $hash])
568-
->scalar();
569-
} catch (DbException) {
570-
// Craft isn't installed yet. See if it's cached as a fallback.
571-
return Craft::$app->getCache()->get(Craft::$app->getAssetManager()->getCacheKeyForPathHash($hash));
572-
}
573-
}
574-
575545
/**
576546
* Processes install requests.
577547
*

0 commit comments

Comments
 (0)