From 79cb75e3fdd5add83803681009f92050cf65cb2f Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Wed, 13 Jul 2022 23:43:26 +0200 Subject: [PATCH 01/20] feat: introduces security module --- scripts/generateLocales.ts | 1 + src/definitions/definitions.ts | 2 + src/definitions/index.ts | 1 + src/definitions/security.ts | 22 +++++++ src/faker.ts | 2 + src/modules/security/index.ts | 79 ++++++++++++++++++++++++ test/__snapshots__/security.spec.ts.snap | 13 ++++ test/security.spec.ts | 64 +++++++++++++++++++ 8 files changed, 184 insertions(+) create mode 100644 src/definitions/security.ts create mode 100644 src/modules/security/index.ts create mode 100644 test/__snapshots__/security.spec.ts.snap create mode 100644 test/security.spec.ts diff --git a/scripts/generateLocales.ts b/scripts/generateLocales.ts index dea43bcd001..108246b7525 100644 --- a/scripts/generateLocales.ts +++ b/scripts/generateLocales.ts @@ -59,6 +59,7 @@ const definitionsTypes: DefinitionsType = { music: 'MusicDefinitions', name: 'NameDefinitions', phone_number: 'PhoneNumberDefinitions', + security: 'SecurityDefinitions', science: 'ScienceDefinitions', system: 'SystemDefinitions', vehicle: 'VehicleDefinitions', diff --git a/src/definitions/definitions.ts b/src/definitions/definitions.ts index 3ec795f7b7e..91d8fb12680 100644 --- a/src/definitions/definitions.ts +++ b/src/definitions/definitions.ts @@ -13,6 +13,7 @@ import type { MusicDefinitions } from './music'; import type { NameDefinitions } from './name'; import type { PhoneNumberDefinitions } from './phone_number'; import type { ScienceDefinitions } from './science'; +import type { SecurityDefinitions } from './security'; import type { SystemDefinitions } from './system'; import type { VehicleDefinitions } from './vehicle'; import type { WordDefinitions } from './word'; @@ -40,6 +41,7 @@ export interface Definitions { music: MusicDefinitions; name: NameDefinitions; phone_number: PhoneNumberDefinitions; + security: SecurityDefinitions; science: ScienceDefinitions; system: SystemDefinitions; vehicle: VehicleDefinitions; diff --git a/src/definitions/index.ts b/src/definitions/index.ts index 03a688528bd..1d6b32cbcf9 100644 --- a/src/definitions/index.ts +++ b/src/definitions/index.ts @@ -20,6 +20,7 @@ export type { MusicDefinitions } from './music'; export type { NameDefinitions, NameTitleDefinitions } from './name'; export type { PhoneNumberDefinitions } from './phone_number'; export type { ScienceDefinitions } from './science'; +export type { SecurityDefinitions } from './security'; export type { SystemDefinitions, SystemMimeTypeEntryDefinitions, diff --git a/src/definitions/security.ts b/src/definitions/security.ts new file mode 100644 index 00000000000..8ce24dae070 --- /dev/null +++ b/src/definitions/security.ts @@ -0,0 +1,22 @@ +import type { Cvss } from '../modules/security'; +import type { LocaleEntry } from './definitions'; + +/** + * The possible definitions related to security. + */ +export type SecurityDefinitions = LocaleEntry<{ + /** + * CVE definition. + */ + cve: string[]; + + /** + * CWE definition. + */ + cwe: string[]; + + /** + * CVSS object + */ + cvss: Cvss; +}>; diff --git a/src/faker.ts b/src/faker.ts index 0283bd6dd6e..895e1ad386e 100644 --- a/src/faker.ts +++ b/src/faker.ts @@ -23,6 +23,7 @@ import { NameModule } from './modules/name'; import { PhoneModule } from './modules/phone'; import { RandomModule } from './modules/random'; import { ScienceModule } from './modules/science'; +import { SecurityModule } from './modules/Security'; import { SystemModule } from './modules/system'; import { UniqueModule } from './modules/unique'; import { VehicleModule } from './modules/vehicle'; @@ -103,6 +104,7 @@ export class Faker { readonly name: NameModule = new NameModule(this); readonly phone: PhoneModule = new PhoneModule(this); readonly science: ScienceModule = new ScienceModule(this); + readonly security: SecurityModule = new SecurityModule(this); readonly system: SystemModule = new SystemModule(this); readonly vehicle: VehicleModule = new VehicleModule(this); readonly word: WordModule = new WordModule(this); diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts new file mode 100644 index 00000000000..d3a40a8b20b --- /dev/null +++ b/src/modules/security/index.ts @@ -0,0 +1,79 @@ +import type { Faker } from '../..'; + +export interface Cvss { + score: number; + vector: string; + rating: 'none' | 'low' | 'medium' | 'high' | 'critical'; +} + +export class SecurityModule { + constructor(private readonly faker: Faker) { + // Bind `this` so namespaced is working correctly + for (const name of Object.getOwnPropertyNames(SecurityModule.prototype)) { + if (name === 'constructor' || typeof this[name] !== 'function') { + continue; + } + this[name] = this[name].bind(this); + } + } + + /** + * Generates a random CVE + * + * @example + * faker.security.cve() // 'CVE-2011-0762' + */ + cve(): string { + return [ + 'CVE', + // Year + this.faker.date + .between('1999-01-01T00:00:00.000Z', '2022-01-01T00:00:00.000Z') + .getFullYear(), + // Sequence in the year + this.faker.random.numeric(5, { allowLeadingZeros: true }), + ].join('-'); + } + + /** + * Generates a random CWE + * + * @example + * faker.security.cwe() // 'CWE-####' + */ + cwe(): string { + return ['CWE', this.faker.random.numeric(4)].join('-'); + } + + /** + * Generates a random CVSS return + * Based on: + * https://www.first.org/cvss/calculator/3.1 + * + * @example + * faker.security.cvss() + */ + cvss(): Cvss { + return { + score: 0.5, + vector: [ + 'CVSS:3.1', + `AV:${this.faker.helpers.arrayElement('NALP'.split(''))}`, + `AC:${this.faker.helpers.arrayElement('LH'.split(''))}`, + `PR:${this.faker.helpers.arrayElement('NLH'.split(''))}`, + `UI:${this.faker.helpers.arrayElement('NR'.split(''))}`, + `S:${this.faker.helpers.arrayElement('UC'.split(''))}`, + `C:${this.faker.helpers.arrayElement('NLH'.split(''))}`, + `I:${this.faker.helpers.arrayElement('NLH'.split(''))}`, + `A:${this.faker.helpers.arrayElement('NLH'.split(''))}`, + ].join('/'), + rating: this.faker.helpers.arrayElement([ + 'none', + 'low', + 'medium', + 'high', + 'critical', + ]), + }; + } +} diff --git a/test/__snapshots__/security.spec.ts.snap b/test/__snapshots__/security.spec.ts.snap new file mode 100644 index 00000000000..d7b727847c2 --- /dev/null +++ b/test/__snapshots__/security.spec.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1 + +exports[`security > seed: 42 > cve() 1`] = `"CVE-2007-79177"`; + +exports[`security > seed: 42 > cwe() 1`] = `"CWE-4791"`; + +exports[`security > seed: 1211 > cve() 1`] = `"CVE-2020-48721"`; + +exports[`security > seed: 1211 > cwe() 1`] = `"CWE-9487"`; + +exports[`security > seed: 1337 > cve() 1`] = `"CVE-2005-51225"`; + +exports[`security > seed: 1337 > cwe() 1`] = `"CWE-3512"`; diff --git a/test/security.spec.ts b/test/security.spec.ts new file mode 100644 index 00000000000..42547884a89 --- /dev/null +++ b/test/security.spec.ts @@ -0,0 +1,64 @@ +import { afterEach, describe, expect, it } from 'vitest'; +import { faker } from '../src'; +import { seededRuns } from './support/seededRuns'; + +const NON_SEEDED_BASED_RUN = 5; + +const functionNames = ['cve', 'cwe']; + +describe('security', () => { + afterEach(() => { + faker.locale = 'en'; + }); + + for (const seed of seededRuns) { + describe(`seed: ${seed}`, () => { + for (const functionName of functionNames) { + it(`${functionName}()`, () => { + faker.seed(seed); + + const actual = faker.security[functionName](); + expect(actual).toMatchSnapshot(); + }); + } + }); + } + + // Create and log-back the seed for debug purposes + faker.seed(Math.ceil(Math.random() * 1_000_000_000)); + + describe(`random seeded tests for seed ${JSON.stringify( + faker.seed() + )}`, () => { + for (let i = 1; i <= NON_SEEDED_BASED_RUN; i++) { + describe('cve()', () => { + it('should return a well formed string', () => { + expect(faker.security.cve()).toMatch(/^CVE-[0-9]{4}-[0-9]{4}/); + }); + }); + + describe('cwe()', () => { + it('should return a well formed string', () => { + expect(faker.security.cwe()).toMatch(/^CWE-[0-9]{4}/); + }); + }); + + describe('cvss()', () => { + it('should return an object', () => { + const cvss = faker.security.cvss(); + expect(cvss).toBeTypeOf('object'); + }); + + it('should return a numeric value', () => { + expect(faker.security.cvss().score).toEqual(expect.any(Number)); + }); + + it('should return a well formed string', () => { + expect(faker.security.cvss().vector).toMatch( + /^CVSS:3.1\/AV:[NALP]\/AC:[LH]\/PR:[NLH]\/UI:[NR]\/S:[UC]\/C:[NLH]\/I:[NLH]\/A:[NLH]/ + ); + }); + }); + } + }); +}); From 9548f7520be5e493ee14ca37ff37393f015c2514 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Tue, 9 Aug 2022 07:35:31 +0200 Subject: [PATCH 02/20] chore: remove unused definition --- src/definitions/definitions.ts | 2 -- src/definitions/index.ts | 1 - src/definitions/security.ts | 22 ---------------------- 3 files changed, 25 deletions(-) delete mode 100644 src/definitions/security.ts diff --git a/src/definitions/definitions.ts b/src/definitions/definitions.ts index 91d8fb12680..3ec795f7b7e 100644 --- a/src/definitions/definitions.ts +++ b/src/definitions/definitions.ts @@ -13,7 +13,6 @@ import type { MusicDefinitions } from './music'; import type { NameDefinitions } from './name'; import type { PhoneNumberDefinitions } from './phone_number'; import type { ScienceDefinitions } from './science'; -import type { SecurityDefinitions } from './security'; import type { SystemDefinitions } from './system'; import type { VehicleDefinitions } from './vehicle'; import type { WordDefinitions } from './word'; @@ -41,7 +40,6 @@ export interface Definitions { music: MusicDefinitions; name: NameDefinitions; phone_number: PhoneNumberDefinitions; - security: SecurityDefinitions; science: ScienceDefinitions; system: SystemDefinitions; vehicle: VehicleDefinitions; diff --git a/src/definitions/index.ts b/src/definitions/index.ts index 1d6b32cbcf9..03a688528bd 100644 --- a/src/definitions/index.ts +++ b/src/definitions/index.ts @@ -20,7 +20,6 @@ export type { MusicDefinitions } from './music'; export type { NameDefinitions, NameTitleDefinitions } from './name'; export type { PhoneNumberDefinitions } from './phone_number'; export type { ScienceDefinitions } from './science'; -export type { SecurityDefinitions } from './security'; export type { SystemDefinitions, SystemMimeTypeEntryDefinitions, diff --git a/src/definitions/security.ts b/src/definitions/security.ts deleted file mode 100644 index 8ce24dae070..00000000000 --- a/src/definitions/security.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { Cvss } from '../modules/security'; -import type { LocaleEntry } from './definitions'; - -/** - * The possible definitions related to security. - */ -export type SecurityDefinitions = LocaleEntry<{ - /** - * CVE definition. - */ - cve: string[]; - - /** - * CWE definition. - */ - cwe: string[]; - - /** - * CVSS object - */ - cvss: Cvss; -}>; From cbbadd2deb208a1f20921507665696511f19d22b Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Tue, 9 Aug 2022 07:37:24 +0200 Subject: [PATCH 03/20] chore: refactor to make CVE date range user editable --- src/internal/toDate.ts | 14 ++++++++++++++ src/modules/date/index.ts | 16 +--------------- src/modules/security/index.ts | 20 +++++++++++++++----- 3 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 src/internal/toDate.ts diff --git a/src/internal/toDate.ts b/src/internal/toDate.ts new file mode 100644 index 00000000000..ea91cc8fd0d --- /dev/null +++ b/src/internal/toDate.ts @@ -0,0 +1,14 @@ +/** + * Converts date passed as a string, number or Date to a Date object. + * If nothing or a non parseable value is passed, takes current date. + * + * @param date Date + */ +export function toDate(date?: string | Date | number): Date { + date = new Date(date); + if (isNaN(date.valueOf())) { + date = new Date(); + } + + return date; +} diff --git a/src/modules/date/index.ts b/src/modules/date/index.ts index 111fc4bae6f..f2582444f81 100644 --- a/src/modules/date/index.ts +++ b/src/modules/date/index.ts @@ -1,21 +1,7 @@ import type { Faker } from '../..'; import type { DateEntryDefinition } from '../../definitions'; import { FakerError } from '../../errors/faker-error'; - -/** - * Converts date passed as a string, number or Date to a Date object. - * If nothing or a non parseable value is passed, takes current date. - * - * @param date Date - */ -function toDate(date?: string | Date | number): Date { - date = new Date(date); - if (isNaN(date.valueOf())) { - date = new Date(); - } - - return date; -} +import { toDate } from '../../internal/toDate'; /** * Module to generate dates. diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index d3a40a8b20b..a327d418322 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -1,4 +1,5 @@ import type { Faker } from '../..'; +import { toDate } from '../../internal/toDate'; export interface Cvss { score: number; @@ -18,18 +19,27 @@ export class SecurityModule { } /** - * Generates a random CVE + * Generates a random CVE between the given boundaries + * + * @param options + * @param options.from The early date boundary + * @param options.to The late date boundary * * @example * faker.security.cve() // 'CVE-2011-0762' + * faker.security.cve({from:'2020-01-01T00:00:00.000Z', to: '2030-01-01T00:00:00.000Z') // 'CVE-2028-0762' */ - cve(): string { + cve(options?: { + from: string | Date | number; + to: string | Date | number; + }): string { + const fromMs = toDate(options?.from || '1999-01-01T00:00:00.000Z'); + const toMs = toDate(options?.to); + return [ 'CVE', // Year - this.faker.date - .between('1999-01-01T00:00:00.000Z', '2022-01-01T00:00:00.000Z') - .getFullYear(), + this.faker.date.between(fromMs, toMs).getFullYear(), // Sequence in the year this.faker.random.numeric(5, { allowLeadingZeros: true }), ].join('-'); From cd77e0074bde8d89ba2a94b71392b35ee6d37f14 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Tue, 9 Aug 2022 18:26:24 +0200 Subject: [PATCH 04/20] chore: remove unused locales setup --- scripts/generateLocales.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/generateLocales.ts b/scripts/generateLocales.ts index 108246b7525..dea43bcd001 100644 --- a/scripts/generateLocales.ts +++ b/scripts/generateLocales.ts @@ -59,7 +59,6 @@ const definitionsTypes: DefinitionsType = { music: 'MusicDefinitions', name: 'NameDefinitions', phone_number: 'PhoneNumberDefinitions', - security: 'SecurityDefinitions', science: 'ScienceDefinitions', system: 'SystemDefinitions', vehicle: 'VehicleDefinitions', From fd3bade5ac14c1548fbd090e9a4a2ba8b539db6e Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Thu, 11 Aug 2022 20:02:59 +0200 Subject: [PATCH 05/20] chore: address PR comments --- src/modules/security/index.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index a327d418322..c713bd55fe7 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -19,20 +19,22 @@ export class SecurityModule { } /** - * Generates a random CVE between the given boundaries + * Generates a random CVE between the given boundaries. * - * @param options - * @param options.from The early date boundary - * @param options.to The late date boundary + * @param options The options to use. Defaults to `{}`. + * @param options.from The early date boundary. Defaults to `1999-01-01T00:00:00.000Z`. + * @param options.to The late date boundary. Defaults to `now`. * * @example * faker.security.cve() // 'CVE-2011-0762' * faker.security.cve({from:'2020-01-01T00:00:00.000Z', to: '2030-01-01T00:00:00.000Z') // 'CVE-2028-0762' */ - cve(options?: { - from: string | Date | number; - to: string | Date | number; - }): string { + cve( + options: { + from?: string | Date | number; + to?: string | Date | number; + } = {} + ): string { const fromMs = toDate(options?.from || '1999-01-01T00:00:00.000Z'); const toMs = toDate(options?.to); From 190d8dc2c1dbdad2e84a835fc4fb8b04421d366c Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Wed, 17 Aug 2022 07:56:38 +0200 Subject: [PATCH 06/20] fix: better align with CWE id convention --- src/modules/security/index.ts | 10 +++++++--- test/__snapshots__/security.spec.ts.snap | 6 +++--- test/security.spec.ts | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index c713bd55fe7..2f0f04e327a 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -20,6 +20,8 @@ export class SecurityModule { /** * Generates a random CVE between the given boundaries. + * Based on: + * https://www.cve.org/ * * @param options The options to use. Defaults to `{}`. * @param options.from The early date boundary. Defaults to `1999-01-01T00:00:00.000Z`. @@ -48,17 +50,19 @@ export class SecurityModule { } /** - * Generates a random CWE + * Generates a random CWE (Common Weakness Enumeration) identifier. + * Based on: + * https://cwe.mitre.org/data/index.html * * @example * faker.security.cwe() // 'CWE-####' */ cwe(): string { - return ['CWE', this.faker.random.numeric(4)].join('-'); + return ['CWE', this.faker.datatype.number({ min: 0, max: 1388 })].join('-'); } /** - * Generates a random CVSS return + * Generates a random CVSS. * Based on: * https://www.first.org/cvss/calculator/3.1 * diff --git a/test/__snapshots__/security.spec.ts.snap b/test/__snapshots__/security.spec.ts.snap index d7b727847c2..f8c672a7148 100644 --- a/test/__snapshots__/security.spec.ts.snap +++ b/test/__snapshots__/security.spec.ts.snap @@ -2,12 +2,12 @@ exports[`security > seed: 42 > cve() 1`] = `"CVE-2007-79177"`; -exports[`security > seed: 42 > cwe() 1`] = `"CWE-4791"`; +exports[`security > seed: 42 > cwe() 1`] = `"CWE-520"`; exports[`security > seed: 1211 > cve() 1`] = `"CVE-2020-48721"`; -exports[`security > seed: 1211 > cwe() 1`] = `"CWE-9487"`; +exports[`security > seed: 1211 > cwe() 1`] = `"CWE-1289"`; exports[`security > seed: 1337 > cve() 1`] = `"CVE-2005-51225"`; -exports[`security > seed: 1337 > cwe() 1`] = `"CWE-3512"`; +exports[`security > seed: 1337 > cwe() 1`] = `"CWE-363"`; diff --git a/test/security.spec.ts b/test/security.spec.ts index 42547884a89..b6c126791bf 100644 --- a/test/security.spec.ts +++ b/test/security.spec.ts @@ -39,7 +39,7 @@ describe('security', () => { describe('cwe()', () => { it('should return a well formed string', () => { - expect(faker.security.cwe()).toMatch(/^CWE-[0-9]{4}/); + expect(faker.security.cwe()).toMatch(/^CWE-([0-9]+)$/); }); }); From 263da5cf27f9e008dda88411692a240225203d38 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Wed, 17 Aug 2022 07:59:29 +0200 Subject: [PATCH 07/20] chore: update JSdoc based on review feedback --- src/internal/toDate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/toDate.ts b/src/internal/toDate.ts index ea91cc8fd0d..def1b6b2740 100644 --- a/src/internal/toDate.ts +++ b/src/internal/toDate.ts @@ -2,7 +2,7 @@ * Converts date passed as a string, number or Date to a Date object. * If nothing or a non parseable value is passed, takes current date. * - * @param date Date + * @param date The input to convert to a date. */ export function toDate(date?: string | Date | number): Date { date = new Date(date); From 430cb90915c4cf2ea046888843f3ee3097ea2ce1 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Wed, 17 Aug 2022 08:09:01 +0200 Subject: [PATCH 08/20] test: switch to seeded test factory --- test/__snapshots__/security.spec.ts.snap | 36 ++++++++++++++++++++---- test/security.spec.ts | 20 ++----------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/test/__snapshots__/security.spec.ts.snap b/test/__snapshots__/security.spec.ts.snap index f8c672a7148..2436c6f0ecb 100644 --- a/test/__snapshots__/security.spec.ts.snap +++ b/test/__snapshots__/security.spec.ts.snap @@ -1,13 +1,37 @@ // Vitest Snapshot v1 -exports[`security > seed: 42 > cve() 1`] = `"CVE-2007-79177"`; +exports[`security > 42 > cve 1`] = `"CVE-2007-79177"`; -exports[`security > seed: 42 > cwe() 1`] = `"CWE-520"`; +exports[`security > 42 > cvss 1`] = ` +{ + "rating": "none", + "score": 0.5, + "vector": "CVSS:3.1/AV:A/AC:H/PR:H/UI:N/S:C/C:H/I:L/A:L", +} +`; -exports[`security > seed: 1211 > cve() 1`] = `"CVE-2020-48721"`; +exports[`security > 42 > cwe 1`] = `"CWE-520"`; -exports[`security > seed: 1211 > cwe() 1`] = `"CWE-1289"`; +exports[`security > 1211 > cve 1`] = `"CVE-2020-48721"`; -exports[`security > seed: 1337 > cve() 1`] = `"CVE-2005-51225"`; +exports[`security > 1211 > cvss 1`] = ` +{ + "rating": "high", + "score": 0.5, + "vector": "CVSS:3.1/AV:P/AC:L/PR:H/UI:R/S:U/C:N/I:H/A:N", +} +`; -exports[`security > seed: 1337 > cwe() 1`] = `"CWE-363"`; +exports[`security > 1211 > cwe 1`] = `"CWE-1289"`; + +exports[`security > 1337 > cve 1`] = `"CVE-2005-51225"`; + +exports[`security > 1337 > cvss 1`] = ` +{ + "rating": "low", + "score": 0.5, + "vector": "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N", +} +`; + +exports[`security > 1337 > cwe 1`] = `"CWE-363"`; diff --git a/test/security.spec.ts b/test/security.spec.ts index b6c126791bf..0f1ffc158fd 100644 --- a/test/security.spec.ts +++ b/test/security.spec.ts @@ -1,31 +1,17 @@ import { afterEach, describe, expect, it } from 'vitest'; import { faker } from '../src'; -import { seededRuns } from './support/seededRuns'; +import { seededTests } from './support/seededRuns'; const NON_SEEDED_BASED_RUN = 5; -const functionNames = ['cve', 'cwe']; +const functionNames = ['cve', 'cwe', 'cvss'] as const; describe('security', () => { afterEach(() => { faker.locale = 'en'; }); - for (const seed of seededRuns) { - describe(`seed: ${seed}`, () => { - for (const functionName of functionNames) { - it(`${functionName}()`, () => { - faker.seed(seed); - - const actual = faker.security[functionName](); - expect(actual).toMatchSnapshot(); - }); - } - }); - } - - // Create and log-back the seed for debug purposes - faker.seed(Math.ceil(Math.random() * 1_000_000_000)); + seededTests(faker, 'security', (t) => t.itEach(...functionNames)); describe(`random seeded tests for seed ${JSON.stringify( faker.seed() From df3e4a22ba0b35169c8bbd5f3c344312d5df5513 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Wed, 31 Aug 2022 18:21:04 +0200 Subject: [PATCH 09/20] chore: clarify variable naming --- src/modules/security/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index 2f0f04e327a..275f8ee511f 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -37,13 +37,13 @@ export class SecurityModule { to?: string | Date | number; } = {} ): string { - const fromMs = toDate(options?.from || '1999-01-01T00:00:00.000Z'); - const toMs = toDate(options?.to); + const fromDateTime = toDate(options?.from || '1999-01-01T00:00:00.000Z'); + const toDateTime = toDate(options?.to); return [ 'CVE', // Year - this.faker.date.between(fromMs, toMs).getFullYear(), + this.faker.date.between(fromDateTime, toDateTime).getFullYear(), // Sequence in the year this.faker.random.numeric(5, { allowLeadingZeros: true }), ].join('-'); From 29acc2ce5018ad9de9197b1418bbaf12be304cdf Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Wed, 31 Aug 2022 18:31:37 +0200 Subject: [PATCH 10/20] test: add tests where arguments are present --- test/__snapshots__/security.spec.ts.snap | 18 +++++++++++++++--- test/security.spec.ts | 12 +++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/test/__snapshots__/security.spec.ts.snap b/test/__snapshots__/security.spec.ts.snap index 2436c6f0ecb..48b1d276eca 100644 --- a/test/__snapshots__/security.spec.ts.snap +++ b/test/__snapshots__/security.spec.ts.snap @@ -1,6 +1,10 @@ // Vitest Snapshot v1 -exports[`security > 42 > cve 1`] = `"CVE-2007-79177"`; +exports[`security > 42 > cve > with from and to date 1`] = `"CVE-2010-79177"`; + +exports[`security > 42 > cve > with from date 1`] = `"CVE-2022-79177"`; + +exports[`security > 42 > cve > with to date 1`] = `"CVE-2007-79177"`; exports[`security > 42 > cvss 1`] = ` { @@ -12,7 +16,11 @@ exports[`security > 42 > cvss 1`] = ` exports[`security > 42 > cwe 1`] = `"CWE-520"`; -exports[`security > 1211 > cve 1`] = `"CVE-2020-48721"`; +exports[`security > 1211 > cve > with from and to date 1`] = `"CVE-2021-48721"`; + +exports[`security > 1211 > cve > with from date 1`] = `"CVE-2022-48721"`; + +exports[`security > 1211 > cve > with to date 1`] = `"CVE-2020-48721"`; exports[`security > 1211 > cvss 1`] = ` { @@ -24,7 +32,11 @@ exports[`security > 1211 > cvss 1`] = ` exports[`security > 1211 > cwe 1`] = `"CWE-1289"`; -exports[`security > 1337 > cve 1`] = `"CVE-2005-51225"`; +exports[`security > 1337 > cve > with from and to date 1`] = `"CVE-2007-51225"`; + +exports[`security > 1337 > cve > with from date 1`] = `"CVE-2022-51225"`; + +exports[`security > 1337 > cve > with to date 1`] = `"CVE-2005-51225"`; exports[`security > 1337 > cvss 1`] = ` { diff --git a/test/security.spec.ts b/test/security.spec.ts index 0f1ffc158fd..30ef6856321 100644 --- a/test/security.spec.ts +++ b/test/security.spec.ts @@ -4,14 +4,20 @@ import { seededTests } from './support/seededRuns'; const NON_SEEDED_BASED_RUN = 5; -const functionNames = ['cve', 'cwe', 'cvss'] as const; - describe('security', () => { afterEach(() => { faker.locale = 'en'; }); - seededTests(faker, 'security', (t) => t.itEach(...functionNames)); + seededTests(faker, 'security', (t) => { + t.describe('cve', (t) => + t + .it('with from date', { from: '2022-08-16' }) + .it('with to date', { to: '2022-08-16' }) + .it('with from and to date', { from: '2002-08-16', to: '2022-08-16' }) + ); + t.itEach('cwe', 'cvss'); + }); describe(`random seeded tests for seed ${JSON.stringify( faker.seed() From 01d0cdcb0dca1f47a6bfcf539169e6df43c5df38 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Wed, 31 Aug 2022 19:59:45 +0200 Subject: [PATCH 11/20] docs: fix missing bracket --- src/modules/security/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index 275f8ee511f..b2904ed2234 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -29,7 +29,7 @@ export class SecurityModule { * * @example * faker.security.cve() // 'CVE-2011-0762' - * faker.security.cve({from:'2020-01-01T00:00:00.000Z', to: '2030-01-01T00:00:00.000Z') // 'CVE-2028-0762' + * faker.security.cve({from:'2020-01-01T00:00:00.000Z', to: '2030-01-01T00:00:00.000Z'}) // 'CVE-2028-0762' */ cve( options: { From 66e0c3b10851a015c90203a9b18c6135b69fe083 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Thu, 1 Sep 2022 07:39:27 +0200 Subject: [PATCH 12/20] docs: adds missing JSDocs --- src/modules/security/index.ts | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index b2904ed2234..3dc7dee4e98 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -1,12 +1,37 @@ import type { Faker } from '../..'; import { toDate } from '../../internal/toDate'; +/** + * The possible definitions related to + * Common Vulnerability Scoring System (CVSS). + */ export interface Cvss { + /** + * A value ranging from 0 to 10. + */ score: number; + + /** + * A compressed textual representation of the values used to derive a score + */ vector: string; + + /** + * A textual representation of the numeric score. + * + * Where: + * - None – 0 + * - Low – 0.1 - 3.9 + * - Medium – 4.0 - 6.9 + * - High – 7.0 - 8.9 + * - Critical – 9.0 - 10.0 + */ rating: 'none' | 'low' | 'medium' | 'high' | 'critical'; } +/** + * Module to generate security related entries. + */ export class SecurityModule { constructor(private readonly faker: Faker) { // Bind `this` so namespaced is working correctly @@ -29,7 +54,7 @@ export class SecurityModule { * * @example * faker.security.cve() // 'CVE-2011-0762' - * faker.security.cve({from:'2020-01-01T00:00:00.000Z', to: '2030-01-01T00:00:00.000Z'}) // 'CVE-2028-0762' + * faker.security.cve({ from:'2020-01-01T00:00:00.000Z', to: '2030-01-01T00:00:00.000Z' }) // 'CVE-2028-0762' */ cve( options: { @@ -55,7 +80,7 @@ export class SecurityModule { * https://cwe.mitre.org/data/index.html * * @example - * faker.security.cwe() // 'CWE-####' + * faker.security.cwe() // 'CWE-123 */ cwe(): string { return ['CWE', this.faker.datatype.number({ min: 0, max: 1388 })].join('-'); From f72bfc64f76abaf5887d55c81c3f114c2c557234 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Thu, 1 Sep 2022 08:01:50 +0200 Subject: [PATCH 13/20] fix: implementation of cvss so score and rating are linked --- src/modules/security/index.ts | 45 ++++++++++++++++++------ test/__snapshots__/security.spec.ts.snap | 16 ++++----- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index 3dc7dee4e98..11694011e1b 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -26,9 +26,14 @@ export interface Cvss { * - High – 7.0 - 8.9 * - Critical – 9.0 - 10.0 */ - rating: 'none' | 'low' | 'medium' | 'high' | 'critical'; + rating: SeverityRating; } +/** + * Possible textual rating definitions for a CVSS identifier + */ +type SeverityRating = 'none' | 'low' | 'medium' | 'high' | 'critical'; + /** * Module to generate security related entries. */ @@ -80,7 +85,7 @@ export class SecurityModule { * https://cwe.mitre.org/data/index.html * * @example - * faker.security.cwe() // 'CWE-123 + * faker.security.cwe() // 'CWE-123' */ cwe(): string { return ['CWE', this.faker.datatype.number({ min: 0, max: 1388 })].join('-'); @@ -95,8 +100,13 @@ export class SecurityModule { * faker.security.cvss() */ cvss(): Cvss { + const score = this.faker.datatype.float({ + min: 0, + max: 10, + precision: 0.1, + }); return { - score: 0.5, + score, vector: [ 'CVSS:3.1', `AV:${this.faker.helpers.arrayElement('NALP'.split(''))}`, @@ -108,13 +118,28 @@ export class SecurityModule { `I:${this.faker.helpers.arrayElement('NLH'.split(''))}`, `A:${this.faker.helpers.arrayElement('NLH'.split(''))}`, ].join('/'), - rating: this.faker.helpers.arrayElement([ - 'none', - 'low', - 'medium', - 'high', - 'critical', - ]), + rating: getRanking(score), }; } } + +function getRanking(score: number): SeverityRating { + if (score === 0) { + return 'none'; + } + + if (score >= 0.1 && score <= 3.9) { + return 'low'; + } + if (score >= 4.0 && score <= 6.9) { + return 'medium'; + } + if (score >= 7.0 && score <= 8.9) { + return 'high'; + } + if (score >= 9.0 && score <= 10.0) { + return 'critical'; + } + + return 'none'; +} diff --git a/test/__snapshots__/security.spec.ts.snap b/test/__snapshots__/security.spec.ts.snap index 48b1d276eca..4846961d822 100644 --- a/test/__snapshots__/security.spec.ts.snap +++ b/test/__snapshots__/security.spec.ts.snap @@ -8,9 +8,9 @@ exports[`security > 42 > cve > with to date 1`] = `"CVE-2007-79177"`; exports[`security > 42 > cvss 1`] = ` { - "rating": "none", - "score": 0.5, - "vector": "CVSS:3.1/AV:A/AC:H/PR:H/UI:N/S:C/C:H/I:L/A:L", + "rating": "low", + "score": 3.7, + "vector": "CVSS:3.1/AV:P/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N", } `; @@ -24,9 +24,9 @@ exports[`security > 1211 > cve > with to date 1`] = `"CVE-2020-48721"`; exports[`security > 1211 > cvss 1`] = ` { - "rating": "high", - "score": 0.5, - "vector": "CVSS:3.1/AV:P/AC:L/PR:H/UI:R/S:U/C:N/I:H/A:N", + "rating": "critical", + "score": 9.3, + "vector": "CVSS:3.1/AV:A/AC:H/PR:H/UI:N/S:U/C:H/I:N/A:H", } `; @@ -41,8 +41,8 @@ exports[`security > 1337 > cve > with to date 1`] = `"CVE-2005-51225"`; exports[`security > 1337 > cvss 1`] = ` { "rating": "low", - "score": 0.5, - "vector": "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N", + "score": 2.6, + "vector": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N", } `; From 0734a2320998b3c3373c197a76e864d4fc8047c7 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Thu, 1 Sep 2022 08:03:33 +0200 Subject: [PATCH 14/20] docs: add security module to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 73bfa9e59f3..2ea725786e2 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ - 👾 Hacker Jargon - “Try to reboot the SQL bus, maybe it will bypass the virtual application!” - 🧍 Names - Generate virtual humans with a complete online and offline identity. - 🔢 Numbers - Of course, we can also generate random numbers and strings. +- 🛡 Security – Generate vulnerability identifiers > **Note**: Faker tries to generate realistic data and not obvious fake data. > The generated names, addresses, emails, phone numbers, and/or other data might be coincidentally valid information. From 5a5a7957e213b7f8711fefbe729a152a96290e0e Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Sun, 4 Sep 2022 20:04:01 +0200 Subject: [PATCH 15/20] Update src/modules/security/index.ts Co-authored-by: Eric Cheng --- src/modules/security/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index 11694011e1b..ce47f0b0a43 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -54,8 +54,8 @@ export class SecurityModule { * https://www.cve.org/ * * @param options The options to use. Defaults to `{}`. - * @param options.from The early date boundary. Defaults to `1999-01-01T00:00:00.000Z`. - * @param options.to The late date boundary. Defaults to `now`. + * @param options.from The lower date boundary. Defaults to `1999-01-01T00:00:00.000Z`. + * @param options.to The upper date boundary. Defaults to `now`. * * @example * faker.security.cve() // 'CVE-2011-0762' From feaa232ffbe023ffb45465828423ca3c1dc0795e Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Thu, 8 Sep 2022 07:49:31 +0200 Subject: [PATCH 16/20] docs: pr feedback --- src/internal/toDate.ts | 2 +- src/modules/security/index.ts | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/internal/toDate.ts b/src/internal/toDate.ts index def1b6b2740..52988468277 100644 --- a/src/internal/toDate.ts +++ b/src/internal/toDate.ts @@ -1,5 +1,5 @@ /** - * Converts date passed as a string, number or Date to a Date object. + * Converts date passed as a string, number, or Date to a Date object. * If nothing or a non parseable value is passed, takes current date. * * @param date The input to convert to a date. diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index ce47f0b0a43..a17c2aa8c8b 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -60,6 +60,8 @@ export class SecurityModule { * @example * faker.security.cve() // 'CVE-2011-0762' * faker.security.cve({ from:'2020-01-01T00:00:00.000Z', to: '2030-01-01T00:00:00.000Z' }) // 'CVE-2028-0762' + * faker.security.cve({ from:'2020-01-01T00:00:00.000Z' }) // 'CVE-2028-0762' + * faker.security.cve({ to: '2019-12-31T00:00:00.000Z' }) // 'CVE-2018-0762' */ cve( options: { @@ -92,7 +94,7 @@ export class SecurityModule { } /** - * Generates a random CVSS. + * Generates random CVSS (Common Vulnerability Scoring System) data. * Based on: * https://www.first.org/cvss/calculator/3.1 * @@ -118,12 +120,20 @@ export class SecurityModule { `I:${this.faker.helpers.arrayElement('NLH'.split(''))}`, `A:${this.faker.helpers.arrayElement('NLH'.split(''))}`, ].join('/'), - rating: getRanking(score), + rating: getRating(score), }; } } -function getRanking(score: number): SeverityRating { +/** + * Returns the textual representation of a score. + * + * @param score A number between 0 and 10. Defaults to `0`. + * + * @example + * getRating(1); // 'low' + */ +function getRating(score: number = 0): SeverityRating { if (score === 0) { return 'none'; } From d15d24a6258a4832e2ff1245f874e3ff61a27e1b Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Thu, 8 Sep 2022 07:55:08 +0200 Subject: [PATCH 17/20] docs: add example return value for security.cvss() --- src/modules/security/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index a17c2aa8c8b..cf80e0dc8c8 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -99,7 +99,7 @@ export class SecurityModule { * https://www.first.org/cvss/calculator/3.1 * * @example - * faker.security.cvss() + * faker.security.cvss() // { score: 3.8, vector: 'CVSS:3.1/AV:P/AC:H/PR:H/UI:R/S:U/C:H/I:N/A:N/E:P/RL:W/RC:C', rating: 'low' } */ cvss(): Cvss { const score = this.faker.datatype.float({ From 02e23677aad5405361d96fd0c7bd458d45cd1853 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Sat, 10 Sep 2022 09:21:43 +0200 Subject: [PATCH 18/20] fix: correct casing in filepath --- src/faker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/faker.ts b/src/faker.ts index 895e1ad386e..b2a76acc3c4 100644 --- a/src/faker.ts +++ b/src/faker.ts @@ -23,7 +23,7 @@ import { NameModule } from './modules/name'; import { PhoneModule } from './modules/phone'; import { RandomModule } from './modules/random'; import { ScienceModule } from './modules/science'; -import { SecurityModule } from './modules/Security'; +import { SecurityModule } from './modules/security'; import { SystemModule } from './modules/system'; import { UniqueModule } from './modules/unique'; import { VehicleModule } from './modules/vehicle'; From 3901aacce7bf526dfeb000bb3da2c9b8adaf4682 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Sat, 10 Sep 2022 09:22:40 +0200 Subject: [PATCH 19/20] chore: refactor to strictly typed convience method --- src/modules/security/index.ts | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index cf80e0dc8c8..ca280ab8a2b 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -1,4 +1,5 @@ import type { Faker } from '../..'; +import { faker } from '../..'; import { toDate } from '../../internal/toDate'; /** @@ -111,20 +112,32 @@ export class SecurityModule { score, vector: [ 'CVSS:3.1', - `AV:${this.faker.helpers.arrayElement('NALP'.split(''))}`, - `AC:${this.faker.helpers.arrayElement('LH'.split(''))}`, - `PR:${this.faker.helpers.arrayElement('NLH'.split(''))}`, - `UI:${this.faker.helpers.arrayElement('NR'.split(''))}`, - `S:${this.faker.helpers.arrayElement('UC'.split(''))}`, - `C:${this.faker.helpers.arrayElement('NLH'.split(''))}`, - `I:${this.faker.helpers.arrayElement('NLH'.split(''))}`, - `A:${this.faker.helpers.arrayElement('NLH'.split(''))}`, + `AV:${randomCharFromString('NALP')}`, + `AC:${randomCharFromString('LH')}`, + `PR:${randomCharFromString('NLH')}`, + `UI:${randomCharFromString('NR')}`, + `S:${randomCharFromString('UC')}`, + `C:${randomCharFromString('NLH')}`, + `I:${randomCharFromString('NLH')}`, + `A:${randomCharFromString('NLH')}`, ].join('/'), rating: getRating(score), }; } } +/** + * Returns a random character from a string. + * + * @param string + * + * @example + * randomCharFromString('abc'); // 'b' + */ +function randomCharFromString(string: string): string { + return String(faker.helpers.arrayElement(string.split(''))); +} + /** * Returns the textual representation of a score. * From f4b2053c33ad6592f257546f9c4256464ce5c135 Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Sat, 10 Sep 2022 09:25:08 +0200 Subject: [PATCH 20/20] chore: export types for security module --- src/index.ts | 1 + src/internal/toDate.ts | 2 ++ src/modules/security/index.ts | 8 +++++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 984a8c758d5..c5661f952ea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,6 +60,7 @@ export type { GenderType, NameModule, SexType } from './modules/name'; export type { PhoneModule } from './modules/phone'; export type { RandomModule } from './modules/random'; export type { ChemicalElement, ScienceModule, Unit } from './modules/science'; +export type { Cvss, SecurityModule, SeverityRating } from './modules/security'; export type { SystemModule } from './modules/system'; export type { UniqueModule } from './modules/unique'; export type { VehicleModule } from './modules/vehicle'; diff --git a/src/internal/toDate.ts b/src/internal/toDate.ts index 52988468277..86ea3e8fd06 100644 --- a/src/internal/toDate.ts +++ b/src/internal/toDate.ts @@ -3,6 +3,8 @@ * If nothing or a non parseable value is passed, takes current date. * * @param date The input to convert to a date. + * + * @since 8.0.0 */ export function toDate(date?: string | Date | number): Date { date = new Date(date); diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts index ca280ab8a2b..2247c4f9f72 100644 --- a/src/modules/security/index.ts +++ b/src/modules/security/index.ts @@ -33,7 +33,7 @@ export interface Cvss { /** * Possible textual rating definitions for a CVSS identifier */ -type SeverityRating = 'none' | 'low' | 'medium' | 'high' | 'critical'; +export type SeverityRating = 'none' | 'low' | 'medium' | 'high' | 'critical'; /** * Module to generate security related entries. @@ -63,6 +63,8 @@ export class SecurityModule { * faker.security.cve({ from:'2020-01-01T00:00:00.000Z', to: '2030-01-01T00:00:00.000Z' }) // 'CVE-2028-0762' * faker.security.cve({ from:'2020-01-01T00:00:00.000Z' }) // 'CVE-2028-0762' * faker.security.cve({ to: '2019-12-31T00:00:00.000Z' }) // 'CVE-2018-0762' + * + * @since 8.0.0 */ cve( options: { @@ -89,6 +91,8 @@ export class SecurityModule { * * @example * faker.security.cwe() // 'CWE-123' + * + * @since 8.0.0 */ cwe(): string { return ['CWE', this.faker.datatype.number({ min: 0, max: 1388 })].join('-'); @@ -101,6 +105,8 @@ export class SecurityModule { * * @example * faker.security.cvss() // { score: 3.8, vector: 'CVSS:3.1/AV:P/AC:H/PR:H/UI:R/S:U/C:H/I:N/A:N/E:P/RL:W/RC:C', rating: 'low' } + * + * @since 8.0.0 */ cvss(): Cvss { const score = this.faker.datatype.float({