Profiles·Public

moment

semver>=2.29.0 <3.0.0postconditions16functions15last verified2026-04-16coverage score83%

Postconditions — what we check

  • moment · moment-invalid-date
    error
    Wheninput string is not a valid date format
    ReturnsInvalid moment object (moment.isValid() returns false)
    Required handlingCaller MUST check isValid() before using the moment object. Invalid moment objects can cause incorrect date calculations, display issues, or NaN values propagating through the application. Use pattern: const m = moment(input); if (!m.isValid()) { /* handle error */ }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • utc · utc-invalid-date
    error
    Wheninput string is not a valid date format
    ReturnsInvalid moment object (moment.isValid() returns false)
    Required handlingCaller MUST check isValid() after parsing. Invalid UTC moments can cause timezone calculation errors and data corruption. Use pattern: const m = moment.utc(input); if (!m.isValid()) { /* handle error */ }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • locale · locale-path-traversal
    error
    Whenuser-provided locale string passed without validation
    ThrowsPath traversal vulnerability — unvalidated locale string may traverse to arbitrary files
    Required handlingWhen using user input to set locale, MUST validate against an allowlist. Vulnerable versions (1.0.1-2.29.1) allow path traversal via locale strings containing dot-dot sequences (/../). Patched in version 2.29.2. Use pattern: const allowed = ['en','fr','de']; if (allowed.includes(userLocale)) moment.locale(userLocale);
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • parseZone · parsezone-invalid-date
    error
    Wheninput string is not a valid date format
    ReturnsInvalid moment object (moment.isValid() returns false, utcOffset() returns NaN)
    Required handlingCaller MUST check isValid() before using the moment object. moment.parseZone() is often used to preserve timezone offset from API responses (e.g., "2013-01-01T00:00:00-13:00"). If the input string is malformed, the returned moment is invalid AND utcOffset() returns NaN — both the date and the offset are silently broken. Use pattern: const m = moment.parseZone(input); if (!m.isValid()) { /* handle */ }
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[4]
  • unix · unix-nan-timestamp
    warning
    Wheninput is NaN, null, undefined, or a non-numeric string
    ReturnsInvalid moment object (moment.isValid() returns false)
    Required handlingCaller MUST validate that the Unix timestamp is a finite number before calling moment.unix(). Common sources of invalid timestamps: API responses where a field is null/undefined (e.g., user.lastLoginAt), database values that are 0 or negative (treated as valid by moment — epoch-relative), or string values not cast to number. moment.unix(NaN) silently returns an invalid moment object. Use pattern: const ts = getTimestamp(); if (typeof ts === 'number' && isFinite(ts)) { moment.unix(ts) }
    costlowin prodsilent failureusers seelost datavisibilitysilent
    Sources[5]
  • duration · duration-nan-propagation
    warning
    Wheninput to duration-based arithmetic is NaN or invalid
    ReturnsNaN values in all numeric outputs (asMilliseconds(), asSeconds(), etc.)
    Required handlingmoment.duration() does not throw when given NaN inputs. Instead all numeric extraction methods (asMilliseconds(), asSeconds(), asMinutes(), etc.) return NaN, which propagates silently through calculations. Common pattern causing issues: computing duration from two moment objects where one is invalid — moment.duration(invalid.diff(valid)) produces NaN duration. Check isValid() on both moments before computing diffs: if (start.isValid() && end.isValid()) { const d = moment.duration(end.diff(start)); }
    costlowin prodsilent failureusers seelost datavisibilitysilent
    Sources[6]
  • defineLocale · definelocale-path-traversal
    error
    Whenlocale name is user-provided and contains path traversal sequences
    ThrowsPath traversal vulnerability — the locale name is later used as a file path in require('./locale/' + name)
    Required handlingWhen the locale name comes from user input, MUST validate against an allowlist of known safe locale codes (e.g., ['en', 'fr', 'de', 'es', 'zh']). The name is passed through isLocaleNameSane() in loadLocale() (checks for '/' and '\'), but this check is insufficient against other path traversal vectors or locale names used in string interpolation elsewhere in the application. Patched in moment 2.29.2. If on older version, validate before calling.
    costhighin prodsilent failureusers seesecurity breachvisibilitysilent
    Sources[3][7]
  • updateLocale · updatelocale-path-traversal
    error
    Whenlocale name is user-provided and contains path traversal sequences
    ThrowsPath traversal vulnerability — the locale name is later used as a file path
    Required handlingSame risk as moment.locale() and moment.defineLocale(). When updating locales dynamically (e.g., in multi-tenant SaaS where tenant sets their locale preference), MUST validate against an allowlist of known locale codes. Use pattern: const ALLOWED_LOCALES = ['en', 'fr', 'de']; if (ALLOWED_LOCALES.includes(tenantLocale)) { moment.updateLocale(tenantLocale, config); }
    costhighin prodsilent failureusers seesecurity breachvisibilitysilent
    Sources[3][8]
  • format · format-invalid-date-string
    error
    Whenmoment object is invalid (parsed from malformed input)
    ReturnsThe string 'Invalid date' (or locale-specific equivalent) instead of a formatted date
    Required handlingCaller MUST check isValid() before calling format(). The string 'Invalid date' is silently returned and easily gets stored in databases, API responses, or displayed to users without error. This is the #1 moment.js footgun in production: moment(userInput).format('YYYY-MM-DD') returns "Invalid date" instead of throwing. Use pattern: const m = moment(input); if (!m.isValid()) { throw new Error('invalid date'); } return m.format('YYYY-MM-DD');
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[9][10]
  • format · format-redos-unvalidated-input
    error
    Whenformat string or input string from user-provided data is excessively long (>10,000 chars)
    ThrowsReDoS (Regular Expression Denial of Service) — quadratic time complexity in RFC2822 date parsing
    Required handlingMUST validate input string length before parsing untrusted strings with moment(). The RFC2822 date format regex has quadratic (N²) complexity for strings exceeding 10,000 characters. A 50,000-char input can cause 25+ second blocking on the event loop. Use pattern: if (typeof input === 'string' && input.length > 1000) { throw new Error('date input too long'); } Patched in moment 2.29.4. If on vulnerable version, add length validation.
    costhighin proddegraded serviceusers seeservice unavailablevisibilityvisible
    Sources[11]
  • toISOString · toisostring-null-for-invalid
    error
    Whenmoment object is invalid (parsed from malformed input)
    Returnsnull instead of an ISO string
    Required handlingCaller MUST check isValid() before calling toISOString(). The null return value is a common source of TypeErrors: code that chains .split('T') or .substring(0, 10) on the toISOString() result crashes at runtime. Also affects JSON.stringify(obj) when obj contains a moment in toJSON() call — toJSON() calls toISOString() and returns null for invalid moments. Use pattern: const m = moment(input); if (!m.isValid()) { return null; } return m.toISOString();
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10][12]
  • add · add-invalid-moment-propagation
    warning
    Whenadd() is called on a moment object that is already invalid (isValid() === false)
    ReturnsInvalid moment object — NaN propagates through all subsequent operations
    Required handlingCaller MUST check isValid() on the source moment BEFORE calling add(). When add() is called on an invalid moment, it silently returns another invalid moment. This is particularly dangerous when the moment comes from a parsed user input or API response: the subsequent add() call succeeds without error, but every downstream operation (format(), diff(), toISOString()) continues silently returning "Invalid date" / null / NaN. Pattern: const m = moment(input); if (!m.isValid()) { throw new Error('invalid date'); } m.add(1, 'day');
    costlowin prodsilent failureusers seelost datavisibilitysilent
    Sources[13][10]
  • subtract · subtract-invalid-moment-propagation
    warning
    Whensubtract() is called on a moment object that is already invalid (isValid() === false)
    ReturnsInvalid moment object — NaN propagates through all subsequent operations
    Required handlingCaller MUST check isValid() on the source moment BEFORE calling subtract(). When subtract() is called on an invalid moment, it silently returns another invalid moment. Common dangerous pattern: computing expiry dates (moment(createdAt).subtract(30, 'days')) where createdAt may be null/undefined from a DB column — the invalid moment silently propagates, producing incorrect expiry timestamps that get stored in the database. Pattern: const m = moment(input); if (!m.isValid()) { throw new Error('invalid date'); } m.subtract(30, 'days');
    costlowin prodsilent failureusers seelost datavisibilitysilent
    Sources[14][10]
  • min · min-invalid-moment-propagation
    error
    Whenany argument to moment.min() is an invalid moment (isValid() === false)
    ReturnsInvalid moment object, regardless of the validity of other arguments
    Required handlingCaller MUST ensure all moments passed to moment.min() are valid before calling. Even if only one of many moments is invalid, the entire min() result is invalid. This is counterintuitive — developers often expect min() to return the best valid value, but instead it propagates invalidity from any single bad argument. Common dangerous pattern: moment.min(userInputDates.map(d => moment(d))) where any one unparseable date in the array causes the entire result to be invalid. Pattern: const validDates = dates.map(d => moment(d)).filter(m => m.isValid()); moment.min(validDates);
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[15]
  • max · max-invalid-moment-propagation
    error
    Whenany argument to moment.max() is an invalid moment (isValid() === false)
    ReturnsInvalid moment object, regardless of the validity of other arguments
    Required handlingCaller MUST ensure all moments passed to moment.max() are valid before calling. Even if only one of many moments is invalid, the entire max() result is invalid. This is the same counterintuitive propagation as moment.min(). Common dangerous pattern: moment.max(events.map(e => moment(e.endAt))) where any one null/missing endAt in the dataset causes the maximum to be invalid — leading to "Invalid date" being stored as the computed deadline or expiry date. Pattern: const validDates = dates.map(d => moment(d)).filter(m => m.isValid()); moment.max(validDates);
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[16]
  • fromNow · fromnow-invalid-date-string
    warning
    Whenmoment object is invalid (isValid() === false)
    ReturnsThe localized string 'Invalid date' instead of a relative time string
    Required handlingCaller MUST check isValid() before calling fromNow(). The string "Invalid date" is silently returned and easily gets displayed to users in UI components like "Posted Invalid date ago" or "Expires Invalid date". This is a common UX bug in production React/Next.js apps that use moment for relative timestamps. Use pattern: const m = moment(input); return m.isValid() ? m.fromNow() : 'Unknown date';
    costlowin prodsilent failureusers seedegraded performancevisibilityvisible
    Sources[17][10]

Sources

Every postcondition cites at least one of these. Numbered to match the footnotes above.

  1. [1]momentjs.com/docshttps://momentjs.com/docs/#/parsing/string/
  2. [2]momentjs.com/docshttps://momentjs.com/docs/#/parsing/utc/
  3. [3]nvd.nist.gov/vuln/detailhttps://nvd.nist.gov/vuln/detail/CVE-2022-24785
  4. [4]momentjs.com/docshttps://momentjs.com/docs/#/parsing/parse-zone/
  5. [5]momentjs.com/docshttps://momentjs.com/docs/#/parsing/unix-timestamp/
  6. [6]momentjs.com/docshttps://momentjs.com/docs/#/durations/
  7. [7]momentjs.com/docshttps://momentjs.com/docs/#/i18n/defining-locale/
  8. [8]momentjs.com/docshttps://momentjs.com/docs/#/i18n/changing-locale/
  9. [9]momentjs.com/docshttps://momentjs.com/docs/#/displaying/format/
  10. [10]momentjs.com/docshttps://momentjs.com/docs/#/parsing/is-valid/
  11. [11]nvd.nist.gov/vuln/detailhttps://nvd.nist.gov/vuln/detail/CVE-2022-31129
  12. [12]momentjs.com/docshttps://momentjs.com/docs/#/displaying/as-iso-string/
  13. [13]momentjs.com/docshttps://momentjs.com/docs/#/manipulating/add/
  14. [14]momentjs.com/docshttps://momentjs.com/docs/#/manipulating/subtract/
  15. [15]momentjs.com/docshttps://momentjs.com/docs/#/get-set/min/
  16. [16]momentjs.com/docshttps://momentjs.com/docs/#/get-set/max/
  17. [17]momentjs.com/docshttps://momentjs.com/docs/#/displaying/fromnow/
Need a different package?
Request a profile