From 92529e2a7b587aa2a7e8acdf22a46245b10834bf Mon Sep 17 00:00:00 2001 From: hupe1980 Date: Thu, 30 Jun 2022 07:50:11 +0200 Subject: [PATCH] Add risk aspect --- API.md | 564 ++++++++++++++++++++++++++--------------- src/communication.ts | 28 +- src/index.ts | 1 + src/model.ts | 4 + src/risk-category.ts | 2 +- src/risks.ts | 16 ++ src/technical-asset.ts | 4 +- 7 files changed, 406 insertions(+), 213 deletions(-) create mode 100644 src/risks.ts diff --git a/API.md b/API.md index 43b0d6f..0b5107b 100644 --- a/API.md +++ b/API.md @@ -1053,6 +1053,320 @@ public readonly tags: string[]; --- +### Communication + +#### Initializers + +```typescript +import { Communication } from 'cdktg' + +new Communication(scope: Construct, id: string, props: CommunicationProps) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | CommunicationProps | *No description.* | + +--- + +##### `scope`Required + +- *Type:* constructs.Construct + +--- + +##### `id`Required + +- *Type:* string + +--- + +##### `props`Required + +- *Type:* CommunicationProps + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| toString | Returns a string representation of this construct. | +| hasDataAssets | *No description.* | +| isBidirectional | *No description.* | +| isEncrypted | *No description.* | +| isProcessLocal | *No description.* | +| receives | *No description.* | +| sends | *No description.* | + +--- + +##### `toString` + +```typescript +public toString(): string +``` + +Returns a string representation of this construct. + +##### `hasDataAssets` + +```typescript +public hasDataAssets(): boolean +``` + +##### `isBidirectional` + +```typescript +public isBidirectional(): boolean +``` + +##### `isEncrypted` + +```typescript +public isEncrypted(): boolean +``` + +##### `isProcessLocal` + +```typescript +public isProcessLocal(): boolean +``` + +##### `receives` + +```typescript +public receives(assets: DataAsset): void +``` + +###### `assets`Required + +- *Type:* DataAsset + +--- + +##### `sends` + +```typescript +public sends(assets: DataAsset): void +``` + +###### `assets`Required + +- *Type:* DataAsset + +--- + +#### Static Functions + +| **Name** | **Description** | +| --- | --- | +| isConstruct | Checks if `x` is a construct. | +| isCommunicationl | *No description.* | + +--- + +##### `isConstruct` + +```typescript +import { Communication } from 'cdktg' + +Communication.isConstruct(x: any) +``` + +Checks if `x` is a construct. + +Use this method instead of `instanceof` to properly detect `Construct` +instances, even when the construct library is symlinked. + +Explanation: in JavaScript, multiple copies of the `constructs` library on +disk are seen as independent, completely different libraries. As a +consequence, the class `Construct` in each copy of the `constructs` library +is seen as a different class, and an instance of one class will not test as +`instanceof` the other class. `npm install` will not create installations +like this, but users may manually symlink construct libraries together or +use a monorepo tool: in those cases, multiple copies of the `constructs` +library can be accidentally installed, and `instanceof` will behave +unpredictably. It is safest to avoid using `instanceof`, and using +this type-testing method instead. + +###### `x`Required + +- *Type:* any + +Any object. + +--- + +##### `isCommunicationl` + +```typescript +import { Communication } from 'cdktg' + +Communication.isCommunicationl(x: any) +``` + +###### `x`Required + +- *Type:* any + +--- + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | +| authentication | Authentication | *No description.* | +| authorization | Authorization | *No description.* | +| description | string | *No description.* | +| id | string | *No description.* | +| ipFiltered | boolean | *No description.* | +| protocol | Protocol | *No description.* | +| readonly | boolean | *No description.* | +| source | TechnicalAsset | *No description.* | +| target | TechnicalAsset | *No description.* | +| title | string | *No description.* | +| usage | Usage | *No description.* | +| vpn | boolean | *No description.* | + +--- + +##### `node`Required + +```typescript +public readonly node: Node; +``` + +- *Type:* constructs.Node + +The tree node. + +--- + +##### `authentication`Required + +```typescript +public readonly authentication: Authentication; +``` + +- *Type:* Authentication + +--- + +##### `authorization`Required + +```typescript +public readonly authorization: Authorization; +``` + +- *Type:* Authorization + +--- + +##### `description`Required + +```typescript +public readonly description: string; +``` + +- *Type:* string + +--- + +##### `id`Required + +```typescript +public readonly id: string; +``` + +- *Type:* string + +--- + +##### `ipFiltered`Required + +```typescript +public readonly ipFiltered: boolean; +``` + +- *Type:* boolean + +--- + +##### `protocol`Required + +```typescript +public readonly protocol: Protocol; +``` + +- *Type:* Protocol + +--- + +##### `readonly`Required + +```typescript +public readonly readonly: boolean; +``` + +- *Type:* boolean + +--- + +##### `source`Required + +```typescript +public readonly source: TechnicalAsset; +``` + +- *Type:* TechnicalAsset + +--- + +##### `target`Required + +```typescript +public readonly target: TechnicalAsset; +``` + +- *Type:* TechnicalAsset + +--- + +##### `title`Required + +```typescript +public readonly title: string; +``` + +- *Type:* string + +--- + +##### `usage`Required + +```typescript +public readonly usage: Usage; +``` + +- *Type:* Usage + +--- + +##### `vpn`Required + +```typescript +public readonly vpn: boolean; +``` + +- *Type:* boolean + +--- + + ### DataAsset #### Initializers @@ -6243,212 +6557,6 @@ public readonly justification: string; --- -### Communication - -#### Initializers - -```typescript -import { Communication } from 'cdktg' - -new Communication(title: string, props: CommunicationProps) -``` - -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| title | string | *No description.* | -| props | CommunicationProps | *No description.* | - ---- - -##### `title`Required - -- *Type:* string - ---- - -##### `props`Required - -- *Type:* CommunicationProps - ---- - -#### Methods - -| **Name** | **Description** | -| --- | --- | -| isEncrypted | *No description.* | -| isProcessLocal | *No description.* | -| receives | *No description.* | -| sends | *No description.* | - ---- - -##### `isEncrypted` - -```typescript -public isEncrypted(): boolean -``` - -##### `isProcessLocal` - -```typescript -public isProcessLocal(): boolean -``` - -##### `receives` - -```typescript -public receives(assets: DataAsset): void -``` - -###### `assets`Required - -- *Type:* DataAsset - ---- - -##### `sends` - -```typescript -public sends(assets: DataAsset): void -``` - -###### `assets`Required - -- *Type:* DataAsset - ---- - - -#### Properties - -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| authentication | Authentication | *No description.* | -| authorization | Authorization | *No description.* | -| description | string | *No description.* | -| ipFiltered | boolean | *No description.* | -| protocol | Protocol | *No description.* | -| readonly | boolean | *No description.* | -| source | TechnicalAsset | *No description.* | -| target | TechnicalAsset | *No description.* | -| title | string | *No description.* | -| usage | Usage | *No description.* | -| vpn | boolean | *No description.* | - ---- - -##### `authentication`Required - -```typescript -public readonly authentication: Authentication; -``` - -- *Type:* Authentication - ---- - -##### `authorization`Required - -```typescript -public readonly authorization: Authorization; -``` - -- *Type:* Authorization - ---- - -##### `description`Required - -```typescript -public readonly description: string; -``` - -- *Type:* string - ---- - -##### `ipFiltered`Required - -```typescript -public readonly ipFiltered: boolean; -``` - -- *Type:* boolean - ---- - -##### `protocol`Required - -```typescript -public readonly protocol: Protocol; -``` - -- *Type:* Protocol - ---- - -##### `readonly`Required - -```typescript -public readonly readonly: boolean; -``` - -- *Type:* boolean - ---- - -##### `source`Required - -```typescript -public readonly source: TechnicalAsset; -``` - -- *Type:* TechnicalAsset - ---- - -##### `target`Required - -```typescript -public readonly target: TechnicalAsset; -``` - -- *Type:* TechnicalAsset - ---- - -##### `title`Required - -```typescript -public readonly title: string; -``` - -- *Type:* string - ---- - -##### `usage`Required - -```typescript -public readonly usage: Usage; -``` - -- *Type:* Usage - ---- - -##### `vpn`Required - -```typescript -public readonly vpn: boolean; -``` - -- *Type:* boolean - ---- - - ### Image #### Initializers @@ -6949,6 +7057,48 @@ public readonly mostRelevantTrustBoundary: TrustBoundary; --- +### RiskAspect + +- *Implements:* IAspect + +#### Initializers + +```typescript +import { RiskAspect } from 'cdktg' + +new RiskAspect() +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| visit | All aspects can visit an IConstruct. | + +--- + +##### `visit` + +```typescript +public visit(node: IConstruct): void +``` + +All aspects can visit an IConstruct. + +###### `node`Required + +- *Type:* constructs.IConstruct + +--- + + + + ### RiskTracking #### Initializers @@ -7260,7 +7410,7 @@ Testing.model() ### IAspect -- *Implemented By:* IAspect +- *Implemented By:* RiskAspect, IAspect Represents an Aspect. diff --git a/src/communication.ts b/src/communication.ts index 6a09c03..f778028 100644 --- a/src/communication.ts +++ b/src/communication.ts @@ -1,7 +1,10 @@ +import { Construct } from "constructs"; import { DataAsset } from "./data-asset"; import { TechnicalAsset } from "./technical-asset"; import { Usage } from "./usage"; +const COMMUNICATION_SYMBOL = Symbol.for("cdktg/Communication"); + export interface CommunicationOptions { readonly description: string; readonly protocol: Protocol; @@ -18,7 +21,12 @@ export interface CommunicationProps extends CommunicationOptions { readonly target: TechnicalAsset; } -export class Communication { +export class Communication extends Construct { + public static isCommunicationl(x: any): x is Communication { + return x !== null && typeof x === "object" && COMMUNICATION_SYMBOL in x; + } + + public readonly id: string; public readonly title: string; public readonly source: TechnicalAsset; public readonly target: TechnicalAsset; @@ -34,8 +42,10 @@ export class Communication { private dataAssetsSent: Set; private dataAssetsReceived: Set; - constructor(title: string, props: CommunicationProps) { - this.title = title; + constructor(scope: Construct, id: string, props: CommunicationProps) { + super(scope, id); + this.id = createId(props.source.id, id); + this.title = id; this.source = props.source; this.target = props.target; this.description = props.description; @@ -63,6 +73,10 @@ export class Communication { }); } + public hasDataAssets(): boolean { + return this.dataAssetsSent.size > 0 || this.dataAssetsReceived.size > 0; + } + public isEncrypted(): boolean { return [ Protocol.HTTPS, @@ -96,6 +110,10 @@ export class Communication { ].includes(this.protocol); } + public isBidirectional(): boolean { + return this.dataAssetsSent.size > 0 && this.dataAssetsReceived.size > 0; + } + /** * @internal */ @@ -120,6 +138,10 @@ export class Communication { } } +function createId(sourceAssetId: string, title: string): string { + return sourceAssetId + ">" + title.toLowerCase(); +} + export enum Protocol { UNKNOEN = "unknown-protocol", HTTP = "http", diff --git a/src/index.ts b/src/index.ts index 2655bc9..4646996 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ export * from "./aspect"; export * from "./resource"; export * from "./risk-category"; export * from "./risk-tracking"; +export * from "./risks"; export * from "./author"; export * from "./cia-triad"; export * from "./communication"; diff --git a/src/model.ts b/src/model.ts index ed25e7b..8427c72 100644 --- a/src/model.ts +++ b/src/model.ts @@ -1,11 +1,13 @@ import { Construct, IConstruct } from "constructs"; import { AbuseCase } from "./abuse-case"; +import { Aspects } from "./aspect"; import { Author } from "./author"; import { DataAsset } from "./data-asset"; import { Overview } from "./overview"; import { RiskCategory } from "./risk-category"; import { RiskTracking, RiskTrackingProps } from "./risk-tracking"; +import { RiskAspect } from "./risks"; import { SecurityRequirement } from "./security-requirement"; import { SharedRuntime } from "./shared-runtime"; import { Threagile } from "./spec/threatgile.generated"; @@ -165,6 +167,8 @@ export class Model extends Construct { this.tags = new Set(); this.riskTracking = new Map(); this.rawOverrides = {}; + + Aspects.of(this).add(new RiskAspect()); } public addTag(tag: string) { diff --git a/src/risk-category.ts b/src/risk-category.ts index 32c5d22..5cf229b 100644 --- a/src/risk-category.ts +++ b/src/risk-category.ts @@ -67,7 +67,7 @@ export class Risk { most_relevant_data_asset: this.mostRelevantDataAsset?.id, most_relevant_technical_asset: this.mostRelevantTechnicalAsset?.id, most_relevant_communication_link: - this.mostRelevantCommunicationLink?.title, + this.mostRelevantCommunicationLink?.id, most_relevant_trust_boundary: this.mostRelevantTrustBoundary?.id, most_relevant_shared_runtime: this.mostRelevantSharedRuntime?.id, }, diff --git a/src/risks.ts b/src/risks.ts new file mode 100644 index 0000000..f5ff70d --- /dev/null +++ b/src/risks.ts @@ -0,0 +1,16 @@ +import { IConstruct } from "constructs"; +import { Annotations } from "./annotations"; +import { IAspect } from "./aspect"; +import { Communication } from "./communication"; + +export class RiskAspect implements IAspect { + visit(node: IConstruct): void { + if (Communication.isCommunicationl(node)) { + // When a technical communication link does not send or receive any data assets, this is + // an indicator for an unnecessary communication link (or for an incomplete model). + if (!node.hasDataAssets()) { + Annotations.of(node).addWarning("Unnecessary Communication Link"); + } + } + } +} diff --git a/src/technical-asset.ts b/src/technical-asset.ts index 08a53e5..f8b18f6 100644 --- a/src/technical-asset.ts +++ b/src/technical-asset.ts @@ -168,7 +168,7 @@ export class TechnicalAsset extends Resource { target: TechnicalAsset, options: CommunicationOptions ): Communication { - const communication = new Communication(id, { + const communication = new Communication(this, id, { source: this, target: target, ...options, @@ -217,7 +217,7 @@ export class TechnicalAsset extends Resource { }, }; - threagile[this.node.id].communication_links = this.communications.reduce( + threagile[this.title].communication_links = this.communications.reduce( (prev, current) => Object.assign(prev, current._toThreagile()), {} );