ghost 5.130.1 → 5.130.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/{tryghost-i18n-5.130.1.tgz → tryghost-i18n-5.130.3.tgz} +0 -0
- package/core/built/admin/assets/{chunk.524.8a4cbb5b8ae5cf01697e.js → chunk.524.d4dce2126c430c48812b.js} +8 -8
- package/core/built/admin/assets/{chunk.582.7b14e9ac2e84d285035e.js → chunk.582.7031dee8f98fced7de40.js} +9 -9
- package/core/built/admin/assets/{chunk.728.077782a432061228b91e.js → chunk.728.985c45ad584b4b91ca60.js} +19 -19
- package/core/built/admin/assets/{ghost-280b83af263b51bc4d6ce5bd8f536096.js → ghost-bf886dc6db7f0d5d41ebe6a468fadc90.js} +3 -3
- package/core/built/admin/assets/ghost-dark-a6901b9bab812b089d0c87e14735d69e.css +1 -0
- package/core/built/admin/assets/ghost-e474a058646b6df157de25ea1cd978d5.css +1 -0
- package/core/built/admin/index.html +4 -4
- package/core/server/services/explore-ping/ExplorePingService.js +3 -1
- package/core/server/services/members/members-api/controllers/RouterController.js +28 -10
- package/core/server/services/members/members-api/utils/normalize-email.js +31 -0
- package/core/server/services/settings/SettingsBREADService.js +5 -1
- package/core/server/services/settings/settings-service.js +2 -0
- package/package.json +4 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/yarn.lock +4 -4
- package/core/built/admin/assets/ghost-3d0ad0c58f433d5735532bf25d4fd423.css +0 -1
- package/core/built/admin/assets/ghost-dark-f19869a3fd0ef48c525149b9c87e4241.css +0 -1
- /package/core/built/admin/assets/{chunk.728.077782a432061228b91e.js.LICENSE.txt → chunk.728.985c45ad584b4b91ca60.js.LICENSE.txt} +0 -0
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
</style>
|
|
29
29
|
|
|
30
30
|
<link integrity="" rel="stylesheet" href="assets/vendor-0ede59da8efb5e28fa929557f7ff7154.css">
|
|
31
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
31
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-e474a058646b6df157de25ea1cd978d5.css" title="light">
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
</head>
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
49
49
|
|
|
50
50
|
<script src="assets/vendor-aed0068cf9b67d042dd23a6343545b7b.js"></script>
|
|
51
|
-
<script src="assets/chunk.728.
|
|
52
|
-
<script src="assets/chunk.524.
|
|
53
|
-
<script src="assets/ghost-
|
|
51
|
+
<script src="assets/chunk.728.985c45ad584b4b91ca60.js"></script>
|
|
52
|
+
<script src="assets/chunk.524.d4dce2126c430c48812b.js"></script>
|
|
53
|
+
<script src="assets/ghost-bf886dc6db7f0d5d41ebe6a468fadc90.js"></script>
|
|
54
54
|
</body>
|
|
55
55
|
</html>
|
|
@@ -33,7 +33,9 @@ module.exports = class ExplorePingService {
|
|
|
33
33
|
ghost: this.ghostVersion.full,
|
|
34
34
|
site_uuid: this.settingsCache.get('site_uuid'),
|
|
35
35
|
url: this.config.get('url'),
|
|
36
|
-
theme: this.settingsCache.get('active_theme')
|
|
36
|
+
theme: this.settingsCache.get('active_theme'),
|
|
37
|
+
facebook: this.settingsCache.get('facebook'),
|
|
38
|
+
twitter: this.settingsCache.get('twitter')
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
try {
|
|
@@ -4,6 +4,7 @@ const sanitizeHtml = require('sanitize-html');
|
|
|
4
4
|
const {BadRequestError, NoPermissionError, UnauthorizedError, DisabledFeatureError, NotFoundError} = require('@tryghost/errors');
|
|
5
5
|
const errors = require('@tryghost/errors');
|
|
6
6
|
const {isEmail} = require('@tryghost/validator');
|
|
7
|
+
const normalizeEmail = require('../utils/normalize-email');
|
|
7
8
|
|
|
8
9
|
const messages = {
|
|
9
10
|
emailRequired: 'Email is required.',
|
|
@@ -585,6 +586,23 @@ module.exports = class RouterController {
|
|
|
585
586
|
});
|
|
586
587
|
}
|
|
587
588
|
|
|
589
|
+
// Normalize email to prevent homograph attacks
|
|
590
|
+
let normalizedEmail;
|
|
591
|
+
|
|
592
|
+
try {
|
|
593
|
+
normalizedEmail = normalizeEmail(email);
|
|
594
|
+
|
|
595
|
+
if (normalizedEmail !== email) {
|
|
596
|
+
logging.info(`Email normalized from ${email} to ${normalizedEmail} for magic link`);
|
|
597
|
+
}
|
|
598
|
+
} catch (err) {
|
|
599
|
+
logging.error(`Failed to normalize [${email}]: ${err.message}`);
|
|
600
|
+
|
|
601
|
+
throw new errors.BadRequestError({
|
|
602
|
+
message: tpl(messages.invalidEmail)
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
|
|
588
606
|
if (honeypot) {
|
|
589
607
|
logging.warn('Honeypot field filled, this is likely a bot');
|
|
590
608
|
|
|
@@ -606,9 +624,9 @@ module.exports = class RouterController {
|
|
|
606
624
|
|
|
607
625
|
try {
|
|
608
626
|
if (emailType === 'signup' || emailType === 'subscribe') {
|
|
609
|
-
await this._handleSignup(req, referrer);
|
|
627
|
+
await this._handleSignup(req, normalizedEmail, referrer);
|
|
610
628
|
} else {
|
|
611
|
-
await this._handleSignin(req, referrer);
|
|
629
|
+
await this._handleSignin(req, normalizedEmail, referrer);
|
|
612
630
|
}
|
|
613
631
|
|
|
614
632
|
res.writeHead(201);
|
|
@@ -626,7 +644,7 @@ module.exports = class RouterController {
|
|
|
626
644
|
}
|
|
627
645
|
}
|
|
628
646
|
|
|
629
|
-
async _handleSignup(req, referrer = null) {
|
|
647
|
+
async _handleSignup(req, normalizedEmail, referrer = null) {
|
|
630
648
|
if (!this._allowSelfSignup()) {
|
|
631
649
|
if (this._settingsCache.get('members_signup_access') === 'paid') {
|
|
632
650
|
throw new errors.BadRequestError({
|
|
@@ -640,14 +658,14 @@ module.exports = class RouterController {
|
|
|
640
658
|
}
|
|
641
659
|
|
|
642
660
|
const blockedEmailDomains = this._settingsCache.get('all_blocked_email_domains');
|
|
643
|
-
const emailDomain =
|
|
661
|
+
const emailDomain = normalizedEmail.split('@')[1]?.toLowerCase();
|
|
644
662
|
if (emailDomain && blockedEmailDomains.includes(emailDomain)) {
|
|
645
663
|
throw new errors.BadRequestError({
|
|
646
664
|
message: tpl(messages.blockedEmailDomain)
|
|
647
665
|
});
|
|
648
666
|
}
|
|
649
667
|
|
|
650
|
-
const {
|
|
668
|
+
const {emailType} = req.body;
|
|
651
669
|
|
|
652
670
|
const tokenData = {
|
|
653
671
|
labels: req.body.labels,
|
|
@@ -657,13 +675,13 @@ module.exports = class RouterController {
|
|
|
657
675
|
attribution: await this._memberAttributionService.getAttribution(req.body.urlHistory)
|
|
658
676
|
};
|
|
659
677
|
|
|
660
|
-
return await this._sendEmailWithMagicLink({email, tokenData, requestedType: emailType, referrer});
|
|
678
|
+
return await this._sendEmailWithMagicLink({email: normalizedEmail, tokenData, requestedType: emailType, referrer});
|
|
661
679
|
}
|
|
662
680
|
|
|
663
|
-
async _handleSignin(req, referrer = null) {
|
|
664
|
-
const {
|
|
681
|
+
async _handleSignin(req, normalizedEmail, referrer = null) {
|
|
682
|
+
const {emailType} = req.body;
|
|
665
683
|
|
|
666
|
-
const member = await this._memberRepository.get({email});
|
|
684
|
+
const member = await this._memberRepository.get({email: normalizedEmail});
|
|
667
685
|
|
|
668
686
|
if (!member) {
|
|
669
687
|
throw new errors.BadRequestError({
|
|
@@ -672,7 +690,7 @@ module.exports = class RouterController {
|
|
|
672
690
|
}
|
|
673
691
|
|
|
674
692
|
const tokenData = {};
|
|
675
|
-
return await this._sendEmailWithMagicLink({email, tokenData, requestedType: emailType, referrer});
|
|
693
|
+
return await this._sendEmailWithMagicLink({email: normalizedEmail, tokenData, requestedType: emailType, referrer});
|
|
676
694
|
}
|
|
677
695
|
|
|
678
696
|
/**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const punycode = require('punycode/');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes email addresses by converting Unicode domains to ASCII (punycode)
|
|
5
|
+
* This prevents homograph attacks where Unicode characters are used to spoof
|
|
6
|
+
* domains
|
|
7
|
+
*
|
|
8
|
+
* @param {string} email The email address to normalize
|
|
9
|
+
* @returns {string} The normalized email address
|
|
10
|
+
* @throws {Error} When punycode conversion fails
|
|
11
|
+
*/
|
|
12
|
+
function normalizeEmail(email) {
|
|
13
|
+
if (!email || typeof email !== 'string') {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const atIndex = email.lastIndexOf('@');
|
|
18
|
+
|
|
19
|
+
if (atIndex === -1) {
|
|
20
|
+
return email;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const localPart = email.substring(0, atIndex);
|
|
24
|
+
const domainPart = email.substring(atIndex + 1);
|
|
25
|
+
|
|
26
|
+
const asciiDomain = punycode.toASCII(domainPart);
|
|
27
|
+
|
|
28
|
+
return `${localPart}@${asciiDomain}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = normalizeEmail;
|
|
@@ -24,12 +24,14 @@ class SettingsBREADService {
|
|
|
24
24
|
* @param {Object} options.singleUseTokenProvider
|
|
25
25
|
* @param {Object} options.urlUtils
|
|
26
26
|
* @param {Object} options.labsService - labs service instance
|
|
27
|
+
* @param {Object} options.limitsService - limits service instance
|
|
27
28
|
* @param {{service: Object}} options.emailAddressService
|
|
28
29
|
*/
|
|
29
|
-
constructor({SettingsModel, settingsCache, labsService, mail, singleUseTokenProvider, urlUtils, emailAddressService}) {
|
|
30
|
+
constructor({SettingsModel, settingsCache, labsService, limitsService, mail, singleUseTokenProvider, urlUtils, emailAddressService}) {
|
|
30
31
|
this.SettingsModel = SettingsModel;
|
|
31
32
|
this.settingsCache = settingsCache;
|
|
32
33
|
this.labs = labsService;
|
|
34
|
+
this.limitsService = limitsService;
|
|
33
35
|
this.emailAddressService = emailAddressService;
|
|
34
36
|
|
|
35
37
|
/* email verification setup */
|
|
@@ -194,6 +196,8 @@ class SettingsBREADService {
|
|
|
194
196
|
}
|
|
195
197
|
|
|
196
198
|
if (stripeConnectData) {
|
|
199
|
+
await this.limitsService.errorIfWouldGoOverLimit('limitStripeConnect');
|
|
200
|
+
|
|
197
201
|
filteredSettings.push({
|
|
198
202
|
key: 'stripe_connect_publishable_key',
|
|
199
203
|
value: stripeConnectData.public_key
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const events = require('../../lib/common/events');
|
|
6
6
|
const models = require('../../models');
|
|
7
7
|
const labs = require('../../../shared/labs');
|
|
8
|
+
const limits = require('../limits');
|
|
8
9
|
const config = require('../../../shared/config');
|
|
9
10
|
const adapterManager = require('../adapter-manager');
|
|
10
11
|
const SettingsCache = require('../../../shared/settings-cache');
|
|
@@ -30,6 +31,7 @@ const getSettingsBREADServiceInstance = () => {
|
|
|
30
31
|
SettingsModel: models.Settings,
|
|
31
32
|
settingsCache: SettingsCache,
|
|
32
33
|
labsService: labs,
|
|
34
|
+
limitsService: limits,
|
|
33
35
|
mail,
|
|
34
36
|
singleUseTokenProvider: new SingleUseTokenProvider({
|
|
35
37
|
SingleUseTokenModel: models.SingleUseToken,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghost",
|
|
3
|
-
"version": "5.130.
|
|
3
|
+
"version": "5.130.3",
|
|
4
4
|
"description": "The professional publishing platform",
|
|
5
5
|
"author": "Ghost Foundation",
|
|
6
6
|
"homepage": "https://ghost.org",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"@tryghost/helpers": "1.1.97",
|
|
87
87
|
"@tryghost/html-to-plaintext": "1.0.4",
|
|
88
88
|
"@tryghost/http-cache-utils": "0.1.20",
|
|
89
|
-
"@tryghost/i18n": "file:components/tryghost-i18n-5.130.
|
|
89
|
+
"@tryghost/i18n": "file:components/tryghost-i18n-5.130.3.tgz",
|
|
90
90
|
"@tryghost/image-transform": "1.4.6",
|
|
91
91
|
"@tryghost/job-manager": "1.0.3",
|
|
92
92
|
"@tryghost/kg-card-factory": "5.1.2",
|
|
@@ -233,7 +233,7 @@
|
|
|
233
233
|
"@types/bookshelf": "1.2.9",
|
|
234
234
|
"@types/common-tags": "1.8.4",
|
|
235
235
|
"@types/jsonwebtoken": "9.0.10",
|
|
236
|
-
"@types/node": "22.16.
|
|
236
|
+
"@types/node": "22.16.5",
|
|
237
237
|
"@types/node-jose": "1.1.13",
|
|
238
238
|
"@types/nodemailer": "6.4.17",
|
|
239
239
|
"@types/sinon": "17.0.4",
|
|
@@ -274,7 +274,7 @@
|
|
|
274
274
|
"jackspeak": "2.3.6",
|
|
275
275
|
"moment": "2.24.0",
|
|
276
276
|
"moment-timezone": "0.5.45",
|
|
277
|
-
"@tryghost/i18n": "file:components/tryghost-i18n-5.130.
|
|
277
|
+
"@tryghost/i18n": "file:components/tryghost-i18n-5.130.3.tgz"
|
|
278
278
|
},
|
|
279
279
|
"nx": {
|
|
280
280
|
"targets": {
|