Skip to content

Commit

Permalink
Added multiple date format support for field
Browse files Browse the repository at this point in the history
  • Loading branch information
jeemok committed Jul 11, 2021
1 parent a5bba42 commit b1b05ff
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 20 deletions.
10 changes: 10 additions & 0 deletions .nsprc.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
"notes": "Ignored since we don't use xxx method",
"expiry": 1615462134681
},
"1338": {
"active": true,
"expiry": "01/31/2021, 11:03:58"
},
"1339": {
"expiry": "Sun, 11 Jul 2021 03:03:13 GMT"
},
"1340": {
"expiry": "Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time)"
},
"4501": {
"active": false,
"notes": "This will be fixed by the library maintainers by June 14"
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 3.1.0 (July 11, 2021)

- Updated declaration file extension so it will not be included in final build
- Added multiple date format support for `expiry` field

## 3.0.1 (July 11, 2021)

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@ You may add a file `.nsprc` to your project root directory to manage the excepti

### Fields

| Attribute | Description | Default |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `active` | Boolean type to determine if we should use it for exception; `true` or `false` | `true` |
| `expiry` | Date time in milliseconds, the number of milliseconds since midnight 01 January, 1970 UTC.<br />You can use `new Date(2021, 1, 1).valueOf()` to get the milliseconds value. | |
| `notes` | Notes related to the vulnerability; will be displayed in the table summary. |
| Attribute | Type | Description | Default | Examples |
| --------- | ---------------- | --------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `active` | Boolean | If the tool should use it for exception | `true` | `true` |
| `expiry` | String \| Number | UNIX timestamp | | - `'2020-01-31'` <br> - `'2020/01/31'` <br> - `'01/31/2021, 11:03:58'` <br> - `'1 March 2016 15:00'` <br> - `'1 March 2016 3:00 pm'` <br> - `'2012-01-26T13:51:50.417-07:00'` <br> - `'Sun, 11 Jul 2021 03:03:13 GMT'` <br> - `'Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time)'` <br> - `327611110417` |
| `notes` | String | Notes related to the vulnerability. | |

<br />

Expand Down
4 changes: 4 additions & 0 deletions src/types/date.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type DateAnalysis = {
valid: boolean;
expired?: boolean;
};
1 change: 1 addition & 0 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './color';
export * from './date';
export * from './general';
export * from './level';
export * from './nsprc';
Expand Down
2 changes: 1 addition & 1 deletion src/types/nsprc.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export interface NsprcConfigs {
readonly active?: boolean;
readonly expiry?: number;
readonly expiry?: string | number;
readonly notes?: string;
}

Expand Down
29 changes: 29 additions & 0 deletions src/utils/date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { DateAnalysis } from 'src/types';

/**
* Validate if the given timestamp is a valid UNIX timestamp
* @param {Any} timestamp The given timestamp
* @return {Boolean} Returns true if it is a valid UNIX timestamp
*/
export function isValidDate(timestamp: string | number): boolean {
return new Date(timestamp).getTime() > 0;
}

/**
* Analyze the given date time if it has expired (in the past)
* @param {String | Number} expiry Expiry timestamp
* @param {String | Number} now The date to compare with
* @return {Object} Return the analysis
*/
export function analyzeExpiry(expiry?: string | number, now: string | number = new Date().valueOf()): DateAnalysis {
if (!expiry) {
return { valid: true };
}
if (!isValidDate(expiry) || !isValidDate(now)) {
return { valid: false };
}
return {
valid: true,
expired: new Date(now).getTime() > new Date(expiry).getTime(),
};
}
9 changes: 5 additions & 4 deletions src/utils/vulnerability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import get from 'lodash.get';
import { isJsonString } from './common';
import { color, getSeverityBgColor } from './color';
import { printExceptionReport } from './print';
import { analyzeExpiry } from './date';

import {
NpmAuditJson,
Expand Down Expand Up @@ -187,21 +188,21 @@ export function processExceptions(nsprc: NsprcFile, cmdExceptions: number[] = []
const isValidId = !isNaN(numberId);
const isActive = Boolean(get(details, 'active', true)); // default to true
const expiryDate = get(details, 'expiry') ? new Date(get(details, 'expiry')).toUTCString() : '';
const hasExpired = get(details, 'expiry') ? get(details, 'expiry') < new Date(Date.now()).getTime() : false;
const notes = typeof details === 'string' ? details : get(details, 'notes', '');
const { valid, expired } = analyzeExpiry(get(details, 'expiry'));

let status = color('active', 'green');
if (hasExpired) {
if (expired) {
status = color('expired', 'red');
} else if (!isValidId) {
} else if (!isValidId || !valid) {
status = color('invalid', 'red');
} else if (!isActive) {
status = color('inactive', 'yellow');
}

acc.report.push([id, status, expiryDate, notes]);

if (isValidId && isActive && !hasExpired) {
if (isValidId && isActive && !expired) {
acc.exceptionIds.push(numberId);
}

Expand Down
4 changes: 2 additions & 2 deletions test/__mocks__/nsprc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
"active": true
},
"1556": {
"expiry": 1615462134681,
"expiry": "2021-03-11T11:28:54.681Z",
"active": true,
"notes": "Issue: https://github.com/jeemok/better-npm-audit/issues/28"
},
"975": {
"expiry": 1615462134681
"expiry": "Thu Mar 11 2021 19:28:54 GMT+0800 (Malaysia Time)"
},
"976": {
"active": false
Expand Down
70 changes: 70 additions & 0 deletions test/utils/date.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { expect } from 'chai';
import { isValidDate, analyzeExpiry } from '../../src/utils/date';

describe('Date utils', () => {
describe('#isValidDate', () => {
it('should be able to determine a valid UNIX timestamp correctly', () => {
// Valid cases
expect(isValidDate('2020-01-31')).to.equal(true);
expect(isValidDate('2020/01/31')).to.equal(true);
expect(isValidDate('01/31/2021')).to.equal(true);
expect(isValidDate('01/31/2021, 11:03:58')).to.equal(true);
expect(isValidDate('1 March 2016 15:00')).to.equal(true);
expect(isValidDate('1 March 2016 3:00 pm')).to.equal(true);
expect(isValidDate('1 Mar 2016')).to.equal(true);
expect(isValidDate('1 March 2016')).to.equal(true);
expect(isValidDate('March 1 2016')).to.equal(true);
expect(isValidDate('Julai 1 2016')).to.equal(true);
expect(isValidDate('Julei 1 2016')).to.equal(true);
expect(isValidDate('Jul 11 2021')).to.equal(true);
expect(isValidDate('Sun Jul 11 2021')).to.equal(true);
expect(isValidDate('2020-01-01T00:00:00')).to.equal(true);
expect(isValidDate('2020-01-01T00:00:00Z')).to.equal(true);
expect(isValidDate('2020-01-01T00:00:00.000Z')).to.equal(true);
expect(isValidDate('2012-01-26T13:51:50.417-07:00')).to.equal(true);
expect(isValidDate('Sun, 11 Jul 2021 03:03:13 GMT')).to.equal(true);
expect(isValidDate('Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time)')).to.equal(true);
expect(isValidDate('Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time)')).to.equal(true);
expect(isValidDate(1327611110417)).to.equal(true);

// Invalid cases
expect(isValidDate('1327611110417')).to.equal(false);
expect(isValidDate('2020-31-01')).to.equal(false);
expect(isValidDate('2020/31/01')).to.equal(false);
expect(isValidDate('31/01/2021')).to.equal(false);
expect(isValidDate('31-01-2021')).to.equal(false);
expect(isValidDate('Unknown 1 2016')).to.equal(false);
expect(isValidDate('2020-01-01T00:000:00')).to.equal(false);
expect(isValidDate('11:03:58')).to.equal(false);
});
});

describe('#analyzeExpiry', () => {
it('should return valid and not expired if not given any date', () => {
expect(analyzeExpiry()).to.deep.equal({ valid: true });
});

it('should be able to detect invalid dates', () => {
expect(analyzeExpiry('2020-01-32', '2020-02-02')).to.deep.equal({ valid: false });
expect(analyzeExpiry('2020-01-30', '2020-13-02')).to.deep.equal({ valid: false });
expect(analyzeExpiry('2020-01-50')).to.deep.equal({ valid: false });
});

it('should be able to analyze the given timestamp correctly', () => {
// Only dates
expect(analyzeExpiry('2020-01-31', '2020-01-01')).to.deep.equal({ valid: true, expired: false });
expect(analyzeExpiry('2020-01-31', '2020-01-31')).to.deep.equal({ valid: true, expired: false });
expect(analyzeExpiry('2020-01-31', '2020-02-02')).to.deep.equal({ valid: true, expired: true });

// Dates & time
expect(analyzeExpiry('1 March 2020 3:00 pm', '1 March 2020 2:59:00 pm')).to.deep.equal({ valid: true, expired: false });
expect(analyzeExpiry('1 March 2020 3:00 pm', '1 March 2020 3:00:00 pm')).to.deep.equal({ valid: true, expired: false });
expect(analyzeExpiry('1 March 2020 3:00 pm', '1 March 2020 3:00:01 pm')).to.deep.equal({ valid: true, expired: true });

// Milliseconds
expect(analyzeExpiry(1327611110410, 1327611110409)).to.deep.equal({ valid: true, expired: false });
expect(analyzeExpiry(1327611110410, 1327611110410)).to.deep.equal({ valid: true, expired: false });
expect(analyzeExpiry(1327611110410, 1327611110411)).to.deep.equal({ valid: true, expired: true });
});
});
});
16 changes: 8 additions & 8 deletions test/utils/vulnerability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ describe('Vulnerability utils', () => {
expect(result).to.have.property('report');

const activeExceptionIds = result.report
.filter((exception: Array<string>) => exception[1] === '\u001b[32mactive\u001b[0m')
.map((each: Array<string | number>) => Number(each[0]));
.filter((exception: string[]) => exception[1] === '\u001b[32mactive\u001b[0m')
.map((each: (string | number)[]) => Number(each[0]));
expect(activeExceptionIds).to.have.length(4).to.deep.equal([985, 1213, 1654, 2100]);
});

Expand All @@ -73,8 +73,8 @@ describe('Vulnerability utils', () => {
expect(result).to.have.property('report');

const activeExceptionIds = result.report
.filter((exception: Array<string>) => exception[1] === '\u001b[33minactive\u001b[0m')
.map((each: Array<string | number>) => Number(each[0]));
.filter((exception: string[]) => exception[1] === '\u001b[33minactive\u001b[0m')
.map((each: (string | number)[]) => Number(each[0]));
expect(activeExceptionIds).to.have.length(1).to.deep.equal([976]);
});

Expand All @@ -85,8 +85,8 @@ describe('Vulnerability utils', () => {
expect(result).to.have.property('report');

const activeExceptionIds = result.report
.filter((exception: Array<string>) => exception[1] === '\u001b[31mexpired\u001b[0m')
.map((each: Array<string | number>) => Number(each[0]));
.filter((exception: string[]) => exception[1] === '\u001b[31mexpired\u001b[0m')
.map((each: (string | number)[]) => Number(each[0]));
expect(activeExceptionIds).to.have.length(5).to.deep.equal([975, 1084, 1179, 1556, 1651]);

// Clean up
Expand All @@ -99,8 +99,8 @@ describe('Vulnerability utils', () => {
expect(result).to.have.property('report');

const activeExceptionIds = result.report
.filter((exception: Array<string>) => exception[1] === '\u001b[31minvalid\u001b[0m')
.map((each: Array<string | number>) => each[0]);
.filter((exception: string[]) => exception[1] === '\u001b[31minvalid\u001b[0m')
.map((each: (string | number)[]) => each[0]);
expect(activeExceptionIds).to.have.length(1).to.deep.equal(['Note']);
});
});
Expand Down

0 comments on commit b1b05ff

Please sign in to comment.