@rawdash/connector-wiz 0.27.0 → 0.28.2

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/wiz.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(res: Response, parseJson: boolean): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n paginateChunked,\n schemasFromResources,\n selectActivePhases,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\nexport const configFields = defineConfigFields(\n z.object({\n apiEndpoint: z\n .string()\n .url()\n .regex(\n /^https:\\/\\/api\\.[a-z0-9-]+\\.app\\.wiz\\.io\\/graphql$/i,\n 'Wiz GraphQL endpoint, e.g. \"https://api.us1.app.wiz.io/graphql\".',\n )\n .meta({\n label: 'GraphQL API endpoint',\n description:\n 'Tenant-specific Wiz GraphQL endpoint shown on the Wiz service-account page (e.g. \"https://api.us1.app.wiz.io/graphql\"). The region segment changes per data residency.',\n placeholder: 'https://api.us1.app.wiz.io/graphql',\n }),\n clientId: z.string().min(1).meta({\n label: 'Service-account client ID',\n description:\n 'Client ID of the Wiz service account authorized for the API.',\n placeholder: 'aaaa-bbbb-cccc-dddd',\n }),\n clientSecret: z.object({ $secret: z.string().min(1) }).meta({\n label: 'Service-account client secret',\n description:\n 'Client secret of the Wiz service account. Stored as a secret.',\n placeholder: 'WIZ_CLIENT_SECRET',\n secret: true,\n }),\n tokenEndpoint: z.string().url().optional().meta({\n label: 'OAuth token endpoint (optional)',\n description:\n 'Override the OAuth 2.0 token endpoint. Defaults to https://auth.app.wiz.io/oauth/token; use the gov / fed equivalent for non-commercial deployments.',\n placeholder: 'https://auth.app.wiz.io/oauth/token',\n }),\n audience: z.string().min(1).optional().meta({\n label: 'OAuth audience (optional)',\n description:\n 'OAuth audience claim requested when minting the access token. Defaults to \"wiz-api\"; some legacy tenants require \"beyond-api\".',\n placeholder: 'wiz-api',\n }),\n resources: z\n .array(z.enum(['issues', 'issue_events', 'vulnerabilities']))\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n 'Which Wiz resources to sync. Omit to sync all of them. The issues and issue_events resources share the same underlying GraphQL query.',\n }),\n }),\n);\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'Wiz',\n category: 'security',\n brandColor: '#11253E',\n tagline:\n 'Sync cloud-security issues, issue lifecycle events, and vulnerability findings from a Wiz tenant for open-critical, MTTR, and posture dashboards.',\n vendor: {\n name: 'Wiz',\n domain: 'wiz.io',\n apiDocs: 'https://win.wiz.io/reference/welcome',\n website: 'https://wiz.io',\n },\n auth: {\n summary:\n 'OAuth 2.0 client-credentials flow against a Wiz service account. The connector mints an access token, refreshes it on expiry, and sends it as a Bearer header on every GraphQL request.',\n setup: [\n 'In the Wiz portal, open Settings -> Service Accounts and create a new service account.',\n 'Grant it the read scopes for the resources you intend to sync (typically read:issues and read:vulnerabilities).',\n 'Copy the Client ID, Client Secret, and Token Endpoint shown on the service-account page.',\n 'Copy the GraphQL API endpoint shown on the same page (e.g. \"https://api.us1.app.wiz.io/graphql\"); the region segment is tenant-specific.',\n 'Store the client secret as a rawdash secret and reference it from the connector config as `clientSecret: secret(\"WIZ_CLIENT_SECRET\")`.',\n ],\n },\n limitations: [\n \"Issue lifecycle events are derived from each issue's createdAt / resolvedAt timestamps, not from a dedicated audit-log endpoint, so administrative reopen / re-resolve transitions inside the same sync window are collapsed to the latest state.\",\n 'Service-account auth only; per-user OAuth is out of scope.',\n 'Cloud-configuration and threat-detection issues are returned by the same /issues query and are not segmented at the connector layer; filter on the `issueType` attribute downstream.',\n ],\n});\n\nexport type WizResource = 'issues' | 'issue_events' | 'vulnerabilities';\n\nexport interface WizSettings {\n apiEndpoint: string;\n tokenEndpoint?: string;\n audience?: string;\n resources?: readonly WizResource[];\n}\n\nconst wizCredentials = {\n clientId: {\n description: 'Wiz service-account client ID',\n auth: 'required' as const,\n },\n clientSecret: {\n description: 'Wiz service-account client secret',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype WizCredentials = typeof wizCredentials;\n\nconst PHASE_ORDER = ['issues', 'vulnerabilities'] as const;\n\ntype WizPhase = (typeof PHASE_ORDER)[number];\n\ntype WizSyncCursor = ChunkedSyncCursor<WizPhase, string>;\n\nconst isWizSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\nconst ISSUE_ENTITY = 'wiz_issue';\nconst ISSUE_EVENT = 'wiz_issue_event';\nconst VULNERABILITY_ENTITY = 'wiz_vulnerability';\n\nconst DEFAULT_TOKEN_ENDPOINT = 'https://auth.app.wiz.io/oauth/token';\nconst DEFAULT_AUDIENCE = 'wiz-api';\nconst PAGE_SIZE = 100;\nconst TOKEN_EXPIRY_GRACE_S = 60;\n\nconst SEVERITIES = [\n 'CRITICAL',\n 'HIGH',\n 'MEDIUM',\n 'LOW',\n 'INFORMATIONAL',\n] as const;\ntype Severity = (typeof SEVERITIES)[number];\n\nconst ISSUE_STATUSES = ['OPEN', 'IN_PROGRESS', 'RESOLVED', 'REJECTED'] as const;\ntype IssueStatus = (typeof ISSUE_STATUSES)[number];\n\nconst VULN_STATUSES = ['OPEN', 'RESOLVED', 'IGNORED', 'IN_PROGRESS'] as const;\ntype VulnStatus = (typeof VULN_STATUSES)[number];\n\nconst ISSUE_EVENT_KINDS = ['opened', 'resolved'] as const;\ntype IssueEventKind = (typeof ISSUE_EVENT_KINDS)[number];\n\nconst idString = z.string().min(1);\nconst isoString = z.string().min(1);\n\nconst oauthTokenSchema = z.object({\n access_token: z.string().min(1),\n token_type: z.string().optional(),\n expires_in: z.number().optional(),\n});\n\nconst entitySnapshotSchema = z.object({\n id: idString.nullable().optional(),\n name: z.string().nullable().optional(),\n type: z.string().nullable().optional(),\n cloudProvider: z.string().nullable().optional(),\n externalId: z.string().nullable().optional(),\n});\n\nconst sourceRuleSchema = z.object({\n id: idString.nullable().optional(),\n name: z.string().nullable().optional(),\n});\n\nconst issueSchema = z.object({\n id: idString,\n severity: z.string(),\n status: z.string(),\n type: z.string().nullable().optional(),\n resolutionReason: z.string().nullable().optional(),\n createdAt: isoString,\n updatedAt: isoString.nullable().optional(),\n resolvedAt: isoString.nullable().optional(),\n dueAt: isoString.nullable().optional(),\n sourceRule: sourceRuleSchema.nullable().optional(),\n entitySnapshot: entitySnapshotSchema.nullable().optional(),\n});\n\ninterface PageInfo {\n hasNextPage: boolean;\n endCursor: string | null;\n}\n\nconst issuesArraySchema = z.array(issueSchema);\n\ninterface IssuesResponse {\n data?: {\n issues?: {\n nodes?: z.infer<typeof issueSchema>[];\n pageInfo?: PageInfo;\n };\n };\n errors?: Array<{ message: string }>;\n}\n\nconst vulnerableAssetSchema = z.object({\n id: idString.nullable().optional(),\n name: z.string().nullable().optional(),\n type: z.string().nullable().optional(),\n cloudPlatform: z.string().nullable().optional(),\n});\n\nconst vulnerabilitySchema = z.object({\n id: idString,\n name: z.string().nullable().optional(),\n severity: z.string(),\n status: z.string(),\n vulnerabilityExternalId: z.string().nullable().optional(),\n firstDetectedAt: isoString.nullable().optional(),\n lastDetectedAt: isoString.nullable().optional(),\n resolvedAt: isoString.nullable().optional(),\n vulnerableAsset: vulnerableAssetSchema.nullable().optional(),\n});\n\nconst vulnerabilitiesArraySchema = z.array(vulnerabilitySchema);\n\ninterface VulnerabilitiesResponse {\n data?: {\n vulnerabilityFindings?: {\n nodes?: z.infer<typeof vulnerabilitySchema>[];\n pageInfo?: PageInfo;\n };\n };\n errors?: Array<{ message: string }>;\n}\n\nexport const wizResources = defineResources({\n [ISSUE_ENTITY]: {\n shape: 'entity',\n filterable: [\n {\n field: 'severity',\n ops: ['eq'],\n values: [...SEVERITIES],\n },\n {\n field: 'status',\n ops: ['eq'],\n values: [...ISSUE_STATUSES],\n },\n { field: 'cloudProvider', ops: ['eq'] },\n { field: 'resourceType', ops: ['eq'] },\n ],\n description:\n 'Wiz issues (cloud-configuration, toxic-combination, and threat-detection findings) keyed by issue id, with severity, status, the offending entity snapshot, and lifecycle timestamps.',\n endpoint: 'GraphQL query: issues { nodes { ... } }',\n notes:\n 'Paginated via the GraphQL connection cursor; incremental syncs filter on updatedAt.after and stop once a page is entirely older than options.since.',\n fields: [\n {\n name: 'severity',\n description: 'CRITICAL, HIGH, MEDIUM, LOW, or INFORMATIONAL.',\n },\n { name: 'status', description: 'OPEN, IN_PROGRESS, RESOLVED, REJECTED.' },\n {\n name: 'issueType',\n description:\n 'Issue category (e.g. CLOUD_CONFIGURATION, TOXIC_COMBINATION).',\n },\n {\n name: 'ruleName',\n description: 'Name of the source rule that produced the issue.',\n },\n {\n name: 'resourceName',\n description: 'Name of the cloud resource the issue applies to.',\n },\n {\n name: 'resourceType',\n description:\n 'Type of the cloud resource (e.g. EC2_INSTANCE, S3_BUCKET).',\n },\n {\n name: 'cloudProvider',\n description: 'AWS, GCP, AZURE, etc.',\n },\n {\n name: 'createdAt',\n description: 'When Wiz first opened the issue (Unix ms).',\n },\n {\n name: 'resolvedAt',\n description: 'When the issue was resolved (Unix ms; null if open).',\n },\n {\n name: 'dueAt',\n description: 'Remediation due date as configured by SLA (Unix ms).',\n },\n ],\n responses: {\n oauth_token: oauthTokenSchema,\n issues: issuesArraySchema,\n },\n },\n [ISSUE_EVENT]: {\n shape: 'event',\n filterable: [\n {\n field: 'kind',\n ops: ['eq'],\n values: [...ISSUE_EVENT_KINDS],\n },\n {\n field: 'severity',\n ops: ['eq'],\n values: [...SEVERITIES],\n },\n ],\n description:\n 'Issue lifecycle events derived from each Wiz issue: one event at createdAt (kind=\"opened\") and, when present, one at resolvedAt (kind=\"resolved\"). Used to build open-rate, resolution-rate, and MTTR widgets.',\n endpoint: 'GraphQL query: issues { nodes { ... } } (derived)',\n notes:\n 'Events are derived from the same issues GraphQL query; enabling issue_events without issues still triggers the query but skips the entity write.',\n fields: [\n {\n name: 'kind',\n description: '\"opened\" or \"resolved\".',\n },\n {\n name: 'issueId',\n description: 'The Wiz issue id this lifecycle event belongs to.',\n },\n {\n name: 'severity',\n description: 'Severity of the originating issue at sync time.',\n },\n {\n name: 'cloudProvider',\n description: 'Cloud provider of the affected resource.',\n },\n ],\n },\n [VULNERABILITY_ENTITY]: {\n shape: 'entity',\n filterable: [\n {\n field: 'severity',\n ops: ['eq'],\n values: [...SEVERITIES],\n },\n {\n field: 'status',\n ops: ['eq'],\n values: [...VULN_STATUSES],\n },\n { field: 'cloudPlatform', ops: ['eq'] },\n ],\n description:\n 'Wiz vulnerability findings keyed by finding id, with CVE id, severity, status, first / last detection timestamps, and the affected asset.',\n endpoint: 'GraphQL query: vulnerabilityFindings { nodes { ... } }',\n notes:\n 'Paginated via the GraphQL connection cursor; incremental syncs filter on lastDetectedAt.after.',\n fields: [\n {\n name: 'severity',\n description: 'CRITICAL, HIGH, MEDIUM, LOW, or INFORMATIONAL.',\n },\n { name: 'status', description: 'OPEN, RESOLVED, IGNORED, IN_PROGRESS.' },\n {\n name: 'name',\n description: 'Vulnerability name as reported by Wiz.',\n },\n {\n name: 'cve',\n description: 'Vulnerability external id, typically a CVE identifier.',\n },\n {\n name: 'assetName',\n description: 'Name of the affected asset.',\n },\n {\n name: 'assetType',\n description: 'Type of the affected asset.',\n },\n {\n name: 'cloudPlatform',\n description: 'Cloud platform hosting the affected asset.',\n },\n {\n name: 'firstDetectedAt',\n description: 'When the vulnerability was first detected (Unix ms).',\n },\n {\n name: 'lastDetectedAt',\n description: 'When the vulnerability was last detected (Unix ms).',\n },\n {\n name: 'resolvedAt',\n description:\n 'When the vulnerability was resolved (Unix ms; null if open).',\n },\n ],\n responses: {\n vulnerabilities: vulnerabilitiesArraySchema,\n },\n },\n});\n\nexport const id = 'wiz';\n\ntype OauthTokenResponse = z.infer<typeof oauthTokenSchema>;\ntype WizIssue = z.infer<typeof issueSchema>;\ntype WizVulnerability = z.infer<typeof vulnerabilitySchema>;\n\nconst ISSUES_QUERY = `\n query Issues($first: Int!, $after: String, $filterBy: IssueFilters, $orderBy: IssueOrder) {\n issues(first: $first, after: $after, filterBy: $filterBy, orderBy: $orderBy) {\n nodes {\n id\n severity\n status\n type\n resolutionReason\n createdAt\n updatedAt\n resolvedAt\n dueAt\n sourceRule { id name }\n entitySnapshot { id name type cloudProvider externalId }\n }\n pageInfo { hasNextPage endCursor }\n }\n }\n`;\n\nconst VULNERABILITIES_QUERY = `\n query VulnerabilityFindings(\n $first: Int!\n $after: String\n $filterBy: VulnerabilityFindingFilters\n ) {\n vulnerabilityFindings(first: $first, after: $after, filterBy: $filterBy) {\n nodes {\n id\n name\n severity\n status\n vulnerabilityExternalId\n firstDetectedAt\n lastDetectedAt\n resolvedAt\n vulnerableAsset { id name type cloudPlatform }\n }\n pageInfo { hasNextPage endCursor }\n }\n }\n`;\n\nfunction parseIsoMs(value: string | null | undefined): number | null {\n if (!value) {\n return null;\n }\n const ms = Date.parse(value);\n return Number.isFinite(ms) ? ms : null;\n}\n\nfunction normalizeSeverity(value: string): Severity | string {\n const upper = value.toUpperCase();\n return (SEVERITIES as readonly string[]).includes(upper) ? upper : value;\n}\n\nfunction normalizeIssueStatus(value: string): IssueStatus | string {\n const upper = value.toUpperCase();\n return (ISSUE_STATUSES as readonly string[]).includes(upper) ? upper : value;\n}\n\nfunction normalizeVulnStatus(value: string): VulnStatus | string {\n const upper = value.toUpperCase();\n return (VULN_STATUSES as readonly string[]).includes(upper) ? upper : value;\n}\n\nexport class WizConnector extends BaseConnector<WizSettings, WizCredentials> {\n static readonly id = id;\n\n static readonly resources = wizResources;\n\n static readonly schemas = schemasFromResources(wizResources);\n\n static create(input: unknown, ctx?: ConnectorContext): WizConnector {\n const parsed = configFields.parse(input);\n return new WizConnector(\n {\n apiEndpoint: parsed.apiEndpoint,\n tokenEndpoint: parsed.tokenEndpoint,\n audience: parsed.audience,\n resources: parsed.resources,\n },\n {\n clientId: parsed.clientId,\n clientSecret: parsed.clientSecret,\n },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = wizCredentials;\n\n private accessToken: string | null = null;\n private accessTokenExpiry = 0;\n\n private tokenEndpoint(): string {\n return this.settings.tokenEndpoint ?? DEFAULT_TOKEN_ENDPOINT;\n }\n\n private audience(): string {\n return this.settings.audience ?? DEFAULT_AUDIENCE;\n }\n\n private resourceAllowed(resource: WizResource): boolean {\n const enabled = this.settings.resources;\n if (!enabled || enabled.length === 0) {\n return true;\n }\n return enabled.includes(resource);\n }\n\n private async refreshAccessToken(signal?: AbortSignal): Promise<string> {\n const form = new URLSearchParams({\n grant_type: 'client_credentials',\n audience: this.audience(),\n client_id: this.creds.clientId,\n client_secret: this.creds.clientSecret,\n });\n const res = await this.post<OauthTokenResponse>(this.tokenEndpoint(), {\n resource: 'oauth_token',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n 'User-Agent': connectorUserAgent('wiz'),\n },\n body: form.toString(),\n signal,\n });\n const token = res.body.access_token;\n const expiresIn = res.body.expires_in ?? 3600;\n this.accessToken = token;\n this.accessTokenExpiry =\n Date.now() + Math.max(0, expiresIn - TOKEN_EXPIRY_GRACE_S) * 1000;\n return token;\n }\n\n private async getAccessToken(signal?: AbortSignal): Promise<string> {\n if (!this.accessToken || Date.now() >= this.accessTokenExpiry) {\n return this.refreshAccessToken(signal);\n }\n return this.accessToken;\n }\n\n private async graphql<T>(\n query: string,\n variables: Record<string, unknown>,\n resource: string,\n signal?: AbortSignal,\n retried = false,\n ): Promise<HttpResponse<T>> {\n const token = await this.getAccessToken(signal);\n const res = await this.post<T>(this.settings.apiEndpoint, {\n resource,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': connectorUserAgent('wiz'),\n },\n body: JSON.stringify({ query, variables }),\n signal,\n });\n if (res.status === 401 && !retried) {\n this.accessToken = null;\n this.accessTokenExpiry = 0;\n return this.graphql<T>(query, variables, resource, signal, true);\n }\n const body = res.body as {\n data?: unknown;\n errors?: Array<{ message: string }>;\n };\n if (body.errors && body.errors.length > 0) {\n const messages = body.errors.map((e) => e.message).join('; ');\n throw new Error(`Wiz GraphQL error: ${messages}`);\n }\n if (!body.data) {\n throw new Error(\n `Wiz GraphQL response missing data for resource '${resource}'`,\n );\n }\n return res;\n }\n\n private issuesFilter(\n options: SyncOptions,\n ): Record<string, unknown> | undefined {\n if (!options.since) {\n return undefined;\n }\n return { updatedAt: { after: options.since } };\n }\n\n private vulnerabilitiesFilter(\n options: SyncOptions,\n ): Record<string, unknown> | undefined {\n if (!options.since) {\n return undefined;\n }\n return { lastDetectedAt: { after: options.since } };\n }\n\n private isPageAllOlderThan(\n items: WizIssue[],\n sinceMs: number | null,\n ): boolean {\n if (sinceMs === null || items.length === 0) {\n return false;\n }\n for (const i of items) {\n const ts = parseIsoMs(i.updatedAt ?? i.createdAt);\n if (ts !== null && ts >= sinceMs) {\n return false;\n }\n }\n return true;\n }\n\n private async fetchIssuesPage(\n page: string | null,\n options: SyncOptions,\n sinceMs: number | null,\n signal: AbortSignal | undefined,\n ): Promise<{ items: WizIssue[]; next: string | null }> {\n const res = await this.graphql<IssuesResponse>(\n ISSUES_QUERY,\n {\n first: PAGE_SIZE,\n after: page,\n filterBy: this.issuesFilter(options),\n orderBy: { field: 'UPDATED_AT', direction: 'DESC' },\n },\n 'issues',\n signal,\n );\n const conn = res.body.data?.issues;\n if (!conn?.nodes || !conn.pageInfo) {\n throw new Error(\"Wiz GraphQL response missing 'issues' connection\");\n }\n const nodes = conn.nodes;\n const next = conn.pageInfo.hasNextPage ? conn.pageInfo.endCursor : null;\n if (this.isPageAllOlderThan(nodes, sinceMs)) {\n return { items: nodes, next: null };\n }\n return { items: nodes, next };\n }\n\n private async fetchVulnerabilitiesPage(\n page: string | null,\n options: SyncOptions,\n signal: AbortSignal | undefined,\n ): Promise<{ items: WizVulnerability[]; next: string | null }> {\n const res = await this.graphql<VulnerabilitiesResponse>(\n VULNERABILITIES_QUERY,\n {\n first: PAGE_SIZE,\n after: page,\n filterBy: this.vulnerabilitiesFilter(options),\n },\n 'vulnerabilities',\n signal,\n );\n const conn = res.body.data?.vulnerabilityFindings;\n if (!conn?.nodes || !conn.pageInfo) {\n throw new Error(\n \"Wiz GraphQL response missing 'vulnerabilityFindings' connection\",\n );\n }\n const nodes = conn.nodes;\n const next = conn.pageInfo.hasNextPage ? conn.pageInfo.endCursor : null;\n return { items: nodes, next };\n }\n\n private async writeIssues(\n storage: StorageHandle,\n items: WizIssue[],\n sinceMs: number | null,\n ): Promise<void> {\n const writeEntity = this.resourceAllowed('issues');\n const writeEvent = this.resourceAllowed('issue_events');\n\n for (const issue of items) {\n const severity = normalizeSeverity(issue.severity);\n const status = normalizeIssueStatus(issue.status);\n const createdMs = parseIsoMs(issue.createdAt);\n const updatedMs = parseIsoMs(issue.updatedAt ?? null) ?? createdMs;\n const resolvedMs = parseIsoMs(issue.resolvedAt ?? null);\n const dueMs = parseIsoMs(issue.dueAt ?? null);\n const snap = issue.entitySnapshot ?? null;\n const cloudProvider = snap?.cloudProvider ?? null;\n const resourceType = snap?.type ?? null;\n const resourceName = snap?.name ?? null;\n const resourceExternalId = snap?.externalId ?? null;\n const ruleId = issue.sourceRule?.id ?? null;\n const ruleName = issue.sourceRule?.name ?? null;\n\n if (writeEntity) {\n await storage.entity({\n type: ISSUE_ENTITY,\n id: issue.id,\n attributes: {\n severity,\n status,\n issueType: issue.type ?? null,\n resolutionReason: issue.resolutionReason ?? null,\n ruleId,\n ruleName,\n resourceId: snap?.id ?? null,\n resourceName,\n resourceType,\n resourceExternalId,\n cloudProvider,\n createdAt: createdMs,\n resolvedAt: resolvedMs,\n dueAt: dueMs,\n },\n updated_at: updatedMs ?? createdMs ?? 0,\n });\n }\n\n if (writeEvent) {\n if (createdMs !== null && (sinceMs === null || createdMs >= sinceMs)) {\n await storage.event({\n name: ISSUE_EVENT,\n start_ts: createdMs,\n end_ts: null,\n attributes: {\n kind: 'opened' satisfies IssueEventKind,\n issueId: issue.id,\n severity,\n status,\n cloudProvider,\n resourceType,\n ruleName,\n },\n });\n }\n if (\n resolvedMs !== null &&\n (sinceMs === null || resolvedMs >= sinceMs)\n ) {\n await storage.event({\n name: ISSUE_EVENT,\n start_ts: resolvedMs,\n end_ts: null,\n attributes: {\n kind: 'resolved' satisfies IssueEventKind,\n issueId: issue.id,\n severity,\n status,\n cloudProvider,\n resourceType,\n ruleName,\n },\n });\n }\n }\n }\n }\n\n private async writeVulnerabilities(\n storage: StorageHandle,\n items: WizVulnerability[],\n ): Promise<void> {\n for (const v of items) {\n const severity = normalizeSeverity(v.severity);\n const status = normalizeVulnStatus(v.status);\n const firstMs = parseIsoMs(v.firstDetectedAt ?? null);\n const lastMs = parseIsoMs(v.lastDetectedAt ?? null) ?? firstMs;\n const resolvedMs = parseIsoMs(v.resolvedAt ?? null);\n await storage.entity({\n type: VULNERABILITY_ENTITY,\n id: v.id,\n attributes: {\n name: v.name ?? null,\n severity,\n status,\n cve: v.vulnerabilityExternalId ?? null,\n assetId: v.vulnerableAsset?.id ?? null,\n assetName: v.vulnerableAsset?.name ?? null,\n assetType: v.vulnerableAsset?.type ?? null,\n cloudPlatform: v.vulnerableAsset?.cloudPlatform ?? null,\n firstDetectedAt: firstMs,\n lastDetectedAt: lastMs,\n resolvedAt: resolvedMs,\n },\n updated_at: lastMs ?? firstMs ?? 0,\n });\n }\n }\n\n private async clearScopeOnFirstPage(\n storage: StorageHandle,\n phase: WizPhase,\n isFull: boolean,\n ): Promise<void> {\n if (!isFull) {\n return;\n }\n switch (phase) {\n case 'issues':\n if (this.resourceAllowed('issues')) {\n await storage.entities([], { types: [ISSUE_ENTITY] });\n }\n if (this.resourceAllowed('issue_events')) {\n await storage.events([], { names: [ISSUE_EVENT] });\n }\n return;\n case 'vulnerabilities':\n await storage.entities([], { types: [VULNERABILITY_ENTITY] });\n return;\n }\n }\n\n private resolveCursor(cursor: unknown): WizSyncCursor | undefined {\n return isWizSyncCursor(cursor) ? cursor : undefined;\n }\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor = this.resolveCursor(options.cursor);\n const isFull = options.mode === 'full';\n const sinceMs = options.since ? Date.parse(options.since) : null;\n\n const phases = selectActivePhases<WizResource, WizPhase>(\n (r) => (r === 'vulnerabilities' ? 'vulnerabilities' : 'issues'),\n PHASE_ORDER,\n this.settings.resources,\n );\n\n return paginateChunked<WizPhase, string>({\n phases,\n cursor,\n signal,\n logger: this.logger,\n fetchPage: async (phase, page, sig) => {\n switch (phase) {\n case 'issues':\n return this.fetchIssuesPage(page, options, sinceMs, sig);\n case 'vulnerabilities':\n return this.fetchVulnerabilitiesPage(page, options, sig);\n }\n },\n writeBatch: async (phase, items, page) => {\n if (page === null) {\n await this.clearScopeOnFirstPage(storage, phase, isFull);\n }\n switch (phase) {\n case 'issues':\n await this.writeIssues(storage, items as WizIssue[], sinceMs);\n return;\n case 'vulnerabilities':\n await this.writeVulnerabilities(\n storage,\n items as WizVulnerability[],\n );\n return;\n }\n },\n });\n }\n}\n","import { WizConnector } from './wiz';\n\nexport {\n WizConnector,\n configFields,\n doc,\n id,\n wizResources as resources,\n} from './wiz';\nexport type { WizResource, WizSettings } from './wiz';\nexport default WizConnector;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/wiz.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(\n res: Response,\n parseJson: boolean,\n binary: boolean,\n): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n if (binary) {\n return new Uint8Array(await res.arrayBuffer());\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n const binary = req.binary ?? false;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson, binary);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n paginateChunked,\n schemasFromResources,\n selectActivePhases,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\nexport const configFields = defineConfigFields(\n z.object({\n apiEndpoint: z\n .string()\n .url()\n .regex(\n /^https:\\/\\/api\\.[a-z0-9-]+\\.app\\.wiz\\.io\\/graphql$/i,\n 'Wiz GraphQL endpoint, e.g. \"https://api.us1.app.wiz.io/graphql\".',\n )\n .meta({\n label: 'GraphQL API endpoint',\n description:\n 'Tenant-specific Wiz GraphQL endpoint shown on the Wiz service-account page (e.g. \"https://api.us1.app.wiz.io/graphql\"). The region segment changes per data residency.',\n placeholder: 'https://api.us1.app.wiz.io/graphql',\n }),\n clientId: z.string().min(1).meta({\n label: 'Service-account client ID',\n description:\n 'Client ID of the Wiz service account authorized for the API.',\n placeholder: 'aaaa-bbbb-cccc-dddd',\n }),\n clientSecret: z.object({ $secret: z.string().min(1) }).meta({\n label: 'Service-account client secret',\n description:\n 'Client secret of the Wiz service account. Stored as a secret.',\n placeholder: 'WIZ_CLIENT_SECRET',\n secret: true,\n }),\n tokenEndpoint: z.string().url().optional().meta({\n label: 'OAuth token endpoint (optional)',\n description:\n 'Override the OAuth 2.0 token endpoint. Defaults to https://auth.app.wiz.io/oauth/token; use the gov / fed equivalent for non-commercial deployments.',\n placeholder: 'https://auth.app.wiz.io/oauth/token',\n }),\n audience: z.string().min(1).optional().meta({\n label: 'OAuth audience (optional)',\n description:\n 'OAuth audience claim requested when minting the access token. Defaults to \"wiz-api\"; some legacy tenants require \"beyond-api\".',\n placeholder: 'wiz-api',\n }),\n resources: z\n .array(z.enum(['issues', 'issue_events', 'vulnerabilities']))\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n 'Which Wiz resources to sync. Omit to sync all of them. The issues and issue_events resources share the same underlying GraphQL query.',\n }),\n }),\n);\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'Wiz',\n category: 'security',\n brandColor: '#11253E',\n tagline:\n 'Sync cloud-security issues, issue lifecycle events, and vulnerability findings from a Wiz tenant for open-critical, MTTR, and posture dashboards.',\n vendor: {\n name: 'Wiz',\n domain: 'wiz.io',\n apiDocs: 'https://win.wiz.io/reference/welcome',\n website: 'https://wiz.io',\n },\n auth: {\n summary:\n 'OAuth 2.0 client-credentials flow against a Wiz service account. The connector mints an access token, refreshes it on expiry, and sends it as a Bearer header on every GraphQL request.',\n setup: [\n 'In the Wiz portal, open Settings -> Service Accounts and create a new service account.',\n 'Grant it the read scopes for the resources you intend to sync (typically read:issues and read:vulnerabilities).',\n 'Copy the Client ID, Client Secret, and Token Endpoint shown on the service-account page.',\n 'Copy the GraphQL API endpoint shown on the same page (e.g. \"https://api.us1.app.wiz.io/graphql\"); the region segment is tenant-specific.',\n 'Store the client secret as a rawdash secret and reference it from the connector config as `clientSecret: secret(\"WIZ_CLIENT_SECRET\")`.',\n ],\n },\n limitations: [\n \"Issue lifecycle events are derived from each issue's createdAt / resolvedAt timestamps, not from a dedicated audit-log endpoint, so administrative reopen / re-resolve transitions inside the same sync window are collapsed to the latest state.\",\n 'Service-account auth only; per-user OAuth is out of scope.',\n 'Cloud-configuration and threat-detection issues are returned by the same /issues query and are not segmented at the connector layer; filter on the `issueType` attribute downstream.',\n ],\n});\n\nexport type WizResource = 'issues' | 'issue_events' | 'vulnerabilities';\n\nexport interface WizSettings {\n apiEndpoint: string;\n tokenEndpoint?: string;\n audience?: string;\n resources?: readonly WizResource[];\n}\n\nconst wizCredentials = {\n clientId: {\n description: 'Wiz service-account client ID',\n auth: 'required' as const,\n },\n clientSecret: {\n description: 'Wiz service-account client secret',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype WizCredentials = typeof wizCredentials;\n\nconst PHASE_ORDER = ['issues', 'vulnerabilities'] as const;\n\ntype WizPhase = (typeof PHASE_ORDER)[number];\n\ntype WizSyncCursor = ChunkedSyncCursor<WizPhase, string>;\n\nconst isWizSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\nconst ISSUE_ENTITY = 'wiz_issue';\nconst ISSUE_EVENT = 'wiz_issue_event';\nconst VULNERABILITY_ENTITY = 'wiz_vulnerability';\n\nconst DEFAULT_TOKEN_ENDPOINT = 'https://auth.app.wiz.io/oauth/token';\nconst DEFAULT_AUDIENCE = 'wiz-api';\nconst PAGE_SIZE = 100;\nconst TOKEN_EXPIRY_GRACE_S = 60;\n\nconst SEVERITIES = [\n 'CRITICAL',\n 'HIGH',\n 'MEDIUM',\n 'LOW',\n 'INFORMATIONAL',\n] as const;\ntype Severity = (typeof SEVERITIES)[number];\n\nconst ISSUE_STATUSES = ['OPEN', 'IN_PROGRESS', 'RESOLVED', 'REJECTED'] as const;\ntype IssueStatus = (typeof ISSUE_STATUSES)[number];\n\nconst VULN_STATUSES = ['OPEN', 'RESOLVED', 'IGNORED', 'IN_PROGRESS'] as const;\ntype VulnStatus = (typeof VULN_STATUSES)[number];\n\nconst ISSUE_EVENT_KINDS = ['opened', 'resolved'] as const;\ntype IssueEventKind = (typeof ISSUE_EVENT_KINDS)[number];\n\nconst idString = z.string().min(1);\nconst isoString = z.string().min(1);\n\nconst oauthTokenSchema = z.object({\n access_token: z.string().min(1),\n token_type: z.string().optional(),\n expires_in: z.number().optional(),\n});\n\nconst entitySnapshotSchema = z.object({\n id: idString.nullable().optional(),\n name: z.string().nullable().optional(),\n type: z.string().nullable().optional(),\n cloudProvider: z.string().nullable().optional(),\n externalId: z.string().nullable().optional(),\n});\n\nconst sourceRuleSchema = z.object({\n id: idString.nullable().optional(),\n name: z.string().nullable().optional(),\n});\n\nconst issueSchema = z.object({\n id: idString,\n severity: z.string(),\n status: z.string(),\n type: z.string().nullable().optional(),\n resolutionReason: z.string().nullable().optional(),\n createdAt: isoString,\n updatedAt: isoString.nullable().optional(),\n resolvedAt: isoString.nullable().optional(),\n dueAt: isoString.nullable().optional(),\n sourceRule: sourceRuleSchema.nullable().optional(),\n entitySnapshot: entitySnapshotSchema.nullable().optional(),\n});\n\ninterface PageInfo {\n hasNextPage: boolean;\n endCursor: string | null;\n}\n\nconst issuesArraySchema = z.array(issueSchema);\n\ninterface IssuesResponse {\n data?: {\n issues?: {\n nodes?: z.infer<typeof issueSchema>[];\n pageInfo?: PageInfo;\n };\n };\n errors?: Array<{ message: string }>;\n}\n\nconst vulnerableAssetSchema = z.object({\n id: idString.nullable().optional(),\n name: z.string().nullable().optional(),\n type: z.string().nullable().optional(),\n cloudPlatform: z.string().nullable().optional(),\n});\n\nconst vulnerabilitySchema = z.object({\n id: idString,\n name: z.string().nullable().optional(),\n severity: z.string(),\n status: z.string(),\n vulnerabilityExternalId: z.string().nullable().optional(),\n firstDetectedAt: isoString.nullable().optional(),\n lastDetectedAt: isoString.nullable().optional(),\n resolvedAt: isoString.nullable().optional(),\n vulnerableAsset: vulnerableAssetSchema.nullable().optional(),\n});\n\nconst vulnerabilitiesArraySchema = z.array(vulnerabilitySchema);\n\ninterface VulnerabilitiesResponse {\n data?: {\n vulnerabilityFindings?: {\n nodes?: z.infer<typeof vulnerabilitySchema>[];\n pageInfo?: PageInfo;\n };\n };\n errors?: Array<{ message: string }>;\n}\n\nexport const wizResources = defineResources({\n [ISSUE_ENTITY]: {\n shape: 'entity',\n filterable: [\n {\n field: 'severity',\n ops: ['eq'],\n values: [...SEVERITIES],\n },\n {\n field: 'status',\n ops: ['eq'],\n values: [...ISSUE_STATUSES],\n },\n { field: 'cloudProvider', ops: ['eq'] },\n { field: 'resourceType', ops: ['eq'] },\n ],\n description:\n 'Wiz issues (cloud-configuration, toxic-combination, and threat-detection findings) keyed by issue id, with severity, status, the offending entity snapshot, and lifecycle timestamps.',\n endpoint: 'GraphQL query: issues { nodes { ... } }',\n notes:\n 'Paginated via the GraphQL connection cursor; incremental syncs filter on updatedAt.after and stop once a page is entirely older than options.since.',\n fields: [\n {\n name: 'severity',\n description: 'CRITICAL, HIGH, MEDIUM, LOW, or INFORMATIONAL.',\n },\n { name: 'status', description: 'OPEN, IN_PROGRESS, RESOLVED, REJECTED.' },\n {\n name: 'issueType',\n description:\n 'Issue category (e.g. CLOUD_CONFIGURATION, TOXIC_COMBINATION).',\n },\n {\n name: 'ruleName',\n description: 'Name of the source rule that produced the issue.',\n },\n {\n name: 'resourceName',\n description: 'Name of the cloud resource the issue applies to.',\n },\n {\n name: 'resourceType',\n description:\n 'Type of the cloud resource (e.g. EC2_INSTANCE, S3_BUCKET).',\n },\n {\n name: 'cloudProvider',\n description: 'AWS, GCP, AZURE, etc.',\n },\n {\n name: 'createdAt',\n description: 'When Wiz first opened the issue (Unix ms).',\n },\n {\n name: 'resolvedAt',\n description: 'When the issue was resolved (Unix ms; null if open).',\n },\n {\n name: 'dueAt',\n description: 'Remediation due date as configured by SLA (Unix ms).',\n },\n ],\n responses: {\n oauth_token: oauthTokenSchema,\n issues: issuesArraySchema,\n },\n },\n [ISSUE_EVENT]: {\n shape: 'event',\n filterable: [\n {\n field: 'kind',\n ops: ['eq'],\n values: [...ISSUE_EVENT_KINDS],\n },\n {\n field: 'severity',\n ops: ['eq'],\n values: [...SEVERITIES],\n },\n ],\n description:\n 'Issue lifecycle events derived from each Wiz issue: one event at createdAt (kind=\"opened\") and, when present, one at resolvedAt (kind=\"resolved\"). Used to build open-rate, resolution-rate, and MTTR widgets.',\n endpoint: 'GraphQL query: issues { nodes { ... } } (derived)',\n notes:\n 'Events are derived from the same issues GraphQL query; enabling issue_events without issues still triggers the query but skips the entity write.',\n fields: [\n {\n name: 'kind',\n description: '\"opened\" or \"resolved\".',\n },\n {\n name: 'issueId',\n description: 'The Wiz issue id this lifecycle event belongs to.',\n },\n {\n name: 'severity',\n description: 'Severity of the originating issue at sync time.',\n },\n {\n name: 'cloudProvider',\n description: 'Cloud provider of the affected resource.',\n },\n ],\n },\n [VULNERABILITY_ENTITY]: {\n shape: 'entity',\n filterable: [\n {\n field: 'severity',\n ops: ['eq'],\n values: [...SEVERITIES],\n },\n {\n field: 'status',\n ops: ['eq'],\n values: [...VULN_STATUSES],\n },\n { field: 'cloudPlatform', ops: ['eq'] },\n ],\n description:\n 'Wiz vulnerability findings keyed by finding id, with CVE id, severity, status, first / last detection timestamps, and the affected asset.',\n endpoint: 'GraphQL query: vulnerabilityFindings { nodes { ... } }',\n notes:\n 'Paginated via the GraphQL connection cursor; incremental syncs filter on lastDetectedAt.after.',\n fields: [\n {\n name: 'severity',\n description: 'CRITICAL, HIGH, MEDIUM, LOW, or INFORMATIONAL.',\n },\n { name: 'status', description: 'OPEN, RESOLVED, IGNORED, IN_PROGRESS.' },\n {\n name: 'name',\n description: 'Vulnerability name as reported by Wiz.',\n },\n {\n name: 'cve',\n description: 'Vulnerability external id, typically a CVE identifier.',\n },\n {\n name: 'assetName',\n description: 'Name of the affected asset.',\n },\n {\n name: 'assetType',\n description: 'Type of the affected asset.',\n },\n {\n name: 'cloudPlatform',\n description: 'Cloud platform hosting the affected asset.',\n },\n {\n name: 'firstDetectedAt',\n description: 'When the vulnerability was first detected (Unix ms).',\n },\n {\n name: 'lastDetectedAt',\n description: 'When the vulnerability was last detected (Unix ms).',\n },\n {\n name: 'resolvedAt',\n description:\n 'When the vulnerability was resolved (Unix ms; null if open).',\n },\n ],\n responses: {\n vulnerabilities: vulnerabilitiesArraySchema,\n },\n },\n});\n\nexport const id = 'wiz';\n\ntype OauthTokenResponse = z.infer<typeof oauthTokenSchema>;\ntype WizIssue = z.infer<typeof issueSchema>;\ntype WizVulnerability = z.infer<typeof vulnerabilitySchema>;\n\nconst ISSUES_QUERY = `\n query Issues($first: Int!, $after: String, $filterBy: IssueFilters, $orderBy: IssueOrder) {\n issues(first: $first, after: $after, filterBy: $filterBy, orderBy: $orderBy) {\n nodes {\n id\n severity\n status\n type\n resolutionReason\n createdAt\n updatedAt\n resolvedAt\n dueAt\n sourceRule { id name }\n entitySnapshot { id name type cloudProvider externalId }\n }\n pageInfo { hasNextPage endCursor }\n }\n }\n`;\n\nconst VULNERABILITIES_QUERY = `\n query VulnerabilityFindings(\n $first: Int!\n $after: String\n $filterBy: VulnerabilityFindingFilters\n ) {\n vulnerabilityFindings(first: $first, after: $after, filterBy: $filterBy) {\n nodes {\n id\n name\n severity\n status\n vulnerabilityExternalId\n firstDetectedAt\n lastDetectedAt\n resolvedAt\n vulnerableAsset { id name type cloudPlatform }\n }\n pageInfo { hasNextPage endCursor }\n }\n }\n`;\n\nfunction parseIsoMs(value: string | null | undefined): number | null {\n if (!value) {\n return null;\n }\n const ms = Date.parse(value);\n return Number.isFinite(ms) ? ms : null;\n}\n\nfunction normalizeSeverity(value: string): Severity | string {\n const upper = value.toUpperCase();\n return (SEVERITIES as readonly string[]).includes(upper) ? upper : value;\n}\n\nfunction normalizeIssueStatus(value: string): IssueStatus | string {\n const upper = value.toUpperCase();\n return (ISSUE_STATUSES as readonly string[]).includes(upper) ? upper : value;\n}\n\nfunction normalizeVulnStatus(value: string): VulnStatus | string {\n const upper = value.toUpperCase();\n return (VULN_STATUSES as readonly string[]).includes(upper) ? upper : value;\n}\n\nexport class WizConnector extends BaseConnector<WizSettings, WizCredentials> {\n static readonly id = id;\n\n static readonly resources = wizResources;\n\n static readonly schemas = schemasFromResources(wizResources);\n\n static create(input: unknown, ctx?: ConnectorContext): WizConnector {\n const parsed = configFields.parse(input);\n return new WizConnector(\n {\n apiEndpoint: parsed.apiEndpoint,\n tokenEndpoint: parsed.tokenEndpoint,\n audience: parsed.audience,\n resources: parsed.resources,\n },\n {\n clientId: parsed.clientId,\n clientSecret: parsed.clientSecret,\n },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = wizCredentials;\n\n private accessToken: string | null = null;\n private accessTokenExpiry = 0;\n\n private tokenEndpoint(): string {\n return this.settings.tokenEndpoint ?? DEFAULT_TOKEN_ENDPOINT;\n }\n\n private audience(): string {\n return this.settings.audience ?? DEFAULT_AUDIENCE;\n }\n\n private resourceAllowed(resource: WizResource): boolean {\n const enabled = this.settings.resources;\n if (!enabled || enabled.length === 0) {\n return true;\n }\n return enabled.includes(resource);\n }\n\n private async refreshAccessToken(signal?: AbortSignal): Promise<string> {\n const form = new URLSearchParams({\n grant_type: 'client_credentials',\n audience: this.audience(),\n client_id: this.creds.clientId,\n client_secret: this.creds.clientSecret,\n });\n const res = await this.post<OauthTokenResponse>(this.tokenEndpoint(), {\n resource: 'oauth_token',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n 'User-Agent': connectorUserAgent('wiz'),\n },\n body: form.toString(),\n signal,\n });\n const token = res.body.access_token;\n const expiresIn = res.body.expires_in ?? 3600;\n this.accessToken = token;\n this.accessTokenExpiry =\n Date.now() + Math.max(0, expiresIn - TOKEN_EXPIRY_GRACE_S) * 1000;\n return token;\n }\n\n private async getAccessToken(signal?: AbortSignal): Promise<string> {\n if (!this.accessToken || Date.now() >= this.accessTokenExpiry) {\n return this.refreshAccessToken(signal);\n }\n return this.accessToken;\n }\n\n private async graphql<T>(\n query: string,\n variables: Record<string, unknown>,\n resource: string,\n signal?: AbortSignal,\n retried = false,\n ): Promise<HttpResponse<T>> {\n const token = await this.getAccessToken(signal);\n const res = await this.post<T>(this.settings.apiEndpoint, {\n resource,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': connectorUserAgent('wiz'),\n },\n body: JSON.stringify({ query, variables }),\n signal,\n });\n if (res.status === 401 && !retried) {\n this.accessToken = null;\n this.accessTokenExpiry = 0;\n return this.graphql<T>(query, variables, resource, signal, true);\n }\n const body = res.body as {\n data?: unknown;\n errors?: Array<{ message: string }>;\n };\n if (body.errors && body.errors.length > 0) {\n const messages = body.errors.map((e) => e.message).join('; ');\n throw new Error(`Wiz GraphQL error: ${messages}`);\n }\n if (!body.data) {\n throw new Error(\n `Wiz GraphQL response missing data for resource '${resource}'`,\n );\n }\n return res;\n }\n\n private issuesFilter(\n options: SyncOptions,\n ): Record<string, unknown> | undefined {\n if (!options.since) {\n return undefined;\n }\n return { updatedAt: { after: options.since } };\n }\n\n private vulnerabilitiesFilter(\n options: SyncOptions,\n ): Record<string, unknown> | undefined {\n if (!options.since) {\n return undefined;\n }\n return { lastDetectedAt: { after: options.since } };\n }\n\n private isPageAllOlderThan(\n items: WizIssue[],\n sinceMs: number | null,\n ): boolean {\n if (sinceMs === null || items.length === 0) {\n return false;\n }\n for (const i of items) {\n const ts = parseIsoMs(i.updatedAt ?? i.createdAt);\n if (ts !== null && ts >= sinceMs) {\n return false;\n }\n }\n return true;\n }\n\n private async fetchIssuesPage(\n page: string | null,\n options: SyncOptions,\n sinceMs: number | null,\n signal: AbortSignal | undefined,\n ): Promise<{ items: WizIssue[]; next: string | null }> {\n const res = await this.graphql<IssuesResponse>(\n ISSUES_QUERY,\n {\n first: PAGE_SIZE,\n after: page,\n filterBy: this.issuesFilter(options),\n orderBy: { field: 'UPDATED_AT', direction: 'DESC' },\n },\n 'issues',\n signal,\n );\n const conn = res.body.data?.issues;\n if (!conn?.nodes || !conn.pageInfo) {\n throw new Error(\"Wiz GraphQL response missing 'issues' connection\");\n }\n const nodes = conn.nodes;\n const next = conn.pageInfo.hasNextPage ? conn.pageInfo.endCursor : null;\n if (this.isPageAllOlderThan(nodes, sinceMs)) {\n return { items: nodes, next: null };\n }\n return { items: nodes, next };\n }\n\n private async fetchVulnerabilitiesPage(\n page: string | null,\n options: SyncOptions,\n signal: AbortSignal | undefined,\n ): Promise<{ items: WizVulnerability[]; next: string | null }> {\n const res = await this.graphql<VulnerabilitiesResponse>(\n VULNERABILITIES_QUERY,\n {\n first: PAGE_SIZE,\n after: page,\n filterBy: this.vulnerabilitiesFilter(options),\n },\n 'vulnerabilities',\n signal,\n );\n const conn = res.body.data?.vulnerabilityFindings;\n if (!conn?.nodes || !conn.pageInfo) {\n throw new Error(\n \"Wiz GraphQL response missing 'vulnerabilityFindings' connection\",\n );\n }\n const nodes = conn.nodes;\n const next = conn.pageInfo.hasNextPage ? conn.pageInfo.endCursor : null;\n return { items: nodes, next };\n }\n\n private async writeIssues(\n storage: StorageHandle,\n items: WizIssue[],\n sinceMs: number | null,\n ): Promise<void> {\n const writeEntity = this.resourceAllowed('issues');\n const writeEvent = this.resourceAllowed('issue_events');\n\n for (const issue of items) {\n const severity = normalizeSeverity(issue.severity);\n const status = normalizeIssueStatus(issue.status);\n const createdMs = parseIsoMs(issue.createdAt);\n const updatedMs = parseIsoMs(issue.updatedAt ?? null) ?? createdMs;\n const resolvedMs = parseIsoMs(issue.resolvedAt ?? null);\n const dueMs = parseIsoMs(issue.dueAt ?? null);\n const snap = issue.entitySnapshot ?? null;\n const cloudProvider = snap?.cloudProvider ?? null;\n const resourceType = snap?.type ?? null;\n const resourceName = snap?.name ?? null;\n const resourceExternalId = snap?.externalId ?? null;\n const ruleId = issue.sourceRule?.id ?? null;\n const ruleName = issue.sourceRule?.name ?? null;\n\n if (writeEntity) {\n await storage.entity({\n type: ISSUE_ENTITY,\n id: issue.id,\n attributes: {\n severity,\n status,\n issueType: issue.type ?? null,\n resolutionReason: issue.resolutionReason ?? null,\n ruleId,\n ruleName,\n resourceId: snap?.id ?? null,\n resourceName,\n resourceType,\n resourceExternalId,\n cloudProvider,\n createdAt: createdMs,\n resolvedAt: resolvedMs,\n dueAt: dueMs,\n },\n updated_at: updatedMs ?? createdMs ?? 0,\n });\n }\n\n if (writeEvent) {\n if (createdMs !== null && (sinceMs === null || createdMs >= sinceMs)) {\n await storage.event({\n name: ISSUE_EVENT,\n start_ts: createdMs,\n end_ts: null,\n attributes: {\n kind: 'opened' satisfies IssueEventKind,\n issueId: issue.id,\n severity,\n status,\n cloudProvider,\n resourceType,\n ruleName,\n },\n });\n }\n if (\n resolvedMs !== null &&\n (sinceMs === null || resolvedMs >= sinceMs)\n ) {\n await storage.event({\n name: ISSUE_EVENT,\n start_ts: resolvedMs,\n end_ts: null,\n attributes: {\n kind: 'resolved' satisfies IssueEventKind,\n issueId: issue.id,\n severity,\n status,\n cloudProvider,\n resourceType,\n ruleName,\n },\n });\n }\n }\n }\n }\n\n private async writeVulnerabilities(\n storage: StorageHandle,\n items: WizVulnerability[],\n ): Promise<void> {\n for (const v of items) {\n const severity = normalizeSeverity(v.severity);\n const status = normalizeVulnStatus(v.status);\n const firstMs = parseIsoMs(v.firstDetectedAt ?? null);\n const lastMs = parseIsoMs(v.lastDetectedAt ?? null) ?? firstMs;\n const resolvedMs = parseIsoMs(v.resolvedAt ?? null);\n await storage.entity({\n type: VULNERABILITY_ENTITY,\n id: v.id,\n attributes: {\n name: v.name ?? null,\n severity,\n status,\n cve: v.vulnerabilityExternalId ?? null,\n assetId: v.vulnerableAsset?.id ?? null,\n assetName: v.vulnerableAsset?.name ?? null,\n assetType: v.vulnerableAsset?.type ?? null,\n cloudPlatform: v.vulnerableAsset?.cloudPlatform ?? null,\n firstDetectedAt: firstMs,\n lastDetectedAt: lastMs,\n resolvedAt: resolvedMs,\n },\n updated_at: lastMs ?? firstMs ?? 0,\n });\n }\n }\n\n private async clearScopeOnFirstPage(\n storage: StorageHandle,\n phase: WizPhase,\n isFull: boolean,\n ): Promise<void> {\n if (!isFull) {\n return;\n }\n switch (phase) {\n case 'issues':\n if (this.resourceAllowed('issues')) {\n await storage.entities([], { types: [ISSUE_ENTITY] });\n }\n if (this.resourceAllowed('issue_events')) {\n await storage.events([], { names: [ISSUE_EVENT] });\n }\n return;\n case 'vulnerabilities':\n await storage.entities([], { types: [VULNERABILITY_ENTITY] });\n return;\n }\n }\n\n private resolveCursor(cursor: unknown): WizSyncCursor | undefined {\n return isWizSyncCursor(cursor) ? cursor : undefined;\n }\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor = this.resolveCursor(options.cursor);\n const isFull = options.mode === 'full';\n const sinceMs = options.since ? Date.parse(options.since) : null;\n\n const phases = selectActivePhases<WizResource, WizPhase>(\n (r) => (r === 'vulnerabilities' ? 'vulnerabilities' : 'issues'),\n PHASE_ORDER,\n this.settings.resources,\n );\n\n return paginateChunked<WizPhase, string>({\n phases,\n cursor,\n signal,\n logger: this.logger,\n fetchPage: async (phase, page, sig) => {\n switch (phase) {\n case 'issues':\n return this.fetchIssuesPage(page, options, sinceMs, sig);\n case 'vulnerabilities':\n return this.fetchVulnerabilitiesPage(page, options, sig);\n }\n },\n writeBatch: async (phase, items, page) => {\n if (page === null) {\n await this.clearScopeOnFirstPage(storage, phase, isFull);\n }\n switch (phase) {\n case 'issues':\n await this.writeIssues(storage, items as WizIssue[], sinceMs);\n return;\n case 'vulnerabilities':\n await this.writeVulnerabilities(\n storage,\n items as WizVulnerability[],\n );\n return;\n }\n },\n });\n }\n}\n","import { WizConnector } from './wiz';\n\nexport {\n WizConnector,\n configFields,\n doc,\n id,\n wizResources as resources,\n} from './wiz';\nexport type { WizResource, WizSettings } from './wiz';\nexport default WizConnector;\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rawdash/connector-wiz",
3
- "version": "0.27.0",
3
+ "version": "0.28.2",
4
4
  "description": "Rawdash connector for Wiz — syncs cloud-security issues, issue lifecycle events, and vulnerability findings from the Wiz GraphQL API into the six-shape storage model",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "zod": "^4.4.3",
27
- "@rawdash/core": "0.27.0"
27
+ "@rawdash/core": "0.28.2"
28
28
  },
29
29
  "devDependencies": {
30
30
  "fast-check": "^4.8.0",