Skip to content

Commit

Permalink
Added custom redirects ReDoS validation (TryGhost#20515)
Browse files Browse the repository at this point in the history
refs
[ENG-709](https://linear.app/tryghost/issue/ENG-709/%F0%9F%90%9B-bad-redirects-causing-container-tear-down)

Added validation to prevent RegEx's susceptible to ReDoS from being used
with custom redirects. Also moved error details out of `context` and
into `errorDetails` to be consistent with error logging elsewhere as
well as fix issue in admin-x where blank screen would be shown when an
error occurred during redirects upload (due to logic not accounting for
`context` being an object)
  • Loading branch information
mike182uk authored Jul 2, 2024
1 parent a046ee3 commit b36c235
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 3 deletions.
24 changes: 21 additions & 3 deletions ghost/core/core/server/services/custom-redirects/validation.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const _ = require('lodash');
const tpl = require('@tryghost/tpl');
const errors = require('@tryghost/errors');
const {isSafePattern} = require('redos-detector');

const messages = {
redirectsWrongFormat: 'Incorrect redirects file format.',
Expand Down Expand Up @@ -33,18 +34,35 @@ const validate = (redirects) => {
if (!redirect.from || !redirect.to) {
throw new errors.ValidationError({
message: tpl(messages.redirectsWrongFormat),
context: redirect,
help: tpl(messages.redirectsHelp)
});
}

// Ensure valid regex
try {
// each 'from' property should be a valid RegExp string
new RegExp(redirect.from);
} catch (error) {
throw new errors.ValidationError({
message: tpl(messages.invalidRedirectsFromRegex),
context: redirect,
errorDetails: {
redirect,
invalid: true
},
help: tpl(messages.redirectsHelp)
});
}

// Ensure safe regex
const analysis = isSafePattern(redirect.from);

if (analysis.safe === false) {
throw new errors.ValidationError({
message: tpl(messages.invalidRedirectsFromRegex),
errorDetails: {
redirect,
unsafe: true,
reason: analysis.error
},
help: tpl(messages.redirectsHelp)
});
}
Expand Down
1 change: 1 addition & 0 deletions ghost/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@
"node-jose": "2.2.0",
"path-match": "1.2.4",
"probe-image-size": "7.2.3",
"redos-detector": "5.1.0",
"rss": "1.2.2",
"sanitize-html": "2.13.0",
"semver": "7.6.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ describe('UNIT: custom redirects validation', function () {
should.fail('should have thrown');
} catch (err) {
err.message.should.equal('Incorrect RegEx in redirects file.');
err.errorDetails.redirect.should.equal(config[0]);
err.errorDetails.invalid.should.be.true();
}
});

it('throws for an invalid redirects config having unsafe RegExp in from field', function () {
const config = [{
permanent: true,
from: '^\/episodes\/([a-z0-9-]+)+\/$', // Unsafe due to the surplus + at the end causing infinite backtracking
to: '/'
}];

try {
validate(config);
should.fail('should have thrown');
} catch (err) {
err.message.should.equal('Incorrect RegEx in redirects file.');
err.errorDetails.redirect.should.equal(config[0]);
err.errorDetails.unsafe.should.be.true();
err.errorDetails.reason.should.equal('hitMaxBacktracks');
}
});

Expand Down
14 changes: 14 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -27331,6 +27331,13 @@ redis-parser@^3.0.0:
dependencies:
redis-errors "^1.0.0"

[email protected]:
version "5.1.0"
resolved "https://registry.yarnpkg.com/redos-detector/-/redos-detector-5.1.0.tgz#67660c896a48490e80b35557f876a529680f0f8d"
integrity sha512-08en/ij0//HwKZdKlelRZGQKmQhmKMQPVJPD+1THfYm64mZhLPOG0NBa47+DrOF53DyaRsCFyD7JHJaYITnt9g==
dependencies:
regjsparser "0.10.0"

[email protected]:
version "0.1.14"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.14.tgz#24cf721fe60677146bb77eeb0e1f9dece3d65859"
Expand Down Expand Up @@ -27452,6 +27459,13 @@ regjsgen@^0.2.0:
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
integrity sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==

[email protected]:
version "0.10.0"
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.10.0.tgz#b1ed26051736b436f22fdec1c8f72635f9f44892"
integrity sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==
dependencies:
jsesc "~0.5.0"

regjsparser@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
Expand Down

0 comments on commit b36c235

Please sign in to comment.