Skip to content

Commit

Permalink
Limit extendDatemath to valid ranges
Browse files Browse the repository at this point in the history
There are cases where either Elasticsearch or the JS runtime will fail
if the extended datemath expression goes beyond a certain limit.

In the case of ES, if the new date results in a UNIX epoch smaller than
0, it will crash. In the case of the JS runtime, it will crash if the
UNIX epoch is higher than [8,640,000,000,000,000ms][1].

[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
  • Loading branch information
Alejandro Fernández Gómez committed Jul 8, 2020
1 parent 7f39cb5 commit 69e8da8
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 11 deletions.
16 changes: 16 additions & 0 deletions x-pack/plugins/infra/public/utils/datemath.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@ describe('extendDatemath()', () => {
diffUnit: 'y',
});
});

it('Returns no difference if the next value would result in an epoch smaller than 0', () => {
// FIXME: Test will fail in ~551 years
expect(extendDatemath('now-500y', 'before')).toBeUndefined();

expect(
extendDatemath('1970-01-01T00:00:00.000Z', 'before', '1970-01-01T00:00:00.001Z')
).toBeUndefined();
});
});

describe('with a positive operator', () => {
Expand Down Expand Up @@ -573,6 +582,13 @@ describe('extendDatemath()', () => {
diffUnit: 'y',
});
});

it('Returns no difference if the next value would result in an epoch bigger than the max JS date', () => {
expect(extendDatemath('now+275760y', 'after')).toBeUndefined();
expect(
extendDatemath('+275760-09-13T00:00:00.000Z', 'after', '+275760-09-12T23:59:59.999Z')
).toBeUndefined();
});
});
});
});
Expand Down
43 changes: 32 additions & 11 deletions x-pack/plugins/infra/public/utils/datemath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import dateMath, { Unit } from '@elastic/datemath';

const JS_MAX_DATE = 8640000000000000;

export function isValidDatemath(value: string): boolean {
const parsedValue = dateMath.parse(value);
return !!(parsedValue && parsedValue.isValid());
Expand Down Expand Up @@ -136,18 +138,24 @@ function extendRelativeDatemath(
// if `diffAmount` is not an integer after normalization, express the difference in the original unit
const shouldKeepDiffUnit = diffAmount % 1 !== 0;

return {
value: `now${operator}${normalizedAmount}${normalizedUnit}`,
diffUnit: shouldKeepDiffUnit ? unit : newUnit,
diffAmount: shouldKeepDiffUnit ? Math.abs(newAmount - parsedAmount) : diffAmount,
};
const nextValue = `now${operator}${normalizedAmount}${normalizedUnit}`;

if (isDateInRange(nextValue)) {
return {
value: nextValue,
diffUnit: shouldKeepDiffUnit ? unit : newUnit,
diffAmount: shouldKeepDiffUnit ? Math.abs(newAmount - parsedAmount) : diffAmount,
};
} else {
return undefined;
}
}

function extendAbsoluteDatemath(
value: string,
direction: 'before' | 'after',
oppositeEdge: string
): DatemathExtension {
): DatemathExtension | undefined {
const valueTimestamp = datemathToEpochMillis(value)!;
const oppositeEdgeTimestamp = datemathToEpochMillis(oppositeEdge)!;
const actualTimestampDiff = Math.abs(valueTimestamp - oppositeEdgeTimestamp);
Expand All @@ -159,11 +167,15 @@ function extendAbsoluteDatemath(
? valueTimestamp - normalizedTimestampDiff
: valueTimestamp + normalizedTimestampDiff;

return {
value: new Date(newValue).toISOString(),
diffUnit: normalizedDiff.unit,
diffAmount: normalizedDiff.amount,
};
if (isDateInRange(newValue)) {
return {
value: new Date(newValue).toISOString(),
diffUnit: normalizedDiff.unit,
diffAmount: normalizedDiff.amount,
};
} else {
return undefined;
}
}

const CONVERSION_RATIOS: Record<string, Array<[Unit, number]>> = {
Expand Down Expand Up @@ -265,3 +277,12 @@ export function normalizeDate(amount: number, unit: Unit): { amount: number; uni
// Cannot go one one unit above. Return as it is
return { amount, unit };
}

function isDateInRange(date: string | number): boolean {
try {
const epoch = typeof date === 'string' ? datemathToEpochMillis(date) ?? -1 : date;
return epoch >= 0 && epoch <= JS_MAX_DATE;
} catch {
return false;
}
}

0 comments on commit 69e8da8

Please sign in to comment.