0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

🐛 Fixed triggering wrong error for invalid client versions

fix https://linear.app/ghost/issue/ENG-1502/handle-malformed-client-versions-to-avoid-versionmismatcherror

- in the event we pass a client version that is invalid (ie, anything
  that doesn't resemble semver version), we currently throw a
  VersionMismatchError
- this is slightly misleading because although it is a version mismatch,
  it moreso that it's an invalid string to pass in this header
- to fix that, we can just use `validRange` from `semver` to detect
  this, and throw a BadRequestError with a meaningful error message
- this helps us detect whether the header is actually a version
  mismatch, or an invalid input
This commit is contained in:
Daniel Lockyer 2024-11-27 11:36:26 +01:00 committed by Daniel Lockyer
parent 52d15cf955
commit 4b51942403
2 changed files with 35 additions and 11 deletions

View file

@ -3,6 +3,7 @@ const errors = require('@tryghost/errors');
const tpl = require('@tryghost/tpl');
const messages = {
invalidClientVersionRange: 'Client request for {clientVersion} is invalid.',
versionMismatch: 'Client request for {clientVersion} does not match server version {serverVersion}.'
};
@ -10,19 +11,30 @@ function checkVersionMatch(req, res, next) {
const clientVersion = req.get('X-Ghost-Version');
// can contain pre-release suffix, you should be able to use e.g. 1.19.0-pre [server] with 1.18.0 [client]
const serverVersion = res.locals.version.match(/^(\d+\.)?(\d+\.)?(\d+)/)[0];
const constraint = '^' + clientVersion + '.0';
// no error when client is on an earlier minor version than server
// error when client is on a later minor version than server
// always error when the major version is different
if (clientVersion) {
const constraint = '^' + clientVersion + '.0';
if (clientVersion && !semver.satisfies(serverVersion, constraint)) {
return next(new errors.VersionMismatchError({
message: tpl(messages.versionMismatch, {
clientVersion: clientVersion,
serverVersion: serverVersion
})
}));
// Protect against invalid client ranges
if (!semver.validRange(constraint)) {
return next(new errors.BadRequestError({
message: tpl(messages.invalidClientVersionRange, {
clientVersion
})
}));
}
// no error when client is on an earlier minor version than server
// error when client is on a later minor version than server
// always error when the major version is different
if (!semver.satisfies(serverVersion, constraint)) {
return next(new errors.VersionMismatchError({
message: tpl(messages.versionMismatch, {
clientVersion,
serverVersion
})
}));
}
}
next();

View file

@ -66,6 +66,18 @@ describe('Version Mismatch', function () {
nextStub.firstCall.args.should.be.empty();
});
it('should throw BadRequestError if client version is invalid', function () {
const server = '1.5.0';
const client = 'bananarama';
testVersionMatch(server, client);
nextStub.calledOnce.should.be.true();
nextStub.firstCall.args.should.have.lengthOf(1);
nextStub.firstCall.args[0].should.have.property('errorType', 'BadRequestError');
nextStub.firstCall.args[0].should.have.property('statusCode', 400);
});
it('should throw VersionMismatchError if client version is earlier by a major version', function () {
const server = '2.5.0';
const client = '1.3';