@convisoappsec/mcp 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,117 @@
1
1
  import axios from 'axios';
2
+ import * as F from './filters.js';
3
+ import { buildMutationQuery } from './mutations.js';
4
+
5
+ /** Drop only undefined/null keys, keeping false/0/'' so intentional values survive. */
6
+ function compact(obj) {
7
+ const out = {};
8
+ for (const [k, v] of Object.entries(obj)) {
9
+ if (v !== undefined && v !== null) out[k] = v;
10
+ }
11
+ return out;
12
+ }
13
+
14
+ // --- Curated mutation input builders (pure; exported for tests) ---------------
15
+ // Each maps snake_case tool arguments to the GraphQL <Name>Input shape and supports
16
+ // an optional `extra` object spread in for any input field not in the curated signature.
17
+
18
+ export function buildChangeIssueStatusInput(a = {}) {
19
+ return { input: compact({ id: a.issue_id, status: a.status, reason: a.reason, ...(a.extra || {}) }) };
20
+ }
21
+
22
+ export function buildSourceCodeVulnerabilityInput(a = {}) {
23
+ return {
24
+ input: compact({
25
+ title: a.title,
26
+ description: a.description,
27
+ solution: a.solution,
28
+ category: a.category,
29
+ patterns: a.patterns,
30
+ reference: a.reference,
31
+ impactLevel: a.impact_level ?? 'MEDIUM',
32
+ probabilityLevel: a.probability_level ?? 'MEDIUM',
33
+ severity: a.severity,
34
+ summary: a.summary ?? '',
35
+ impactDescription: a.impact_description ?? '',
36
+ stepsToReproduce: a.steps_to_reproduce ?? '',
37
+ compromisedEnvironment: a.compromised_environment,
38
+ status: a.status ?? 'DRAFT',
39
+ assetId: a.asset_id,
40
+ projectId: a.project_id,
41
+ codeSnippet: a.code_snippet,
42
+ fileName: a.file_name,
43
+ firstLine: a.first_line,
44
+ vulnerableLine: a.vulnerable_line,
45
+ source: a.source,
46
+ sink: a.sink,
47
+ ...(a.extra || {}),
48
+ }),
49
+ };
50
+ }
51
+
52
+ export function buildCreateProjectInput(a = {}) {
53
+ return {
54
+ input: compact({
55
+ companyId: a.company_id,
56
+ typeId: a.type_id,
57
+ label: a.label,
58
+ goal: a.goal,
59
+ scope: a.scope,
60
+ startDate: a.start_date,
61
+ endDate: a.end_date,
62
+ ...(a.extra || {}),
63
+ }),
64
+ };
65
+ }
66
+
67
+ export function buildCreateAssetInput(a = {}) {
68
+ return {
69
+ input: compact({
70
+ companyId: a.company_id,
71
+ name: a.name,
72
+ assetType: a.asset_type,
73
+ url: a.url,
74
+ description: a.description,
75
+ businessImpact: a.business_impact,
76
+ exploitability: a.exploitability,
77
+ assetsTagList: a.tags,
78
+ ...(a.extra || {}),
79
+ }),
80
+ };
81
+ }
82
+
83
+ export function buildCreateTicketInput(a = {}) {
84
+ return {
85
+ input: compact({
86
+ companyId: a.company_id,
87
+ type: a.type,
88
+ title: a.title,
89
+ description: a.description,
90
+ priority: a.priority,
91
+ impact: a.impact,
92
+ ...(a.extra || {}),
93
+ }),
94
+ };
95
+ }
96
+
97
+ export function buildCreatePentestArtifactInput(a = {}) {
98
+ return {
99
+ input: compact({
100
+ companyId: a.company_id,
101
+ applicationId: a.application_id,
102
+ label: a.label,
103
+ pentestType: a.pentest_type,
104
+ description: a.description,
105
+ scopeText: a.scope_text,
106
+ assigneeEmail: a.assignee_email,
107
+ domains: a.domains,
108
+ inScope: a.in_scope,
109
+ outScope: a.out_scope,
110
+ ...(a.extra || {}),
111
+ }),
112
+ };
113
+ }
114
+
2
115
 
3
116
  const GraphQLFieldTemplates = {
4
117
  complete_issue: `
@@ -162,6 +275,181 @@ GraphQLFieldTemplates.complete_issue_with_snippet = `
162
275
  }
163
276
  `;
164
277
 
278
+ export const ISSUES_QUERY = `
279
+ query GetIssues($companyId: ID!, $pagination: PaginationInput!, $filters: IssuesFiltersInput, $sortOptions: [IssueSortOptionInput!]) {
280
+ issues(companyId: $companyId, pagination: $pagination, filters: $filters, sortOptions: $sortOptions) {
281
+ collection {
282
+ id
283
+ title
284
+ severity
285
+ status
286
+ createdAt
287
+ updatedAt
288
+ sla { state dueAt daysRemaining }
289
+ assignedUsers { name email }
290
+ asset { id name }
291
+ project { id label company { id } }
292
+ }
293
+ metadata { totalCount totalPages currentPage limitValue }
294
+ }
295
+ }
296
+ `;
297
+
298
+ export function buildIssuesVariables(companyId, page = 1, limit = 10, opts = {}) {
299
+ const {
300
+ severities, statuses, slaStates, createdAfter, createdBefore, assigneeEmails,
301
+ search, projectId, assetIds, issueIds, sortBy, order, extraFilters,
302
+ } = opts;
303
+ let built = F.prune({
304
+ severities: F.normalizeEnumList(severities, F.SEVERITIES),
305
+ statuses: F.normalizeEnumList(statuses, F.ISSUE_STATUSES),
306
+ slaStates: F.normalizeEnumList(slaStates, F.SLA_STATES),
307
+ createdAtRange: F.buildDateRange(createdAfter, createdBefore),
308
+ assigneeEmails: assigneeEmails || [],
309
+ partialTitle: search,
310
+ projectIds: (projectId !== undefined && projectId !== null && projectId !== 0) ? [projectId] : [],
311
+ assetIds: assetIds || [],
312
+ ids: issueIds || [],
313
+ });
314
+ if (extraFilters) {
315
+ for (const [k, val] of Object.entries(extraFilters)) {
316
+ if (val !== null && val !== undefined) built[k] = val;
317
+ }
318
+ }
319
+ return {
320
+ companyId,
321
+ pagination: { page, perPage: limit },
322
+ filters: built,
323
+ sortOptions: F.buildIssueSortOptions(sortBy, order),
324
+ };
325
+ }
326
+
327
+ export const ASSETS_QUERY = `
328
+ query ListAssets($companyId: ID!, $page: Int, $limit: Int, $search: AssetsSearch) {
329
+ assets(companyId: $companyId, page: $page, limit: $limit, search: $search) {
330
+ collection {
331
+ id
332
+ name
333
+ assetType
334
+ environment
335
+ audience
336
+ createdAt
337
+ updatedAt
338
+ riskScore { current { value } }
339
+ }
340
+ metadata { totalCount totalPages currentPage limitValue }
341
+ }
342
+ }
343
+ `;
344
+
345
+ export function buildAssetsVariables(companyId, page = 1, limit = 10, opts = {}) {
346
+ const {
347
+ name, search, tags, technology, businessImpact, exploitability, assetType,
348
+ environmentCompromised, coveredByScan, sortBy, order, extraFilters,
349
+ } = opts;
350
+ const s = F.prune({
351
+ name,
352
+ search,
353
+ tags: tags || [],
354
+ technology: technology || [],
355
+ businessImpact: F.normalizeEnumList(businessImpact, F.BUSINESS_IMPACT),
356
+ exploitability: F.normalizeEnumList(exploitability, F.EXPLOITABILITY),
357
+ assetType,
358
+ sortBy: F.normalizeEnum(sortBy, F.ASSET_SORT_BY, false),
359
+ order: F.normalizeEnum(order, F.ORDER),
360
+ });
361
+ if (environmentCompromised !== null && environmentCompromised !== undefined) {
362
+ s.environmentCompromised = environmentCompromised;
363
+ }
364
+ if (coveredByScan !== null && coveredByScan !== undefined) {
365
+ s.coveredByScan = coveredByScan;
366
+ }
367
+ if (extraFilters) {
368
+ for (const [k, val] of Object.entries(extraFilters)) {
369
+ if (val !== null && val !== undefined) s[k] = val;
370
+ }
371
+ }
372
+ return { companyId, page, limit, search: s };
373
+ }
374
+
375
+ export function buildTopVulnsVariables(companyId, opts = {}) {
376
+ const {
377
+ severities, statuses, assetIds, assetTags, createdAfter, createdBefore,
378
+ } = opts;
379
+ const fl = F.prune({
380
+ severities: F.normalizeEnumList(severities, F.SEVERITIES),
381
+ statuses: F.normalizeEnumList(statuses, F.ISSUE_STATUSES),
382
+ assetIds: assetIds || [],
383
+ assetTags: assetTags || [],
384
+ createdAtRange: F.buildDateRange(createdAfter, createdBefore),
385
+ });
386
+ const variables = { companyId };
387
+ if (Object.keys(fl).length) variables.filters = fl;
388
+ return variables;
389
+ }
390
+
391
+ export const COMPANIES_QUERY = `
392
+ query companies($page: Int, $limit: Int, $params: CompanySearch, $order: OrderScopesParams, $orderType: OrderParams){
393
+ companies(page: $page, limit: $limit, params: $params, order: $order, orderType : $orderType) {
394
+ collection {
395
+ id
396
+ label
397
+ }
398
+ }
399
+ }
400
+ `;
401
+
402
+ const PROJECTS_QUERY = `
403
+ query projects($page: Int, $limit: Int, $params: ProjectSearch, $sortBy: String, $descending: Boolean){
404
+ projects(page: $page, limit: $limit, params: $params, sortBy: $sortBy, descending : $descending) {
405
+ collection {
406
+ id
407
+ label
408
+ status
409
+ createdAt
410
+ startDate
411
+ endDate
412
+ allocatedAnalyst {
413
+ portalUser {
414
+ name
415
+ }
416
+ }
417
+ projectType {
418
+ label
419
+ }
420
+
421
+ company {
422
+ id
423
+ }
424
+ }
425
+ metadata {
426
+ totalCount
427
+ totalPages
428
+ currentPage
429
+ limitValue
430
+ }
431
+ }
432
+ }
433
+ `;
434
+
435
+ export function buildProjectsVariables(companyId, page = 1, limit = 1000, opts = {}) {
436
+ const {
437
+ search, statuses, projectTypes, createdAfter, createdBefore, tags, analystEmails,
438
+ sortBy = 'createdAt', descending = true,
439
+ } = opts;
440
+ const params = F.prune({
441
+ scopeIdEq: companyId,
442
+ labelCont: search,
443
+ projectStatusLabelIn: statuses || [],
444
+ projectTypeLabelIn: projectTypes || [],
445
+ createdAtGteq: createdAfter,
446
+ createdAtLteq: createdBefore,
447
+ tags: tags || [],
448
+ analystsEmailIn: analystEmails || [],
449
+ });
450
+ return { page, limit, params, sortBy, descending };
451
+ }
452
+
165
453
  class GraphQLClient {
166
454
  constructor(endpoint, apiKey) {
167
455
  this.endpoint = endpoint;
@@ -203,53 +491,33 @@ class GraphQLClient {
203
491
  }
204
492
 
205
493
  if (response.data?.errors) {
494
+ const messages = response.data.errors.map((x) => x?.message).filter(Boolean);
206
495
  const e = new Error('GraphQL error');
207
496
  e.status = 400;
497
+ // Validation/field errors about the caller's own request — safe and useful to surface
498
+ // so the model can correct the input (e.g. a missing required mutation field).
499
+ e.graphqlErrors = messages;
208
500
  throw e;
209
501
  }
210
502
 
211
503
  return response.data.data;
212
504
  }
213
505
 
214
- async get_issues(company_id, search, page = 1, limit = 1, project_id = null, issue_ids = [], asset_ids = []) {
215
- const query = `
216
- query GetIssues($companyId: ID!, $pagination: PaginationInput!, $filters: IssuesFiltersInput) {
217
- issues(companyId: $companyId, pagination: $pagination, filters: $filters) {
218
- collection {
219
- id
220
- title
221
- severity
222
- project {
223
- company {
224
- id
225
- }
226
- }
227
-
228
- asset {
229
- id
230
- }
231
- }
232
- }
233
- }
234
- `;
235
-
236
- const variables = {
237
- companyId: company_id,
238
- filters: { title: search },
239
- pagination: { page: page, perPage: limit }
240
- };
241
-
242
- if (project_id !== null && project_id !== 0) {
243
- variables.filters.projectIds = [project_id];
244
- }
245
- if (Array.isArray(issue_ids) && issue_ids.length > 0) {
246
- variables.filters.ids = issue_ids;
247
- }
248
- if (Array.isArray(asset_ids) && asset_ids.length > 0) {
249
- variables.filters.assetIds = asset_ids;
250
- }
506
+ async getIssues(companyId, opts = {}) {
507
+ const variables = buildIssuesVariables(companyId, opts.page || 1, opts.limit || 10, opts);
508
+ return this.execute(ISSUES_QUERY, variables);
509
+ }
251
510
 
252
- return this.execute(query, variables);
511
+ // Backward-compatible positional-argument wrapper used by existing FeedGateway callers.
512
+ async get_issues(company_id, search, page = 1, limit = 1, project_id = null, issue_ids = [], asset_ids = []) {
513
+ return this.getIssues(company_id, {
514
+ page,
515
+ limit,
516
+ search,
517
+ projectId: project_id,
518
+ issueIds: issue_ids,
519
+ assetIds: asset_ids,
520
+ });
253
521
  }
254
522
 
255
523
  async get_issue_by_id(issue_id, return_snippets = false) {
@@ -265,56 +533,15 @@ class GraphQLClient {
265
533
  return this.execute(query, variables);
266
534
  }
267
535
 
268
- async get_companies(page = 1, limit = 10, search = '') {
269
- const query = `
270
- query companies($page: Int, $limit: Int, $params: CompanySearch, $order: OrderScopesParams, $orderType: OrderParams){
271
- companies(page: $page, limit: $limit, params: $params, order: $order, orderType : $orderType) {
272
- collection {
273
- id
274
- label
275
- }
276
- }
277
- }
278
- `;
279
- const variables = { page, limit, params: { labelCont: search } };
280
- return this.execute(query, variables);
536
+ async get_companies(page = 1, limit = 10, search = '', label_eq = null) {
537
+ const params = F.prune({ labelCont: search, labelEq: label_eq });
538
+ const variables = { page, limit, params };
539
+ return this.execute(COMPANIES_QUERY, variables);
281
540
  }
282
541
 
283
- async get_projects(company_id, page = 1, limit = 1000, search = '') {
284
- const query = `
285
- query projects($page: Int, $limit: Int, $params: ProjectSearch, $sortBy: String, $descending: Boolean){
286
- projects(page: $page, limit: $limit, params: $params, sortBy: $sortBy, descending : $descending) {
287
- collection {
288
- id
289
- label
290
- status
291
- createdAt
292
- startDate
293
- endDate
294
- allocatedAnalyst {
295
- portalUser {
296
- name
297
- }
298
- }
299
- projectType {
300
- label
301
- }
302
-
303
- company {
304
- id
305
- }
306
- }
307
- }
308
- }
309
- `;
310
- const variables = {
311
- page,
312
- limit,
313
- params: { scopeIdEq: company_id, labelCont: search },
314
- sortBy: 'createdAt',
315
- descending: true
316
- };
317
- return this.execute(query, variables);
542
+ async get_projects(company_id, page = 1, limit = 1000, search = '', opts = {}) {
543
+ const variables = buildProjectsVariables(company_id, page, limit, { search, ...opts });
544
+ return this.execute(PROJECTS_QUERY, variables);
318
545
  }
319
546
 
320
547
  async get_project_by_id(project_id) {
@@ -356,30 +583,9 @@ class GraphQLClient {
356
583
  return this.execute(query, variables);
357
584
  }
358
585
 
359
- async get_assets_by_company(company_id, page = 1, limit = 10) {
360
- const query = `
361
- query ListAssets($companyId: ID!, $page: Int, $limit: Int) {
362
- assets(companyId: $companyId, page: $page, limit: $limit) {
363
- collection {
364
- id
365
- name
366
- assetType
367
- environment
368
- audience
369
- createdAt
370
- updatedAt
371
- }
372
- metadata {
373
- totalCount
374
- totalPages
375
- currentPage
376
- limitValue
377
- }
378
- }
379
- }
380
- `;
381
- const variables = { companyId: company_id, page, limit };
382
- return this.execute(query, variables);
586
+ async get_assets_by_company(company_id, page = 1, limit = 10, opts = {}) {
587
+ const variables = buildAssetsVariables(company_id, page, limit, opts);
588
+ return this.execute(ASSETS_QUERY, variables);
383
589
  }
384
590
 
385
591
  async get_asset_by_id(asset_id) {
@@ -412,10 +618,10 @@ class GraphQLClient {
412
618
  return this.execute(query, variables);
413
619
  }
414
620
 
415
- async get_top_vulnerabilities(company_id) {
621
+ async get_top_vulnerabilities(company_id, opts = {}) {
416
622
  const query = `
417
- query TopVulnerabilities($companyId: ID!) {
418
- topVulnerabilities(companyId: $companyId) {
623
+ query TopVulnerabilities($companyId: ID!, $filters: TopVulnerabilitiesFiltersInput) {
624
+ topVulnerabilities(companyId: $companyId, filters: $filters) {
419
625
  affectedAssetsCount
420
626
  criticalCount
421
627
  highCount
@@ -426,7 +632,7 @@ class GraphQLClient {
426
632
  }
427
633
  }
428
634
  `;
429
- const variables = { companyId: company_id };
635
+ const variables = buildTopVulnsVariables(company_id, opts);
430
636
  return this.execute(query, variables);
431
637
  }
432
638
 
@@ -530,6 +736,213 @@ class GraphQLClient {
530
736
  const variables = { projectId: project_id, reportId: report_id };
531
737
  return this.execute(query, variables);
532
738
  }
739
+
740
+ // --- Mutations -------------------------------------------------------------
741
+
742
+ // Generic engine: run any catalogued mutation. Builds the GraphQL document from the
743
+ // SDL-derived catalog (whitelist) and executes it. `variables` is typically { input }.
744
+ async executeMutation(name, variables = {}, returnFields = null) {
745
+ const built = buildMutationQuery(name, variables, returnFields);
746
+ return this.execute(built.query, built.variables);
747
+ }
748
+
749
+ // Curated shortcuts for the most common writes (thin wrappers over executeMutation).
750
+ async change_issue_status(a = {}) {
751
+ return this.executeMutation('changeIssueStatus', buildChangeIssueStatusInput(a));
752
+ }
753
+
754
+ async create_source_code_vulnerability(a = {}) {
755
+ return this.executeMutation('createSourceCodeVulnerability', buildSourceCodeVulnerabilityInput(a));
756
+ }
757
+
758
+ async create_project(a = {}) {
759
+ return this.executeMutation('createProject', buildCreateProjectInput(a));
760
+ }
761
+
762
+ async create_asset(a = {}) {
763
+ return this.executeMutation('createAsset', buildCreateAssetInput(a));
764
+ }
765
+
766
+ async create_ticket(a = {}) {
767
+ return this.executeMutation('createTicket', buildCreateTicketInput(a));
768
+ }
769
+
770
+ // --- Curated DAST / AI-Pentest shortcuts ----------------------------------
771
+ async run_dast(a = {}) {
772
+ return this.executeMutation('startConvisoDast', { input: compact({ assetId: a.asset_id, ...(a.extra || {}) }) });
773
+ }
774
+
775
+ async trigger_pentest(a = {}) {
776
+ return this.executeMutation('createPentestExecution', { input: compact({ artifactId: a.artifact_id, ...(a.extra || {}) }) });
777
+ }
778
+
779
+ async create_pentest_artifact(a = {}) {
780
+ return this.executeMutation('createPentestArtifact', buildCreatePentestArtifactInput(a));
781
+ }
782
+
783
+ // --- Read queries (curated) ------------------------------------------------
784
+ async get_tickets(company_id, { page = 1, limit = 25, search, sort_by, descending, params } = {}) {
785
+ const query = `
786
+ query GetTickets($companyId: ID!, $page: Int, $limit: Int, $sortBy: String, $descending: Boolean, $params: TicketSearch) {
787
+ tickets(companyId: $companyId, page: $page, limit: $limit, sortBy: $sortBy, descending: $descending, params: $params) {
788
+ collection { id title type status priority impact createdAt updatedAt createdBy { name email } assignee { name email } }
789
+ metadata { totalCount totalPages currentPage limitValue }
790
+ }
791
+ }`;
792
+ const p = compact({ search, ...(params || {}) });
793
+ return this.execute(query, { companyId: company_id, page, limit, sortBy: sort_by, descending, params: Object.keys(p).length ? p : undefined });
794
+ }
795
+
796
+ async get_ticket(company_id, ticket_id) {
797
+ const query = `
798
+ query GetTicket($companyId: ID!, $id: ID!) {
799
+ ticket(companyId: $companyId, id: $id) {
800
+ id title description type status priority impact createdAt updatedAt
801
+ createdBy { name email } assignee { name email }
802
+ }
803
+ }`;
804
+ return this.execute(query, { companyId: company_id, id: ticket_id });
805
+ }
806
+
807
+ async get_requirements(scope_id, { page = 1, limit = 25, filters } = {}) {
808
+ const query = `
809
+ query GetRequirements($scopeId: Int!, $pagination: BasePaginationInput!, $filters: RequirementsFilterInput) {
810
+ requirements(scopeId: $scopeId, pagination: $pagination, filters: $filters) {
811
+ collection { id label description global createdAt updatedAt }
812
+ metadata { totalCount totalPages currentPage limitValue }
813
+ }
814
+ }`;
815
+ return this.execute(query, { scopeId: scope_id, pagination: { page, perPage: limit }, filters });
816
+ }
817
+
818
+ async get_requirement(company_id, requirement_id) {
819
+ const query = `
820
+ query GetRequirement($companyId: ID!, $id: ID!) {
821
+ requirement(companyId: $companyId, id: $id) {
822
+ id label description global createdAt updatedAt
823
+ }
824
+ }`;
825
+ return this.execute(query, { companyId: company_id, id: requirement_id });
826
+ }
827
+
828
+ async get_project_requirements(project_id) {
829
+ const query = `
830
+ query GetProjectRequirements($projectId: ID!) {
831
+ projectRequirements(projectId: $projectId) {
832
+ id label description createdAt updatedAt
833
+ }
834
+ }`;
835
+ return this.execute(query, { projectId: project_id });
836
+ }
837
+
838
+ async get_applications(company_id, search = null) {
839
+ const query = `
840
+ query GetApplications($companyId: ID!, $search: String) {
841
+ applications(companyId: $companyId, search: $search) {
842
+ id name description url riskScore assetsCount createdAt updatedAt
843
+ }
844
+ }`;
845
+ return this.execute(query, { companyId: company_id, search });
846
+ }
847
+
848
+ async get_application(company_id, application_id) {
849
+ const query = `
850
+ query GetApplication($companyId: ID!, $id: ID!) {
851
+ application(id: $id, companyId: $companyId) {
852
+ id name description url riskScore assetsCount createdAt updatedAt
853
+ assets { id name }
854
+ }
855
+ }`;
856
+ return this.execute(query, { companyId: company_id, id: application_id });
857
+ }
858
+
859
+ async get_scan_histories(company_id, { assetIds, page = 1, limit = 25, filters, sortOptions } = {}) {
860
+ const query = `
861
+ query GetScanHistories($companyId: ID!, $assetIds: [ID!], $pagination: PaginationInput!, $filters: ScansHistoriesFiltersInput, $sortOptions: [ScansHistoriesSortOptionInput!]) {
862
+ scanHistories(companyId: $companyId, assetIds: $assetIds, pagination: $pagination, filters: $filters, sortOptions: $sortOptions) {
863
+ collection { id status integration createdAt durationInSeconds createdVulnerabilityCount closedVulnerabilityCount importedVulnerabilityCount failureReason asset { id name } }
864
+ metadata { totalCount totalPages currentPage limitValue }
865
+ }
866
+ }`;
867
+ return this.execute(query, { companyId: company_id, assetIds, pagination: { page, perPage: limit }, filters, sortOptions });
868
+ }
869
+
870
+ async get_asset_scans_count(company_id) {
871
+ const query = `
872
+ query GetAssetScansCount($companyId: ID!) {
873
+ assetScansCount(companyId: $companyId) { assetsWithScans assetsWithoutScans consideredScans }
874
+ }`;
875
+ return this.execute(query, { companyId: company_id });
876
+ }
877
+
878
+ async get_sbom_components(company_id, { page = 1, limit = 25, search } = {}) {
879
+ const query = `
880
+ query GetSbomComponents($companyId: ID!, $page: Int, $limit: Int, $search: SbomComponentSearchInput) {
881
+ sbomComponents(companyId: $companyId, page: $page, limit: $limit, search: $search) {
882
+ collection { id name version technology packageManager license issuesBySeverity asset { id name } createdAt updatedAt }
883
+ metadata { totalCount totalPages currentPage limitValue }
884
+ }
885
+ }`;
886
+ return this.execute(query, { companyId: company_id, page, limit, search });
887
+ }
888
+
889
+ async get_pentest_artifacts(company_id, { page = 1, limit = 25, search, assigneeEmail, pentestType, applicationId } = {}) {
890
+ const query = `
891
+ query GetPentestArtifacts($companyId: ID!, $pagination: BasePaginationInput!, $search: String, $assigneeEmail: String, $pentestType: String, $applicationId: ID) {
892
+ pentestArtifacts(companyId: $companyId, pagination: $pagination, search: $search, assigneeEmail: $assigneeEmail, pentestType: $pentestType, applicationId: $applicationId) {
893
+ collection { id label description pentestType createdAt updatedAt useScheduling scheduledAt executionsCount assignee { name email } application { id name } latestExecution { id status runNumber vulnerabilitiesCount createdAt } }
894
+ metadata { totalCount totalPages currentPage limitValue }
895
+ }
896
+ }`;
897
+ return this.execute(query, { companyId: company_id, pagination: { page, perPage: limit }, search, assigneeEmail, pentestType, applicationId });
898
+ }
899
+
900
+ async get_pentest_artifact(artifact_id) {
901
+ const query = `
902
+ query GetPentestArtifact($id: ID!) {
903
+ pentestArtifact(id: $id) {
904
+ id label description pentestType scopeText inScope outScope domains createdAt updatedAt useScheduling scheduledAt
905
+ assignee { name email } application { id name }
906
+ executions { id status runNumber vulnerabilitiesCount createdAt }
907
+ }
908
+ }`;
909
+ return this.execute(query, { id: artifact_id });
910
+ }
911
+
912
+ async get_pentest_execution(execution_id) {
913
+ const query = `
914
+ query GetPentestExecution($id: ID!) {
915
+ pentestExecution(id: $id) {
916
+ id status kind triggerKind runNumber startedAt finishedAt durationSeconds
917
+ vulnerabilitiesCount severityBreakdown nodeCount retestFixedCount retestTotalCount
918
+ project { id label } pentestArtifact { id label } triggeredBy { name email }
919
+ }
920
+ }`;
921
+ return this.execute(query, { id: execution_id });
922
+ }
923
+
924
+ async get_threat_model_artifacts(company_id, { page = 1, limit = 25, search, assigneeEmail, hasVersion } = {}) {
925
+ const query = `
926
+ query GetThreatModelArtifacts($companyId: ID!, $pagination: BasePaginationInput!, $search: String, $assigneeEmail: String, $hasVersion: Boolean) {
927
+ threatModelArtifacts(companyId: $companyId, pagination: $pagination, search: $search, assigneeEmail: $assigneeEmail, hasVersion: $hasVersion) {
928
+ collection { id label description scopeText createdAt updatedAt assignee { name email } latestVersion { id version createdAt } }
929
+ metadata { totalCount totalPages currentPage limitValue }
930
+ }
931
+ }`;
932
+ return this.execute(query, { companyId: company_id, pagination: { page, perPage: limit }, search, assigneeEmail, hasVersion });
933
+ }
934
+
935
+ async get_threat_model_artifact(artifact_id) {
936
+ const query = `
937
+ query GetThreatModelArtifact($id: ID!) {
938
+ threatModelArtifact(id: $id) {
939
+ id label description scopeText createdAt updatedAt
940
+ assignee { name email }
941
+ versions { id version createdAt diagramType scopeText notes }
942
+ }
943
+ }`;
944
+ return this.execute(query, { id: artifact_id });
945
+ }
533
946
  }
534
947
 
535
948
  export { GraphQLClient };