From: Iskren Chernev Date: Tue, 29 Sep 2015 07:47:38 +0000 (-0700) Subject: Fix a few crashes with invalid iso strings X-Git-Tag: 2.11.0~69^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=59ba020cf6733f03a9202767517f6edc7af7d706;p=thirdparty%2Fmoment.git Fix a few crashes with invalid iso strings --- diff --git a/src/lib/create/from-string.js b/src/lib/create/from-string.js index be30c8a11..38bdca17e 100644 --- a/src/lib/create/from-string.js +++ b/src/lib/create/from-string.js @@ -1,4 +1,3 @@ -import { matchOffset } from '../parse/regex'; import { configFromStringAndFormat } from './from-string-and-format'; import { hooks } from '../utils/hooks'; import { deprecate } from '../utils/deprecate'; @@ -6,25 +5,29 @@ import getParsingFlags from './parsing-flags'; // iso 8601 regex // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) -var isoRegex = /^\s*((?:[+-]\d{6}|\d{4})-?(?:\d\d$|\d\d-?\d\d|W\d\d$|W\d\d-?\d|\d\d\d))(?:(T| )(\d\d(?::?\d\d(?::?\d\d(?::?[.,]\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; +var isoRegex = /^\s*((?:[+-]\d{6}|\d{4})-?(?:\d\d-?\d\d|W\d\d-?\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::?\d\d(?::?\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/; + +var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; var isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/, true], - ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/, true], - ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/, true], - ['GGGG-[W]WW', /\d{4}-W\d{2}/, true], + ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/, true], + ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/, true], + ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/, true], + ['GGGG-[W]WW', /\d{4}-W\d\d/, true, false], ['YYYY-DDD', /\d{4}-\d{3}/, true], - ['YYYYYYMMDD', /[+-]\d{6}\d{2}\d{2}/, false], - ['YYYYMMDD', /\d{4}\d{2}\d{2}/, false], + ['YYYY-MM', /\d{4}-\d\d/, true, false], + ['YYYYYYMMDD', /[+-]\d{10}/, false], + ['YYYYMMDD', /\d{8}/, false], // YYYYMM is NOT allowed by the standard - ['GGGG[W]WWE', /\d{4}W\d{2}\d/, false], - ['GGGG[W]WW', /\d{4}W\d{2}/, false], - ['YYYYDDD', /\d{4}\d{3}/, false] + ['GGGG[W]WWE', /\d{4}W\d{3}/, false], + ['GGGG[W]WW', /\d{4}W\d{2}/, false, false], + ['YYYYDDD', /\d{7}/, false] ]; // iso time formats and regexes var isoTimes = [ ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/, true], + ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/, true], ['HH:mm:ss', /\d\d:\d\d:\d\d/, true], ['HH:mm', /\d\d:\d\d/, true], ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/, false], @@ -41,26 +44,41 @@ export function configFromISO(config) { var i, l, string = config._i, match = isoRegex.exec(string), - extendedDate, extendedTime; + extendedDate, extendedTime, allowTime, dateFormat, timeFormat, tzFormat; if (match) { getParsingFlags(config).iso = true; for (i = 0, l = isoDates.length; i < l; i++) { if (isoDates[i][1].exec(match[1])) { - config._f = isoDates[i][0]; + dateFormat = isoDates[i][0]; extendedDate = isoDates[i][2]; + allowTime = isoDates[i][3] !== false; break; } } - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(match[3])) { - // match[2] should be 'T' or space - config._f += (match[2] || ' ') + isoTimes[i][0]; - extendedTime = isoTimes[i][2]; - break; + if (dateFormat == null) { + config._isValid = false; + return; + } + if (match[3] != null) { + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(match[3])) { + // match[2] should be 'T' or space + timeFormat = (match[2] || ' ') + isoTimes[i][0]; + extendedTime = isoTimes[i][2]; + break; + } + } + if (timeFormat == null) { + config._isValid = false; + return; } } + if (!allowTime && timeFormat != null) { + config._isValid = false; + return; + } if (extendedDate != null && extendedTime != null && extendedDate !== extendedTime) { @@ -68,10 +86,15 @@ export function configFromISO(config) { config._isValid = false; return; } - matchOffset.lastIndex = 0; - if (matchOffset.exec(match[4])) { - config._f += 'Z'; + if (match[4] != null) { + if (tzRegex.exec(match[4])) { + tzFormat = 'Z'; + } else { + config._isValid = false; + return; + } } + config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); configFromStringAndFormat(config); } else { config._isValid = false; diff --git a/src/lib/parse/regex.js b/src/lib/parse/regex.js index 9e7d6788c..b1ace2763 100644 --- a/src/lib/parse/regex.js +++ b/src/lib/parse/regex.js @@ -12,6 +12,7 @@ export var matchUnsigned = /\d+/; // 0 - inf export var matchSigned = /[+-]?\d+/; // -inf - inf export var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z +export var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z export var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 diff --git a/src/lib/units/offset.js b/src/lib/units/offset.js index 5d0bf5206..3d21f6aa4 100644 --- a/src/lib/units/offset.js +++ b/src/lib/units/offset.js @@ -3,7 +3,7 @@ import { createDuration } from '../duration/create'; import { addSubtract } from '../moment/add-subtract'; import { isMoment, copyConfig } from '../moment/constructor'; import { addFormatToken } from '../format/format'; -import { addRegexToken, matchOffset } from '../parse/regex'; +import { addRegexToken, matchOffset, matchShortOffset } from '../parse/regex'; import { addParseToken } from '../parse/token'; import { createLocal } from '../create/local'; import { prepareConfig } from '../create/from-anything'; @@ -32,11 +32,11 @@ offset('ZZ', ''); // PARSING -addRegexToken('Z', matchOffset); -addRegexToken('ZZ', matchOffset); +addRegexToken('Z', matchShortOffset); +addRegexToken('ZZ', matchShortOffset); addParseToken(['Z', 'ZZ'], function (input, array, config) { config._useUTC = true; - config._tzm = offsetFromString(input); + config._tzm = offsetFromString(matchShortOffset, input); }); // HELPERS @@ -46,8 +46,8 @@ addParseToken(['Z', 'ZZ'], function (input, array, config) { // '-1530' > ['-15', '30'] var chunkOffset = /([\+\-]|\d\d)/gi; -function offsetFromString(string) { - var matches = ((string || '').match(matchOffset) || []); +function offsetFromString(matcher, string) { + var matches = ((string || '').match(matcher) || []); var chunk = matches[matches.length - 1] || []; var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; var minutes = +(parts[1] * 60) + toInt(parts[2]); @@ -102,7 +102,7 @@ export function getSetOffset (input, keepLocalTime) { } if (input != null) { if (typeof input === 'string') { - input = offsetFromString(input); + input = offsetFromString(matchShortOffset, input); } if (Math.abs(input) < 16) { input = input * 60; @@ -164,7 +164,7 @@ export function setOffsetToParsedOffset () { if (this._tzm) { this.utcOffset(this._tzm); } else if (typeof this._i === 'string') { - this.utcOffset(offsetFromString(this._i)); + this.utcOffset(offsetFromString(matchOffset, this._i)); } return this; } diff --git a/src/test/moment/create.js b/src/test/moment/create.js index a3260c173..ad9757d48 100644 --- a/src/test/moment/create.js +++ b/src/test/moment/create.js @@ -424,6 +424,7 @@ test('parsing iso', function (assert) { tz2 = tz.replace(':', ''), tz3 = tz2.slice(0, 3), formats = [ + ['2011-10', '2011-10-01T00:00:00.000' + tz], ['2011-10-08', '2011-10-08T00:00:00.000' + tz], ['2011-10-08T18', '2011-10-08T18:00:00.000' + tz], ['2011-10-08T18:04', '2011-10-08T18:04:00.000' + tz], @@ -581,6 +582,15 @@ test('parsing iso', function (assert) { } }); +test('non iso 8601 strings', function (assert) { + assert.ok(!moment('2015-10T10:15', moment.ISO_8601).isValid(), 'incomplete date with time'); + assert.ok(!moment('2015-W10T10:15', moment.ISO_8601).isValid(), 'incomplete week date with time'); + assert.ok(!moment('201510', moment.ISO_8601).isValid(), 'basic YYYYMM is not allowed'); + assert.ok(!moment('2015W10T1015', moment.ISO_8601).isValid(), 'incomplete week date with time (basic)'); + assert.ok(!moment('2015-10-08T1015', moment.ISO_8601).isValid(), 'mixing extended and basic format'); + assert.ok(!moment('20151008T10:15', moment.ISO_8601).isValid(), 'mixing basic and extended format'); +}); + test('parsing iso week year/week/weekday', function (assert) { assert.equal(moment.utc('2007-W01').format(), '2007-01-01T00:00:00+00:00', '2008 week 1 (1st Jan Mon)'); assert.equal(moment.utc('2008-W01').format(), '2007-12-31T00:00:00+00:00', '2008 week 1 (1st Jan Tue)'); diff --git a/src/test/moment/is_valid.js b/src/test/moment/is_valid.js index 07999a0d9..02ef46b8c 100644 --- a/src/test/moment/is_valid.js +++ b/src/test/moment/is_valid.js @@ -90,8 +90,8 @@ test('invalid string iso 8601', function (assert) { ], i; for (i = 0; i < tests.length; i++) { - assert.equal(moment(tests[i]).isValid(), false, tests[i] + ' should be invalid'); - assert.equal(moment.utc(tests[i]).isValid(), false, tests[i] + ' should be invalid'); + assert.equal(moment(tests[i], moment.ISO_8601).isValid(), false, tests[i] + ' should be invalid'); + assert.equal(moment.utc(tests[i], moment.ISO_8601).isValid(), false, tests[i] + ' should be invalid'); } }); @@ -108,8 +108,8 @@ test('invalid string iso 8601 + timezone', function (assert) { ], i; for (i = 0; i < tests.length; i++) { - assert.equal(moment(tests[i]).isValid(), false, tests[i] + ' should be invalid'); - assert.equal(moment.utc(tests[i]).isValid(), false, tests[i] + ' should be invalid'); + assert.equal(moment(tests[i], moment.ISO_8601).isValid(), false, tests[i] + ' should be invalid'); + assert.equal(moment.utc(tests[i], moment.ISO_8601).isValid(), false, tests[i] + ' should be invalid'); } });