conviso-ast 3.0.0__py3-none-any.whl

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.
Files changed (128) hide show
  1. conviso_ast-3.0.0.data/scripts/flow_bash_completer.sh +21 -0
  2. conviso_ast-3.0.0.data/scripts/flow_fish_completer.fish +1 -0
  3. conviso_ast-3.0.0.data/scripts/flow_zsh_completer.sh +32 -0
  4. conviso_ast-3.0.0.dist-info/METADATA +37 -0
  5. conviso_ast-3.0.0.dist-info/RECORD +128 -0
  6. conviso_ast-3.0.0.dist-info/WHEEL +5 -0
  7. conviso_ast-3.0.0.dist-info/entry_points.txt +3 -0
  8. conviso_ast-3.0.0.dist-info/top_level.txt +1 -0
  9. convisoappsec/__init__.py +0 -0
  10. convisoappsec/common/__init__.py +5 -0
  11. convisoappsec/common/box.py +251 -0
  12. convisoappsec/common/cleaner.py +78 -0
  13. convisoappsec/common/docker.py +399 -0
  14. convisoappsec/common/exceptions.py +8 -0
  15. convisoappsec/common/git_data_parser.py +76 -0
  16. convisoappsec/common/graphql/__init__.py +0 -0
  17. convisoappsec/common/graphql/error_handlers.py +75 -0
  18. convisoappsec/common/graphql/errors.py +16 -0
  19. convisoappsec/common/graphql/low_client.py +51 -0
  20. convisoappsec/common/retry_handler.py +40 -0
  21. convisoappsec/common/strings.py +8 -0
  22. convisoappsec/flow/__init__.py +3 -0
  23. convisoappsec/flow/api.py +104 -0
  24. convisoappsec/flow/cleaner.py +118 -0
  25. convisoappsec/flow/graphql_api/__init__.py +0 -0
  26. convisoappsec/flow/graphql_api/beta/__init__.py +0 -0
  27. convisoappsec/flow/graphql_api/beta/client.py +18 -0
  28. convisoappsec/flow/graphql_api/beta/models/__init__.py +0 -0
  29. convisoappsec/flow/graphql_api/beta/models/issues/__init__.py +0 -0
  30. convisoappsec/flow/graphql_api/beta/models/issues/container.py +72 -0
  31. convisoappsec/flow/graphql_api/beta/models/issues/iac.py +6 -0
  32. convisoappsec/flow/graphql_api/beta/models/issues/normalize.py +13 -0
  33. convisoappsec/flow/graphql_api/beta/models/issues/sast.py +53 -0
  34. convisoappsec/flow/graphql_api/beta/models/issues/sca.py +78 -0
  35. convisoappsec/flow/graphql_api/beta/resources_api.py +142 -0
  36. convisoappsec/flow/graphql_api/beta/schemas/__init__.py +0 -0
  37. convisoappsec/flow/graphql_api/beta/schemas/mutations/__init__.py +61 -0
  38. convisoappsec/flow/graphql_api/beta/schemas/resolvers/__init__.py +0 -0
  39. convisoappsec/flow/graphql_api/v1/__init__.py +0 -0
  40. convisoappsec/flow/graphql_api/v1/client.py +46 -0
  41. convisoappsec/flow/graphql_api/v1/models/__init__.py +0 -0
  42. convisoappsec/flow/graphql_api/v1/models/asset.py +14 -0
  43. convisoappsec/flow/graphql_api/v1/models/issues.py +16 -0
  44. convisoappsec/flow/graphql_api/v1/models/project.py +35 -0
  45. convisoappsec/flow/graphql_api/v1/resources_api.py +489 -0
  46. convisoappsec/flow/graphql_api/v1/schemas/__init__.py +0 -0
  47. convisoappsec/flow/graphql_api/v1/schemas/mutations/__init__.py +212 -0
  48. convisoappsec/flow/graphql_api/v1/schemas/resolvers/__init__.py +180 -0
  49. convisoappsec/flow/source_code_scanner/__init__.py +9 -0
  50. convisoappsec/flow/source_code_scanner/exceptions.py +2 -0
  51. convisoappsec/flow/source_code_scanner/scc.py +68 -0
  52. convisoappsec/flow/source_code_scanner/source_code_scanner.py +177 -0
  53. convisoappsec/flow/util/__init__.py +7 -0
  54. convisoappsec/flow/util/ci_provider.py +99 -0
  55. convisoappsec/flow/util/metrics.py +16 -0
  56. convisoappsec/flow/util/source_code_compressor.py +22 -0
  57. convisoappsec/flow/version_control_system_adapter.py +528 -0
  58. convisoappsec/flow/version_searchers/__init__.py +9 -0
  59. convisoappsec/flow/version_searchers/sorted_by_versioning_style.py +85 -0
  60. convisoappsec/flow/version_searchers/timebased_version_seacher.py +39 -0
  61. convisoappsec/flow/version_searchers/version_searcher_result.py +33 -0
  62. convisoappsec/flow/versioning_style/__init__.py +0 -0
  63. convisoappsec/flow/versioning_style/semantic_versioning.py +44 -0
  64. convisoappsec/flowcli/__init__.py +3 -0
  65. convisoappsec/flowcli/__main__.py +4 -0
  66. convisoappsec/flowcli/assets/__init__.py +4 -0
  67. convisoappsec/flowcli/assets/create.py +88 -0
  68. convisoappsec/flowcli/assets/entrypoint.py +20 -0
  69. convisoappsec/flowcli/assets/ls.py +63 -0
  70. convisoappsec/flowcli/ast/__init__.py +3 -0
  71. convisoappsec/flowcli/ast/entrypoint.py +427 -0
  72. convisoappsec/flowcli/common.py +175 -0
  73. convisoappsec/flowcli/companies/__init__.py +0 -0
  74. convisoappsec/flowcli/companies/ls.py +25 -0
  75. convisoappsec/flowcli/container/__init__.py +3 -0
  76. convisoappsec/flowcli/container/entrypoint.py +17 -0
  77. convisoappsec/flowcli/container/run.py +306 -0
  78. convisoappsec/flowcli/context.py +49 -0
  79. convisoappsec/flowcli/deploy/__init__.py +0 -0
  80. convisoappsec/flowcli/deploy/create/__init__.py +4 -0
  81. convisoappsec/flowcli/deploy/create/context.py +12 -0
  82. convisoappsec/flowcli/deploy/create/entrypoint.py +31 -0
  83. convisoappsec/flowcli/deploy/create/with_/__init__.py +3 -0
  84. convisoappsec/flowcli/deploy/create/with_/entrypoint.py +20 -0
  85. convisoappsec/flowcli/deploy/create/with_/tag_tracker/__init__.py +4 -0
  86. convisoappsec/flowcli/deploy/create/with_/tag_tracker/context.py +11 -0
  87. convisoappsec/flowcli/deploy/create/with_/tag_tracker/entrypoint.py +30 -0
  88. convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/__init__.py +4 -0
  89. convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/entrypoint.py +21 -0
  90. convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/time_.py +84 -0
  91. convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/versioning_style.py +115 -0
  92. convisoappsec/flowcli/deploy/create/with_/values.py +133 -0
  93. convisoappsec/flowcli/entrypoint.py +103 -0
  94. convisoappsec/flowcli/environment_checker.py +45 -0
  95. convisoappsec/flowcli/findings/__init__.py +4 -0
  96. convisoappsec/flowcli/findings/create/__init__.py +4 -0
  97. convisoappsec/flowcli/findings/create/entrypoint.py +18 -0
  98. convisoappsec/flowcli/findings/create/with_/__init__.py +3 -0
  99. convisoappsec/flowcli/findings/create/with_/entrypoint.py +19 -0
  100. convisoappsec/flowcli/findings/create/with_/version_tracker.py +93 -0
  101. convisoappsec/flowcli/findings/entrypoint.py +19 -0
  102. convisoappsec/flowcli/findings/import_sarif/__init__.py +4 -0
  103. convisoappsec/flowcli/findings/import_sarif/entrypoint.py +430 -0
  104. convisoappsec/flowcli/help_option.py +18 -0
  105. convisoappsec/flowcli/iac/__init__.py +3 -0
  106. convisoappsec/flowcli/iac/entrypoint.py +17 -0
  107. convisoappsec/flowcli/iac/run.py +328 -0
  108. convisoappsec/flowcli/requirements_verifier.py +132 -0
  109. convisoappsec/flowcli/sast/__init__.py +3 -0
  110. convisoappsec/flowcli/sast/entrypoint.py +17 -0
  111. convisoappsec/flowcli/sast/run.py +485 -0
  112. convisoappsec/flowcli/sbom/__init__.py +3 -0
  113. convisoappsec/flowcli/sbom/entrypoint.py +17 -0
  114. convisoappsec/flowcli/sbom/generate.py +235 -0
  115. convisoappsec/flowcli/sca/__init__.py +3 -0
  116. convisoappsec/flowcli/sca/entrypoint.py +17 -0
  117. convisoappsec/flowcli/sca/run.py +479 -0
  118. convisoappsec/flowcli/vulnerability/__init__.py +3 -0
  119. convisoappsec/flowcli/vulnerability/assert_security_rules.py +201 -0
  120. convisoappsec/flowcli/vulnerability/container_vulnerability_manager.py +175 -0
  121. convisoappsec/flowcli/vulnerability/entrypoint.py +18 -0
  122. convisoappsec/flowcli/vulnerability/rules_schema.json +53 -0
  123. convisoappsec/flowcli/vulnerability/run.py +487 -0
  124. convisoappsec/logger.py +29 -0
  125. convisoappsec/sast/__init__.py +0 -0
  126. convisoappsec/sast/decision.py +45 -0
  127. convisoappsec/sast/sastbox.py +296 -0
  128. convisoappsec/version.py +1 -0
@@ -0,0 +1,104 @@
1
+ import copy
2
+ import json
3
+ from contextlib import suppress
4
+ from os import SEEK_SET
5
+ from urllib.parse import urljoin
6
+
7
+ import jsonschema
8
+ import requests
9
+
10
+ PRODUCTION_API_URL = "https://api.convisoappsec.com"
11
+ STAGING_API_URL = "https://api.staging.convisoappsec.com"
12
+ DEVELOPMENT_API_URL = "http://localhost:3000"
13
+ DEFAULT_API_URL = PRODUCTION_API_URL
14
+
15
+
16
+ class RequestsSession(requests.Session):
17
+
18
+ def __init__(self, base_url):
19
+ super().__init__()
20
+ self.base_url = base_url
21
+
22
+ def request(self, method, url, *args, **kwargs):
23
+ url = urljoin(self.base_url, url)
24
+
25
+ return super().request(
26
+ method, url, *args, **kwargs
27
+ )
28
+
29
+
30
+ class FlowAPIException(Exception):
31
+ pass
32
+
33
+
34
+ class FlowAPIAccessDeniedException(FlowAPIException):
35
+ pass
36
+
37
+
38
+ class DeployNotFoundException(FlowAPIException):
39
+ pass
40
+
41
+
42
+ class DockerRegistry(object):
43
+ SAST_ENDPOINT = '/auth/public_auth'
44
+
45
+ def __init__(self, client):
46
+ self.client = client
47
+
48
+ def get_sast_token(self):
49
+ session = self.client.requests_session
50
+ response = session.get(self.SAST_ENDPOINT)
51
+ response.raise_for_status()
52
+ return response.text
53
+
54
+
55
+ class RESTClient(object):
56
+
57
+ def __init__(
58
+ self,
59
+ url=STAGING_API_URL,
60
+ key=None,
61
+ insecure=False,
62
+ user_agent=None,
63
+ ci_provider_name=None
64
+ ):
65
+ self.url = url
66
+ self.insecure = insecure
67
+ self.key = key
68
+ self.user_agent = user_agent
69
+ self.ci_provider_name = ci_provider_name
70
+
71
+ @property
72
+ def requests_session(self):
73
+ session = RequestsSession(self.url)
74
+ session.verify = not self.insecure
75
+
76
+ session.headers.update({
77
+ 'x-api-key': self.key,
78
+ 'x-flowcli-ci-provider-name': self.ci_provider_name
79
+ })
80
+
81
+ if self.user_agent:
82
+ user_agent_header = {}
83
+ name = self.user_agent.get('name')
84
+ version = self.user_agent.get('version')
85
+
86
+ if name and version:
87
+ user_agent_header_fmt = "{name}/{version}"
88
+ user_agent_header_content = user_agent_header_fmt.format(
89
+ name=name,
90
+ version=version,
91
+ )
92
+
93
+ user_agent_header = {
94
+ 'User-Agent': user_agent_header_content
95
+ }
96
+
97
+ session.headers.update(user_agent_header)
98
+
99
+ return session
100
+
101
+
102
+ @property
103
+ def docker_registry(self):
104
+ return DockerRegistry(self)
@@ -0,0 +1,118 @@
1
+ import subprocess
2
+ import shutil
3
+ import os
4
+ from pathlib import Path
5
+ from typing import List, Optional
6
+
7
+
8
+ class RunnerCleanup:
9
+ """Class to handle CI/CD runner cleanup operations"""
10
+
11
+ def __init__(self, temp_dirs: Optional[List[str]] = None):
12
+ """
13
+ Initialize cleanup class
14
+
15
+ Args:
16
+ temp_dirs: Optional list of temporary directories to clean
17
+ """
18
+ self.temp_dirs = temp_dirs or [
19
+ "/tmp",
20
+ "/var/tmp",
21
+ str(Path.home() / ".cache")
22
+ ]
23
+ self.is_runner = self._is_running_in_ci()
24
+ self._check_requirements()
25
+
26
+ def _is_running_in_ci(self) -> bool:
27
+ """Check if running in a CI/CD environment"""
28
+ ci_indicators = {
29
+ 'CI': None,
30
+ 'GITLAB_CI': None,
31
+ 'GITHUB_ACTIONS': None,
32
+ 'JENKINS_URL': None,
33
+ 'TRAVIS': None,
34
+ 'CIRCLECI': None,
35
+ 'BUILD_ID': None,
36
+ 'CI_JOB_ID': None,
37
+ 'GITHUB_RUN_ID': None,
38
+ }
39
+
40
+ # Check if any CI-specific environment variable is set
41
+ for var in ci_indicators:
42
+ if os.environ.get(var):
43
+ print(f"Detected CI environment: {var}")
44
+ return True
45
+
46
+ # Additional check for runner-specific variables
47
+ if os.environ.get('RUNNER_TEMP') or os.environ.get('RUNNER_WORKSPACE'):
48
+ print("Detected GitHub Actions runner environment")
49
+ return True
50
+
51
+ print("No CI environment detected")
52
+ return False
53
+
54
+ def _run_command(self, command: str) -> Optional[str]:
55
+ """Execute shell command and return output"""
56
+
57
+ try:
58
+ result = subprocess.run(
59
+ command,
60
+ shell=True,
61
+ check=True,
62
+ stdout=subprocess.PIPE,
63
+ stderr=subprocess.PIPE,
64
+ text=True
65
+ )
66
+ return result.stdout.strip()
67
+ except subprocess.CalledProcessError as e:
68
+ print(f"Error executing '{command}': {e.stderr}")
69
+ return None
70
+
71
+ def _check_requirements(self) -> None:
72
+ """Check system requirements"""
73
+ if not self._run_command("docker --version"):
74
+ raise RuntimeError("Docker is not installed or not accessible")
75
+
76
+ def get_disk_space(self) -> tuple[float, float, float]:
77
+ """Get disk space information in GB"""
78
+ stat = shutil.disk_usage("/")
79
+ total = stat.total / (1024 ** 3)
80
+ used = stat.used / (1024 ** 3)
81
+ free = stat.free / (1024 ** 3)
82
+ return total, used, free
83
+
84
+ def cleanup_docker(self) -> None:
85
+ """Clean up Docker resources"""
86
+ commands = [
87
+ "docker container prune -f",
88
+ "docker image prune -f",
89
+ "docker volume prune -f",
90
+ "docker network prune -f"
91
+ ]
92
+
93
+ for cmd in commands:
94
+ self._run_command(cmd)
95
+
96
+ def cleanup_temp(self) -> None:
97
+ """Clean up temporary directories"""
98
+
99
+ for dir_path in self.temp_dirs:
100
+ dir_path = Path(dir_path)
101
+ if not dir_path.exists():
102
+ continue
103
+
104
+ for item in dir_path.iterdir():
105
+ try:
106
+ if item.is_file():
107
+ item.unlink()
108
+ elif item.is_dir():
109
+ shutil.rmtree(item, ignore_errors=True)
110
+ except Exception as e:
111
+ print(f"Failed to remove {item}: {e}")
112
+
113
+ def cleanup_all(self):
114
+ """Perform full cleanup and return before/after disk space stats"""
115
+ print("Cleaning up ...")
116
+
117
+ self.cleanup_docker()
118
+ self.cleanup_temp()
File without changes
File without changes
@@ -0,0 +1,18 @@
1
+
2
+ from convisoappsec.common.graphql.low_client import GraphQLClient
3
+ from convisoappsec.flow.graphql_api.beta.resources_api import IssuesAPI
4
+
5
+
6
+ class ConvisoGraphQLClientBeta():
7
+ DEFAULT_AUTHORIZATION_HEADER_NAME = 'x-api-key'
8
+
9
+ def __init__(self, api_url, api_key):
10
+ headers = {
11
+ self.DEFAULT_AUTHORIZATION_HEADER_NAME: api_key
12
+ }
13
+
14
+ self.__low_client = GraphQLClient(api_url, headers)
15
+
16
+ @property
17
+ def issues(self):
18
+ return IssuesAPI(self.__low_client)
File without changes
@@ -0,0 +1,72 @@
1
+ from convisoappsec.flow.graphql_api.beta.models.issues.normalize import Normalize
2
+
3
+
4
+ class CreateOrUpdateContainerFindingInput:
5
+ def __init__(
6
+ self,
7
+ asset_id,
8
+ title,
9
+ description,
10
+ severity,
11
+ solution,
12
+ reference,
13
+ affected_version,
14
+ package,
15
+ cve,
16
+ patched_version,
17
+ category,
18
+ original_issue_id_from_tool
19
+ ):
20
+ self.asset_id = asset_id
21
+ self.title = title
22
+ self.description = description
23
+ self.severity = Normalize.normalize_severity(severity)
24
+ self.solution = solution
25
+ self.reference = reference
26
+ self.affected_version = affected_version
27
+ self.package = package
28
+ self.patched_version = patched_version
29
+ self.original_issue_id_from_tool = original_issue_id_from_tool
30
+ self.category = self.process_field(category)
31
+ self.cve = self.process_field(cve)
32
+
33
+ def to_graphql_dict(self):
34
+ """
35
+ This function returns a dictionary containing various attributes of an
36
+ asset in a GraphQL format.
37
+ """
38
+ return {
39
+ "assetId": int(self.asset_id),
40
+ "title": self.title,
41
+ "description": self.description,
42
+ "severity": self.severity,
43
+ "solution": self.solution,
44
+ "reference": self.reference,
45
+ "affectedVersion": self.affected_version,
46
+ "package": self.package,
47
+ "cve": self.cve,
48
+ "patchedVersion": self.patched_version,
49
+ "category": self.category,
50
+ "originalIssueIdFromTool": self.original_issue_id_from_tool
51
+ }
52
+
53
+ @staticmethod
54
+ def process_field(value):
55
+ """
56
+ Processes a field to ensure it is converted into a string.
57
+
58
+ - If the value is a list, it joins the items into a comma-separated string.
59
+ - If the value is a string, it returns the string as is.
60
+ - If the value is neither a list nor a string, it returns an empty string.
61
+
62
+ Args:
63
+ value (list | str | Any): The value to process.
64
+
65
+ Returns:
66
+ str: The processed string representation of the value.
67
+ """
68
+ if isinstance(value, list):
69
+ return ' , '.join(value)
70
+ elif isinstance(value, str):
71
+ return value
72
+ return ''
@@ -0,0 +1,6 @@
1
+ from convisoappsec.flow.graphql_api.beta.models.issues.sast import CreateSastFindingInput
2
+
3
+ class CreateIacFindingInput(CreateSastFindingInput):
4
+
5
+ def __init__(self, *args, **kwargs):
6
+ super().__init__(*args, **kwargs)
@@ -0,0 +1,13 @@
1
+ class Normalize:
2
+
3
+ @staticmethod
4
+ def normalize_severity(severity):
5
+ """
6
+ The function normalizes severity by validating and returning a standardized severity level.
7
+ """
8
+
9
+ validate_severity = ["LOW", "MEDIUM", "HIGH", "CRITICAL", "NOTIFICATION"]
10
+ if severity.upper() in validate_severity:
11
+ return severity.upper()
12
+ else:
13
+ return validate_severity[0]
@@ -0,0 +1,53 @@
1
+ from convisoappsec.flow.graphql_api.beta.models.issues.normalize import Normalize
2
+
3
+ class CreateSastFindingInput:
4
+ def __init__(
5
+ self,
6
+ asset_id,
7
+ code_snippet,
8
+ file_name,
9
+ vulnerable_line,
10
+ first_line,
11
+ title,
12
+ description,
13
+ severity,
14
+ reference,
15
+ category,
16
+ original_issue_id_from_tool,
17
+ solution,
18
+ control_sync_status_id
19
+ ):
20
+ self.asset_id = asset_id
21
+ self.severity = Normalize.normalize_severity(severity)
22
+ self.title = title
23
+ self.description = description
24
+ self.code_snippet = code_snippet
25
+ self.file_name = file_name
26
+ self.vulnerable_line = int(vulnerable_line)
27
+ self.first_line = int(first_line)
28
+ self.reference = reference
29
+ self.category = category
30
+ self.original_issue_id_from_tool = original_issue_id_from_tool
31
+ self.solution = solution
32
+ self.control_sync_status_id = control_sync_status_id
33
+
34
+ def to_graphql_dict(self):
35
+ """
36
+ This function returns a dictionary containing various attributes of an
37
+ asset in a GraphQL format.
38
+ """
39
+ return {
40
+ "assetId": int(self.asset_id),
41
+ "severity": self.severity,
42
+ "title": self.title,
43
+ "description": self.description,
44
+ "codeSnippet": self.code_snippet,
45
+ "fileName": self.file_name,
46
+ "vulnerableLine": int(self.vulnerable_line),
47
+ "firstLine": int(self.first_line),
48
+ "reference": self.reference,
49
+ "category": str(self.category),
50
+ "originalIssueIdFromTool": str(self.original_issue_id_from_tool),
51
+ "solution": str(self.solution),
52
+ "controlSyncStatusId": str(self.control_sync_status_id)
53
+ }
@@ -0,0 +1,78 @@
1
+ from convisoappsec.flow.graphql_api.beta.models.issues.normalize import Normalize
2
+
3
+
4
+ class CreateScaFindingInput:
5
+ def __init__(
6
+ self,
7
+ asset_id,
8
+ title,
9
+ description,
10
+ severity,
11
+ solution,
12
+ reference,
13
+ file_name,
14
+ affected_version,
15
+ package,
16
+ cve,
17
+ patched_version,
18
+ category,
19
+ original_issue_id_from_tool,
20
+ control_sync_status_id
21
+ ):
22
+ self.asset_id = asset_id
23
+ self.title = title
24
+ self.description = description
25
+ self.severity = Normalize.normalize_severity(severity)
26
+ self.solution = solution
27
+ self.reference = reference
28
+ self.file_name = file_name
29
+ self.affected_version = affected_version
30
+ self.package = package
31
+ self.patched_version = patched_version
32
+ self.original_issue_id_from_tool = original_issue_id_from_tool
33
+ self.category = self.process_field(category)
34
+ self.cve = self.process_field(cve)
35
+ self.control_sync_status_id = control_sync_status_id
36
+
37
+ def to_graphql_dict(self):
38
+ """
39
+ This function returns a dictionary containing various attributes of an
40
+ asset in a GraphQL format.
41
+ """
42
+ return {
43
+ "assetId": int(self.asset_id),
44
+ "title": self.title,
45
+ "description": self.description,
46
+ "severity": self.severity,
47
+ "solution": self.solution,
48
+ "reference": self.reference,
49
+ "fileName": self.file_name,
50
+ "affectedVersion": self.affected_version,
51
+ "package": self.package,
52
+ "cve": self.cve,
53
+ "patchedVersion": self.patched_version,
54
+ "category": self.category,
55
+ "originalIssueIdFromTool": self.original_issue_id_from_tool,
56
+ "controlSyncStatusId": self.control_sync_status_id
57
+ }
58
+
59
+ @staticmethod
60
+ def process_field(value):
61
+ """
62
+ Processes a field to ensure it is converted into a string.
63
+
64
+ - If the value is a list, it joins the items into a comma-separated string.
65
+ - If the value is a string, it returns the string as is.
66
+ - If the value is neither a list nor a string, it returns an empty string.
67
+
68
+ Args:
69
+ value (list | str | Any): The value to process.
70
+
71
+ Returns:
72
+ str: The processed string representation of the value.
73
+ """
74
+ if isinstance(value, list):
75
+ return ' , '.join(value)
76
+ elif isinstance(value, str):
77
+ return value
78
+ return ''
@@ -0,0 +1,142 @@
1
+ import jmespath
2
+ from convisoappsec.flow.graphql_api.beta.models.issues.iac import CreateIacFindingInput
3
+ from convisoappsec.flow.graphql_api.beta.models.issues.sast import CreateSastFindingInput
4
+ from convisoappsec.flow.graphql_api.beta.models.issues.sca import CreateScaFindingInput
5
+ from convisoappsec.flow.graphql_api.beta.models.issues.container import CreateOrUpdateContainerFindingInput
6
+ from convisoappsec.flow.graphql_api.beta.schemas import mutations
7
+ from convisoappsec.flow.graphql_api.v1.schemas import resolvers
8
+
9
+
10
+ class IssuesAPI(object):
11
+ """ To operations on Issues's (aka, findings and vulnerabilities)) in Conviso Platform. """
12
+
13
+ def __init__(self, conviso_graphql_client):
14
+ self.__conviso_graphql_client = conviso_graphql_client
15
+
16
+ def create_sast(self, sast_issue_model: CreateSastFindingInput):
17
+ graphql_variables = {
18
+ "input": sast_issue_model.to_graphql_dict()
19
+ }
20
+
21
+ graphql_body_response = self.__conviso_graphql_client.execute(
22
+ mutations.CREATE_SAST_FINDING_INPUT,
23
+ graphql_variables
24
+ )
25
+
26
+ expected_path = 'createSastFinding.issue'
27
+
28
+ issue = jmespath.search(
29
+ expected_path,
30
+ graphql_body_response,
31
+ )
32
+
33
+ return issue
34
+
35
+ def create_sca(self, sca_issue_model: CreateScaFindingInput):
36
+ graphql_variables = {
37
+ "input": sca_issue_model.to_graphql_dict()
38
+ }
39
+
40
+ graphql_body_response = self.__conviso_graphql_client.execute(
41
+ mutations.CREATE_SCA_FINDING_INPUT,
42
+ graphql_variables
43
+ )
44
+
45
+ expected_path = 'createScaFinding.issue'
46
+
47
+ issue = jmespath.search(
48
+ expected_path,
49
+ graphql_body_response,
50
+ )
51
+
52
+ return issue
53
+
54
+ def create_iac(self, issue_model: CreateIacFindingInput):
55
+ graphql_variables = {
56
+ "input": issue_model.to_graphql_dict()
57
+ }
58
+
59
+ graphql_body_response = self.__conviso_graphql_client.execute(
60
+ mutations.CREATE_SAST_FINDING_INPUT,
61
+ graphql_variables
62
+ )
63
+
64
+ expected_path = 'createSastFinding.issue'
65
+
66
+ issue = jmespath.search(
67
+ expected_path,
68
+ graphql_body_response,
69
+ )
70
+
71
+ return issue
72
+
73
+ def create_container(self, container_issue_model: CreateOrUpdateContainerFindingInput):
74
+ graphql_variables = {
75
+ "input": container_issue_model.to_graphql_dict()
76
+ }
77
+
78
+ graphql_body_response = self.__conviso_graphql_client.execute(
79
+ mutations.CREATE_CONTAINER_FINDING_INPUT,
80
+ graphql_variables
81
+ )
82
+
83
+ expected_path = 'createOrUpdateContainerFinding.issue'
84
+
85
+ issue = jmespath.search(
86
+ expected_path,
87
+ graphql_body_response,
88
+ )
89
+
90
+ return issue
91
+
92
+ def auto_close_vulnerabilities(self, company_id, asset_id, statuses, page=1, vulnerability_type=None):
93
+ """ entry point for auto closing vulnerabilities on conviso platform """
94
+ if vulnerability_type is None:
95
+ vulnerability_type = ['SAST_FINDING', 'SCA_FINDING']
96
+
97
+ graphql_variables = {
98
+ 'company_id': company_id,
99
+ 'asset_id': asset_id,
100
+ 'page': page,
101
+ 'per_page': 100,
102
+ 'statuses': statuses,
103
+ 'failure_types': vulnerability_type
104
+ }
105
+
106
+ graphql_body_response = self.__conviso_graphql_client.execute(
107
+ resolvers.GET_ISSUES_FINGERPRINT,
108
+ graphql_variables
109
+ )
110
+
111
+ expected_path = 'issues'
112
+
113
+ issues = jmespath.search(
114
+ expected_path,
115
+ graphql_body_response
116
+ )
117
+
118
+ return issues
119
+
120
+ def update_issue_status(self, issue_id, status, reason, control_sync_status_id):
121
+ """ Update issue status on conviso platform """
122
+
123
+ graphql_variables = {
124
+ 'issueId': issue_id,
125
+ 'status': status,
126
+ 'reason': reason,
127
+ 'controlSyncStatusId': control_sync_status_id
128
+ }
129
+
130
+ graphql_body_response = self.__conviso_graphql_client.execute(
131
+ mutations.UPDATE_ISSUE_STATUS,
132
+ graphql_variables
133
+ )
134
+
135
+ expected_path = 'changeIssueStatus.issue'
136
+
137
+ issue = jmespath.search(
138
+ expected_path,
139
+ graphql_body_response,
140
+ )
141
+
142
+ return issue
@@ -0,0 +1,61 @@
1
+ CREATE_SAST_FINDING_INPUT = """
2
+ mutation createSastFinding($input: CreateSastFindingInput!) {
3
+ createSastFinding(input: $input) {
4
+ issue {
5
+ id
6
+ }
7
+ }
8
+ }
9
+ """
10
+
11
+ CREATE_SCA_FINDING_INPUT = """
12
+ mutation createScaFinding($input: CreateScaFindingInput!) {
13
+ createScaFinding(input: $input) {
14
+ issue {
15
+ id
16
+ }
17
+ }
18
+ }
19
+ """
20
+
21
+ CREATE_IAC_FINDING_INPUT = """
22
+ mutation createOrUpdateIacFinding($input: CreateOrUpdateSastFindingInput!) {
23
+ createOrUpdateIacFinding(input: $input) {
24
+ issue {
25
+ id
26
+ }
27
+ }
28
+ }
29
+ """
30
+
31
+ CREATE_CONTAINER_FINDING_INPUT = """
32
+ mutation createOrUpdateContainerFinding($input: CreateOrUpdateContainerFindingInput!) {
33
+ createOrUpdateContainerFinding(input: $input) {
34
+ issue {
35
+ id
36
+ }
37
+ }
38
+ }
39
+ """
40
+
41
+ UPDATE_ISSUE_STATUS = """
42
+ mutation (
43
+ $issueId: ID!,
44
+ $status: IssueStatusLabel!,
45
+ $reason: String
46
+ $controlSyncStatusId: ID
47
+ ) {
48
+ changeIssueStatus (
49
+ input: {
50
+ id: $issueId
51
+ status: $status
52
+ reason: $reason
53
+ controlSyncStatusId: $controlSyncStatusId
54
+ }
55
+ ) {
56
+ issue {
57
+ id
58
+ }
59
+ }
60
+ }
61
+ """
File without changes