@infrascan/shared-types 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @infrascan/shared-types
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#89](https://github.com/infrascan/infrascan/pull/89) [`913867b73a72791c79a69ebfec8bf94331654c1d`](https://github.com/infrascan/infrascan/commit/913867b73a72791c79a69ebfec8bf94331654c1d) Thanks [@lfarrel6](https://github.com/lfarrel6)! - Migrate graph output to a standard schema for easier search/filter
8
+
9
+ ## 0.5.1
10
+
11
+ ### Patch Changes
12
+
13
+ - [#84](https://github.com/infrascan/infrascan/pull/84) [`437026c`](https://github.com/infrascan/infrascan/commit/437026cc278ec4b380bcaf3a7a675f3762ce3bea) Thanks [@lfarrel6](https://github.com/lfarrel6)! - Bump aws client sdk versions to remove vulnerable dependency
14
+
3
15
  ## 0.5.0
4
16
 
5
17
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infrascan/shared-types",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Shared types for multiple infrascan packages",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
@@ -12,7 +12,7 @@
12
12
  "author": "Liam Farrelly <liam@l3f7.dev> (https://whoisli.am/)",
13
13
  "license": "MPL-2.0",
14
14
  "devDependencies": {
15
- "@aws-sdk/types": "3.428.0",
15
+ "@aws-sdk/types": "3.609.0",
16
16
  "eslint-config-custom": "*",
17
17
  "tsconfig": "*",
18
18
  "typescript": "^5.1.6"
package/src/api.ts CHANGED
@@ -1,7 +1,12 @@
1
- import type { AwsCredentialIdentityProvider, RetryStrategy, RetryStrategyV2 } from "@aws-sdk/types";
1
+ import type {
2
+ AwsCredentialIdentityProvider,
3
+ RetryStrategy,
4
+ RetryStrategyV2,
5
+ } from "@aws-sdk/types";
2
6
 
3
7
  import type { SelectedEdge, SelectedNode } from "./graph";
4
8
  import type { BaseEdgeResolver } from "./config";
9
+ import type { TranslatedEntity, BaseState } from "./scan";
5
10
 
6
11
  /**
7
12
  * Callback to store state from a specific function call
@@ -73,7 +78,7 @@ export interface Connector {
73
78
  type ClientBuilder<T, P extends Provider> = (
74
79
  credentials: AwsCredentialIdentityProvider,
75
80
  context: ProviderContextMap[P],
76
- retryStrategy?: RetryStrategy | RetryStrategyV2
81
+ retryStrategy?: RetryStrategy | RetryStrategyV2,
77
82
  ) => T;
78
83
 
79
84
  type GetterFn<T, P extends Provider> = (
@@ -112,7 +117,15 @@ export type EntityRoleData = {
112
117
  };
113
118
  type GetIamRoleFn = (stateConnector: Connector) => Promise<EntityRoleData[]>;
114
119
 
115
- export interface ServiceModule<T, P extends Provider> {
120
+ export interface ServiceModule<
121
+ T,
122
+ P extends Provider,
123
+ E extends TranslatedEntity<BaseState, any, any> = TranslatedEntity<
124
+ BaseState,
125
+ any,
126
+ any
127
+ >,
128
+ > {
116
129
  provider: P;
117
130
  service: string;
118
131
  arnLabel?: string;
@@ -120,9 +133,9 @@ export interface ServiceModule<T, P extends Provider> {
120
133
  getClient: ClientBuilder<T, P>;
121
134
  callPerRegion: boolean;
122
135
  getters: GetterFn<T, P>[];
123
- nodes?: string[];
124
136
  getNodes?: GetNodeFn<P>;
125
137
  edges?: BaseEdgeResolver[];
126
138
  getEdges?: GetEdgeFn;
127
139
  getIamRoles?: GetIamRoleFn;
140
+ entities?: E[];
128
141
  }
package/src/config.ts CHANGED
@@ -38,8 +38,6 @@ export type BaseScannerDefinition = {
38
38
  callPerRegion: boolean;
39
39
  // Scanner functions for pulling state from a service
40
40
  getters: BaseGetter[];
41
- // Scrapers for pulling Nodes from service state
42
- nodes?: GenericNodeSelector[];
43
41
  // Scrapers for pulling Edges from service state
44
42
  edges?: BaseEdgeResolver[];
45
43
  // Scrapers for pulling IAM Roles from service state, used to generate permission based edges
@@ -126,7 +124,6 @@ export type ScannerDefinition<
126
124
  skipClientBuilder?: boolean;
127
125
  callPerRegion: boolean;
128
126
  getters: ServiceGetter<ServiceName, Serv, Funcs>[];
129
- nodes?: StateSelector<ServiceName, Serv, Funcs>[];
130
127
  edges?: EdgeSelector<ServiceName, Serv, Funcs>[];
131
128
  iamRoles?: StateSelector<ServiceName, Serv, Funcs>[];
132
129
  };
package/src/graph.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { BaseState } from "./scan";
2
+
1
3
  /**
2
4
  * A node returned from a state selector before its been formatted for a graphing library
3
5
  */
@@ -6,6 +8,7 @@ export interface SelectedNode {
6
8
  name?: string;
7
9
  parent?: string;
8
10
  type?: string;
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
12
  rawState?: any;
10
13
  }
11
14
 
@@ -26,16 +29,19 @@ export type ElementType<ReadType, WriteType> = {
26
29
  readonly _write: WriteType;
27
30
  };
28
31
 
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
33
  export type ElementRecord<K extends keyof any, R, W> = ElementType<
30
34
  Record<K, Readable<R>>,
31
35
  Record<K, Writable<W>>
32
36
  >;
33
37
 
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
39
  export type ReadType<T> = T extends ElementType<infer R, any> ? Readable<R> : T;
35
40
  export type Readable<T> = {
36
41
  [K in keyof T]: ReadType<T[K]>;
37
42
  };
38
43
 
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
45
  export type WriteType<T> = T extends ElementType<any, infer W>
40
46
  ? Writable<W>
41
47
  : T;
@@ -43,7 +49,14 @@ export type Writable<T> = {
43
49
  [K in keyof T]: WriteType<T[K]>;
44
50
  };
45
51
 
46
- export interface Node {
52
+ export interface Node extends BaseState<unknown> {
53
+ parent?: ElementType<Node, string>;
54
+ children?: ElementRecord<string, Node, string>;
55
+ incomingEdges: ElementRecord<string, Edge, string>;
56
+ outgoingEdges: ElementRecord<string, Edge, string>;
57
+ }
58
+
59
+ export interface NodeOld {
47
60
  id: string;
48
61
  name: string;
49
62
  metadata: Record<string, unknown>;
@@ -66,12 +79,7 @@ export interface Edge {
66
79
  export interface Graph {
67
80
  nodes: Readable<Node>[];
68
81
  edges: Readable<Edge>[];
69
- addNode: (
70
- node: Pick<
71
- Writable<Node>,
72
- "id" | "name" | "metadata" | "parent" | "service" | "type"
73
- >,
74
- ) => void;
82
+ addNode: (node: BaseState<unknown>) => void;
75
83
  addEdge: (
76
84
  edge: Pick<Writable<Edge>, "name" | "source" | "target" | "metadata">,
77
85
  ) => void;
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from "./api";
2
2
  export * from "./config";
3
3
  export * from "./graph";
4
- export * from "./scan";
5
- export * from "./plugins";
4
+ export * from "./plugins";
5
+ export * from "./scan";
package/src/plugins.ts CHANGED
@@ -2,25 +2,39 @@ import { Graph } from "./graph";
2
2
  import { AwsContext } from "./api";
3
3
 
4
4
  // All supported events to trigger a plugin
5
- export type GraphPluginEvents = "onServiceComplete" | "onRegionComplete" | "onAccountComplete" | "onGraphComplete";
5
+ export type GraphPluginEvents =
6
+ | "onServiceComplete"
7
+ | "onRegionComplete"
8
+ | "onAccountComplete"
9
+ | "onGraphComplete";
6
10
 
7
11
  /**
8
12
  * A plugin which is called everytime a service's nodes have been added to the graph.
9
13
  * Receives the current graph state, the service ID, and the current AWS Context.
10
14
  */
11
- export type OnServiceCompletePlugin = (graph: Graph, service: string, context: AwsContext) => void | Promise<void>;
15
+ export type OnServiceCompletePlugin = (
16
+ graph: Graph,
17
+ service: string,
18
+ context: AwsContext,
19
+ ) => void | Promise<void>;
12
20
 
13
21
  /**
14
22
  * A plugin which is called everytime a region's nodes have been added to the graph.
15
23
  * Receives the current graph state, and the current AWS Context.
16
24
  */
17
- export type OnRegionCompletePlugin = (graph: Graph, context: AwsContext) => void | Promise<void>;
25
+ export type OnRegionCompletePlugin = (
26
+ graph: Graph,
27
+ context: AwsContext,
28
+ ) => void | Promise<void>;
18
29
 
19
30
  /**
20
31
  * A plugin which is called everytime an account's nodes have been added to the graph.
21
32
  * Receives the current graph state, and the current AWS Context.
22
33
  */
23
- export type OnAccountCompletePlugin = (graph: Graph, context: AwsContext) => void | Promise<void>;
34
+ export type OnAccountCompletePlugin = (
35
+ graph: Graph,
36
+ context: AwsContext,
37
+ ) => void | Promise<void>;
24
38
 
25
39
  /**
26
40
  * A plugin which is called when the entire graph has been generated.
@@ -29,14 +43,14 @@ export type OnAccountCompletePlugin = (graph: Graph, context: AwsContext) => voi
29
43
  export type OnGraphCompletePlugin = (graph: Graph) => void | Promise<void>;
30
44
 
31
45
  export type PluginEventHandlerTypes = {
32
- "onServiceComplete": OnServiceCompletePlugin,
33
- "onRegionComplete": OnRegionCompletePlugin,
34
- "onAccountComplete": OnAccountCompletePlugin,
35
- "onGraphComplete": OnGraphCompletePlugin,
46
+ onServiceComplete: OnServiceCompletePlugin;
47
+ onRegionComplete: OnRegionCompletePlugin;
48
+ onAccountComplete: OnAccountCompletePlugin;
49
+ onGraphComplete: OnGraphCompletePlugin;
36
50
  };
37
51
 
38
52
  export interface GraphPlugin<E extends GraphPluginEvents> {
39
- id: string,
40
- event: E,
41
- handler: PluginEventHandlerTypes[E]
42
- }
53
+ id: string;
54
+ event: E;
55
+ handler: PluginEventHandlerTypes[E];
56
+ }
package/src/scan.ts CHANGED
@@ -1,12 +1,347 @@
1
- export type State<T> = {
2
- _metadata: {
3
- account: string;
4
- region: string;
5
- };
6
- _result: T;
7
- /* eslint-disable @typescript-eslint/no-explicit-any */
8
- _parameters?: any;
1
+ import { AwsContext, Connector } from "./api";
2
+
3
+ export interface CommandCallMetadata {
4
+ account: string;
5
+ region: string;
6
+ partition?: string;
7
+ timestamp: number;
8
+ }
9
+
10
+ export interface State<O, I = unknown> {
11
+ _metadata: CommandCallMetadata;
12
+ _result: O;
13
+ _parameters?: I;
14
+ }
15
+
16
+ export type GenericState = State<unknown>;
17
+
18
+ /**
19
+ * Metadata for any piece of state generated — when it was scanned, and what version of the state schema it uses
20
+ */
21
+ export interface StateMetadata {
22
+ version: string;
23
+ /**
24
+ * Epoch value (in ms)
25
+ */
26
+ timestamp: number;
27
+ }
28
+
29
+ /**
30
+ * Coordinates of a node in the graph including an optional position on the Z axis
31
+ */
32
+ export interface Coordinates {
33
+ x: number;
34
+ y: number;
35
+ z?: number;
36
+ }
37
+
38
+ /**
39
+ * Details about the nodes position in the graph and how it relates to other components
40
+ */
41
+ export interface GraphInfo {
42
+ /**
43
+ * The unique ID of the node in the graph
44
+ */
45
+ id: string;
46
+ /**
47
+ * The human readable name of the node in the graph
48
+ */
49
+ label: string;
50
+ /**
51
+ * The type of the node, used for assigning an icon in the graph
52
+ */
53
+ nodeType: string;
54
+ /**
55
+ * The ID of the parent node, optional.
56
+ */
57
+ parent?: string;
58
+ /**
59
+ * The coordinates of the node in the graph, optional.
60
+ */
61
+ coords?: Coordinates;
62
+ }
63
+
64
+ /**
65
+ * The tenant that the resource has been created in, typically an account or org. Should be defined for every resource.
66
+ */
67
+ export interface Tenant {
68
+ /**
69
+ * CSP that the state refers to (aws, gcp, azure)
70
+ */
71
+ provider: string;
72
+ /**
73
+ * The partition of this state e.g. govcloud, cn partitions
74
+ */
75
+ partition?: string;
76
+ /**
77
+ * The unique ID for the tenant (e.g. account id)
78
+ */
79
+ tenantId: string;
80
+ }
81
+
82
+ /**
83
+ * The geo data for a resource tracking where it is deployed to and, if known, which specific zones (i.e. datacenters) its in.
84
+ */
85
+ export interface Location {
86
+ /**
87
+ * The standard reference name for the region e.g. us-east-1
88
+ */
89
+ code: string;
90
+ /**
91
+ * The availability zone/datacenter that the resource is in e.g. us-east-1a
92
+ */
93
+ zone?: string[];
94
+ }
95
+
96
+ /**
97
+ * Details which identify a resource within the state
98
+ */
99
+ export interface Resource {
100
+ /**
101
+ * Category tracks whether this resource is a higher level concept like an account, or a service of a resource
102
+ */
103
+ category: string;
104
+ /**
105
+ * Subcategory tracks the resource's more specific resource type e.g. sub-service entity type. Not set for higher level concepts
106
+ */
107
+ subcategory?: string;
108
+ /**
109
+ * The unique ID of the resource — either an account number or a resource ID
110
+ */
111
+ id: string;
112
+ /**
113
+ * The human-readable name of the resource
114
+ */
115
+ name: string;
116
+ /**
117
+ * The recorded description for the resource
118
+ */
119
+ description?: string;
120
+ /**
121
+ * The resource level policy which dictates access
122
+ */
123
+ policy?: string;
124
+ }
125
+
126
+ export interface Role {
127
+ arn: string;
128
+ label: string;
129
+ }
130
+
131
+ /**
132
+ * The IAM roles associated with a resource. Supports resources with many roles assigned to varying lifecycles
133
+ */
134
+ export interface IAM {
135
+ roles: Role[];
136
+ }
137
+
138
+ /**
139
+ * The audit details of any existing resource where available - tracks who created the resource and when.
140
+ */
141
+ export interface Audit {
142
+ createdAt?: string | Date;
143
+ createdBy?: string;
144
+ }
145
+
146
+ /**
147
+ * The domain names that a resource can be accessed at
148
+ */
149
+ export interface DNS {
150
+ /**
151
+ * List of domain names at which a resource is accessible
152
+ */
153
+ domains: string[];
154
+ }
155
+
156
+ export type PublicIpStatus = "enabled" | "disabled";
157
+
158
+ /**
159
+ * The network information for a scanned resource which aims to cover both resources being assigned an address
160
+ * and the networking resource itself (VPC, subnet)
161
+ */
162
+ export interface Network {
163
+ /**
164
+ * Information about the public IP of the resource
165
+ */
166
+ publicIp?: {
167
+ status?: PublicIpStatus;
168
+ address?: string;
169
+ };
170
+ /**
171
+ * List of security group IDs that are associated with the resource
172
+ */
173
+ securityGroups?: string[];
174
+ /**
175
+ * The subnets that the resource is deployed into
176
+ */
177
+ targetSubnets?: string[];
178
+ /**
179
+ * The IPs that have been assigned to the resource within the private network.
180
+ */
181
+ assignedAddresses?: {
182
+ ipv4?: string;
183
+ ipv6?: string;
184
+ };
185
+ /**
186
+ * The IP ranges reserved for a networking resource (VPC, subnet)
187
+ */
188
+ reservedAddresses?: [
189
+ {
190
+ family: "ipv4" | "ipv6";
191
+ cidrBlock: string;
192
+ cidrBlockAssociationSets?: [
193
+ {
194
+ associationId: string;
195
+ cidrBlock: string;
196
+ state: string;
197
+ stateMessage: string;
198
+ },
199
+ ];
200
+ firstAddress: string;
201
+ lastAddress: string;
202
+ },
203
+ ];
204
+ /**
205
+ * The information relating to a specific subnet
206
+ */
207
+ subnet?: {
208
+ ipv6Only?: boolean;
209
+ availableAddressCount?: number;
210
+ };
211
+ /**
212
+ * The information relating to a specific VPC.
213
+ */
214
+ vpc?: {
215
+ id?: string;
216
+ dhcpOptionsId?: string;
217
+ state?: string;
218
+ instanceTenancy?: string;
219
+ default?: boolean;
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Generic structure for tracking key value datatypes
225
+ */
226
+ export interface KVPair {
227
+ key?: string;
228
+ value?: string;
229
+ }
230
+
231
+ /**
232
+ * Structure for tracking the source of state that was scanned
233
+ */
234
+ export interface Source<T> {
235
+ command: string;
236
+ parameters?: T;
237
+ }
238
+
239
+ /**
240
+ * Structure for representing the relationship between resources and the load balancers that route to them
241
+ */
242
+ export interface LoadBalancer {
243
+ name?: string;
244
+ port?: number;
245
+ targetGroupArn?: string;
246
+ }
247
+
248
+ export type SizeUnit = "B" | "MB" | "MiB" | "GB" | "GiB";
249
+
250
+ export type TimeUnit = "s" | "ms" | "m" | "h" | "d";
251
+
252
+ export interface QualifiedMeasure<T> {
253
+ unit: T;
254
+ value: number;
255
+ }
256
+
257
+ export interface HealthcheckConfig {
258
+ status?: string;
259
+ gracePeriod?: QualifiedMeasure<TimeUnit>;
260
+ }
261
+
262
+ /**
263
+ * Structure for tracking resources which have an associated encryption key
264
+ */
265
+ export interface EncryptionConfig {
266
+ keyId?: string;
267
+ }
268
+
269
+ export interface DeadLetterConfig {
270
+ targetId?: string;
271
+ }
272
+
273
+ /**
274
+ * Base state type to be extended on a per-service basis with the service specific context to use for linking.
275
+ * Attribute specific information can be found on each type.
276
+ */
277
+ export interface BaseState<T = unknown> {
278
+ $metadata: StateMetadata;
279
+ $graph: GraphInfo;
280
+ $source?: Source<T>;
281
+ deadLetter?: DeadLetterConfig;
282
+ tenant: Tenant;
283
+ location?: Location;
284
+ loadBalancers?: LoadBalancer[];
285
+ resource: Resource;
286
+ dns?: DNS;
287
+ iam?: IAM;
288
+ healthcheck?: HealthcheckConfig;
289
+ network?: Network;
290
+ audit?: Audit;
291
+ tags?: KVPair[];
292
+ encryption?: EncryptionConfig;
293
+ }
294
+
295
+ /**
296
+ * Generic type signature for any ode responsible for translating between data types
297
+ */
298
+ type Translate<A, B> = (val: A) => B;
299
+
300
+ /**
301
+ * Definition of a an entity's translators to convert the raw input state into the common schema
302
+ */
303
+ export type ComponentFactory<Input, Schema = BaseState<unknown>> = {
304
+ [ComponentName in keyof Schema]: Translate<Input, Schema[ComponentName]>;
9
305
  };
10
306
 
11
- /* eslint-disable @typescript-eslint/no-explicit-any */
12
- export type GenericState = State<any>;
307
+ export interface CommonEntity<Schema, RawState, TranslatedState = RawState> {
308
+ version: string;
309
+ selector: string;
310
+ debugLabel: string;
311
+ provider: string;
312
+ command: string;
313
+ category: string;
314
+ subcategory: string;
315
+ nodeType: string;
316
+
317
+ getState: (state: Connector, context: AwsContext) => Promise<RawState[]>;
318
+ components: ComponentFactory<TranslatedState, Schema>;
319
+ }
320
+
321
+ /**
322
+ * Entity which can act directly on its raw state, and does not require any pre-processing before
323
+ */
324
+ export interface SimpleEntity<Schema, RawState>
325
+ extends CommonEntity<Schema, RawState> {
326
+ translate?: never;
327
+ }
328
+
329
+ /**
330
+ * Entity which needs to translate its stored raw state before it can produce the standard schema
331
+ */
332
+ export interface TranslatedEntity<Schema, RawState, TranslatedState>
333
+ extends CommonEntity<Schema, RawState, TranslatedState> {
334
+ translate: Translate<RawState, TranslatedState[]>;
335
+ }
336
+
337
+ /**
338
+ * Discriminated union type for both simple and translated entities
339
+ */
340
+ export type EntityETL<Schema, RawState, TranslatedState = RawState> =
341
+ | SimpleEntity<Schema, RawState>
342
+ | TranslatedEntity<Schema, RawState, TranslatedState>;
343
+
344
+ export type WithCallContext<T, Input> = T & {
345
+ $metadata: CommandCallMetadata;
346
+ $parameters?: Input;
347
+ };