1 | |
2 |
|
3 | |
4 | * List Vulnerability Findings |
5 | * List vulnerability findings (CVEs detected on cloud resources), with optional filters by severity, status, CVE, exploit and fix availability. |
6 | */ |
7 | export async function main( |
8 | auth: RT.Wiz, |
9 | vendor_severity: |
10 | | ("NONE" | "LOW" | "MEDIUM" | "HIGH" | "CRITICAL")[] |
11 | | undefined, |
12 | status: ("UNRESOLVED" | "RESOLVED" | "PASSED" | "IGNORED")[] | undefined, |
13 | cve: string | undefined, |
14 | has_exploit: boolean | undefined, |
15 | has_fix: boolean | undefined, |
16 | first: number | undefined, |
17 | after: string | undefined |
18 | ) { |
19 | const tokenResponse = await fetch( |
20 | auth.auth_url || "https://auth.app.wiz.io/oauth/token", |
21 | { |
22 | method: "POST", |
23 | headers: { "Content-Type": "application/x-www-form-urlencoded" }, |
24 | body: new URLSearchParams({ |
25 | grant_type: "client_credentials", |
26 | audience: auth.audience || "wiz-api", |
27 | client_id: auth.client_id, |
28 | client_secret: auth.client_secret, |
29 | }), |
30 | } |
31 | ) |
32 | if (!tokenResponse.ok) { |
33 | throw new Error(`${tokenResponse.status} ${await tokenResponse.text()}`) |
34 | } |
35 | const { access_token } = (await tokenResponse.json()) as { |
36 | access_token: string |
37 | } |
38 |
|
39 | const filterBy: { [key: string]: any } = {} |
40 | if (vendor_severity && vendor_severity.length > 0) |
41 | filterBy.vendorSeverity = vendor_severity |
42 | if (status && status.length > 0) filterBy.status = status |
43 | if (cve !== undefined && cve !== "") filterBy.vulnerabilityExternalId = [cve] |
44 | if (has_exploit !== undefined) filterBy.hasExploit = has_exploit |
45 | if (has_fix !== undefined) filterBy.hasFix = has_fix |
46 |
|
47 | const query = ` |
48 | query ListVulnerabilityFindings($first: Int, $after: String, $filterBy: VulnerabilityFindingFilters) { |
49 | vulnerabilityFindings(first: $first, after: $after, filterBy: $filterBy) { |
50 | totalCount |
51 | pageInfo { hasNextPage endCursor } |
52 | nodes { |
53 | id |
54 | name |
55 | detailedName |
56 | severity: vendorSeverity |
57 | CVSSSeverity |
58 | score |
59 | exploitabilityScore |
60 | impactScore |
61 | hasExploit |
62 | hasCisaKevExploit |
63 | status |
64 | vulnerabilityExternalId |
65 | version |
66 | fixedVersion |
67 | detectionMethod |
68 | firstDetectedAt |
69 | lastDetectedAt |
70 | resolvedAt |
71 | resolutionReason |
72 | remediation |
73 | link |
74 | portalUrl |
75 | vulnerableAsset { |
76 | ... on VulnerableAssetBase { |
77 | id |
78 | type |
79 | name |
80 | cloudPlatform |
81 | subscriptionId |
82 | tags |
83 | } |
84 | ... on VulnerableAssetVirtualMachine { |
85 | id |
86 | type |
87 | name |
88 | cloudPlatform |
89 | subscriptionId |
90 | tags |
91 | operatingSystem |
92 | } |
93 | ... on VulnerableAssetServerless { |
94 | id |
95 | type |
96 | name |
97 | cloudPlatform |
98 | subscriptionId |
99 | tags |
100 | } |
101 | ... on VulnerableAssetContainerImage { |
102 | id |
103 | type |
104 | name |
105 | cloudPlatform |
106 | subscriptionId |
107 | tags |
108 | } |
109 | ... on VulnerableAssetContainer { |
110 | id |
111 | type |
112 | name |
113 | cloudPlatform |
114 | subscriptionId |
115 | tags |
116 | } |
117 | } |
118 | } |
119 | } |
120 | }` |
121 |
|
122 | const response = await fetch(auth.api_endpoint, { |
123 | method: "POST", |
124 | headers: { |
125 | Authorization: `Bearer ${access_token}`, |
126 | "Content-Type": "application/json", |
127 | Accept: "application/json", |
128 | }, |
129 | body: JSON.stringify({ |
130 | query, |
131 | variables: { first: first ?? 50, after: after || null, filterBy }, |
132 | }), |
133 | }) |
134 |
|
135 | if (!response.ok) { |
136 | throw new Error(`${response.status} ${await response.text()}`) |
137 | } |
138 |
|
139 | const result = (await response.json()) as { data?: any; errors?: any } |
140 | if (result.errors) { |
141 | throw new Error(JSON.stringify(result.errors)) |
142 | } |
143 | return result.data.vulnerabilityFindings |
144 | } |
145 |
|