From 19ffcc032bb8aa0611a1ed6b3518806b784cda4d Mon Sep 17 00:00:00 2001 From: Luke Watts Date: Wed, 13 Jul 2022 23:43:26 +0200 Subject: [PATCH] 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 1d41948ffcc..4c674a5274d 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 c84d77a4daf..f14c9a411a5 100644 --- a/src/faker.ts +++ b/src/faker.ts @@ -23,6 +23,7 @@ import { Name } from './modules/name'; import { Phone } from './modules/phone'; import { Random } from './modules/random'; import { Science } from './modules/science'; +import { Security } from './modules/security'; import { System } from './modules/system'; import { Unique } from './modules/unique'; import { Vehicle } from './modules/vehicle'; @@ -102,6 +103,7 @@ export class Faker { readonly music: Music = new Music(this); readonly name: Name = new Name(this); readonly phone: Phone = new Phone(this); + readonly security: Security = new Security(this); readonly science: Science = new Science(this); readonly system: System = new System(this); readonly vehicle: Vehicle = new Vehicle(this); diff --git a/src/modules/security/index.ts b/src/modules/security/index.ts new file mode 100644 index 00000000000..18291066ffc --- /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 Security { + constructor(private readonly faker: Faker) { + // Bind `this` so namespaced is working correctly + for (const name of Object.getOwnPropertyNames(Security.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]/ + ); + }); + }); + } + }); +});