conviso-ast 3.0.1rc1__py3-none-any.whl → 3.0.1rc2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: conviso-ast
3
- Version: 3.0.1rc1
3
+ Version: 3.0.1rc2
4
4
  Maintainer: Conviso
5
5
  Maintainer-email: development@convisoappsec.com
6
6
  Project-URL: Source, https://github.com/convisoappsec/convisocli/
@@ -1,9 +1,9 @@
1
- conviso_ast-3.0.1rc1.data/scripts/flow_bash_completer.sh,sha256=9q3HPuXq_FCUUV3IFGcOefsOLhPWatUkLY7txiBM7Uo,624
2
- conviso_ast-3.0.1rc1.data/scripts/flow_fish_completer.fish,sha256=-wiuarawDJkms5N-rh99brIOzhy-ktsM1mi1ohQ3Mtg,147
3
- conviso_ast-3.0.1rc1.data/scripts/flow_zsh_completer.sh,sha256=cAtTDGUs5sY4NAA7AjscmLWj0dbNZ9iZhLP6BTz6dEQ,844
1
+ conviso_ast-3.0.1rc2.data/scripts/flow_bash_completer.sh,sha256=9q3HPuXq_FCUUV3IFGcOefsOLhPWatUkLY7txiBM7Uo,624
2
+ conviso_ast-3.0.1rc2.data/scripts/flow_fish_completer.fish,sha256=-wiuarawDJkms5N-rh99brIOzhy-ktsM1mi1ohQ3Mtg,147
3
+ conviso_ast-3.0.1rc2.data/scripts/flow_zsh_completer.sh,sha256=cAtTDGUs5sY4NAA7AjscmLWj0dbNZ9iZhLP6BTz6dEQ,844
4
4
  convisoappsec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  convisoappsec/logger.py,sha256=aTNebqOau9nEadBySMTXtnbGkOkJ_q2kyFlX1mzizeg,1132
6
- convisoappsec/version.py,sha256=Bb5rkX4OTT3aSW-flfnHp0ednpp-iyDmXsLJQoTfb98,27
6
+ convisoappsec/version.py,sha256=Ar9UODgrsoORlQ5GGKFg77QgwYFX2i9wxLv9bnu4Srg,27
7
7
  convisoappsec/common/__init__.py,sha256=QN7tV2C_jhTiWUrJHv2jbeq6ae3MssgLUWpQZwe8O2s,105
8
8
  convisoappsec/common/box.py,sha256=WTtPF3YWxkcdblPmFTzrzQlPPPUwVsDt2zoi6xFMy1U,7561
9
9
  convisoappsec/common/cleaner.py,sha256=Iy8BWCXj_v51oovcYzI_uhaJzLL-fCUyDxrbBglfwEs,2680
@@ -66,7 +66,8 @@ convisoappsec/flowcli/assets/create.py,sha256=JDmxDH1WbyaQYLTxoi6dS_WosRm7kjvpJV
66
66
  convisoappsec/flowcli/assets/entrypoint.py,sha256=CEoev3d1ogclPNP3l31PBhGkoiZZ400kxsnnF02uxFk,310
67
67
  convisoappsec/flowcli/assets/ls.py,sha256=IYfKIca218BvZ5ecrtDgZ_T8xVg0XO3Eq1kK16BMBsc,1484
68
68
  convisoappsec/flowcli/ast/__init__.py,sha256=9SO9inH22PPm4jLlljVTJEeJKZujFKfwqTBbBo-TwFM,47
69
- convisoappsec/flowcli/ast/entrypoint.py,sha256=pwkXn9R1FNuUlTGrwUIMCacpklYSFR9BFDB59a_ivcU,15946
69
+ convisoappsec/flowcli/ast/dry_run.py,sha256=pb0eM-jXE83UpWTBcw3ulVSgY0WhWvWbcb-58j2YjC4,3588
70
+ convisoappsec/flowcli/ast/entrypoint.py,sha256=HI02I6r8v2cBYwCeOfQZLtFjGhTl3dF_83Tu2FXCM8Y,15999
70
71
  convisoappsec/flowcli/companies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
72
  convisoappsec/flowcli/companies/ls.py,sha256=0287VTXOkKnuXTaJh392crYjx0qank0pBvmESq3P_Bw,892
72
73
  convisoappsec/flowcli/container/__init__.py,sha256=Z666kQa4qVfbcuyLaW_hvORdAPtZ9dTQFcQb2H2Nkfg,59
@@ -96,16 +97,19 @@ convisoappsec/flowcli/findings/create/with_/version_tracker.py,sha256=NrnYx6OMQW
96
97
  convisoappsec/flowcli/findings/import_sarif/__init__.py,sha256=3iR7f9V9NdoIQTrD25EVu6OAYPPJ4RZEaM4P34BS0js,66
97
98
  convisoappsec/flowcli/findings/import_sarif/entrypoint.py,sha256=MIHi9ZWU2Jn2o7o5mDLp81xfb14gsa33aGnqh1YR7n8,13740
98
99
  convisoappsec/flowcli/iac/__init__.py,sha256=a3IZzSKpm987fMEliTECDeXO_Eduk7eg-aQzmaWvUXQ,47
99
- convisoappsec/flowcli/iac/entrypoint.py,sha256=vGsHdIss3KEWartTZkKl7k5w1qxhYIVqHGOJdklc3b8,241
100
- convisoappsec/flowcli/iac/run.py,sha256=_yNmw4L7tka6x7NnO_s56GRXrGv93RYCp0I2M22X7_M,11074
100
+ convisoappsec/flowcli/iac/dry_run.py,sha256=xvC0Wb2Sxl37yEjFaFMGcRvLJ4Q8evrNQoUXuahHtCg,2990
101
+ convisoappsec/flowcli/iac/entrypoint.py,sha256=WMlSwHd7cLxhUfy643OquQhUHEy4yNf7s0K5bkrXYno,295
102
+ convisoappsec/flowcli/iac/run.py,sha256=bpE3GF-ls-kUJhcxNinxY2eNBWEamZpSj-7nreRJYIQ,11202
101
103
  convisoappsec/flowcli/sast/__init__.py,sha256=S4O78eZGhgpT2lZY3GSUIUTQJB5a62uAVirEqbf4EQY,49
102
- convisoappsec/flowcli/sast/entrypoint.py,sha256=7bvQ6mVLWvykGfjD-o0xbu7ybm8Hj2DTqvCv5CstIas,245
104
+ convisoappsec/flowcli/sast/dry_run.py,sha256=lUi9LCfBlBjcAYm4v_Etl3raPZcTHVr5fVxGnB1eVSM,5787
105
+ convisoappsec/flowcli/sast/entrypoint.py,sha256=XMu8WpZNwSujWOwbHUThk3JK_WtRWHau9kYD2ttjULY,300
103
106
  convisoappsec/flowcli/sast/run.py,sha256=7uX6K1VLASU8RnoCKNYIIqXSsNkF4JQ8OKhAG2CKjuI,16353
104
107
  convisoappsec/flowcli/sbom/__init__.py,sha256=qzwiPWniK41Y41XvJJhxtRZguHrSSenb57lhy64KnSc,49
105
108
  convisoappsec/flowcli/sbom/entrypoint.py,sha256=ax2WL5BtCu24NeZYe6zzCVZ2ujlviXkvtY7PZihlz3s,263
106
- convisoappsec/flowcli/sbom/generate.py,sha256=Oke4JX4vARqpsMlGC2cPye_LMapIMRDyZv2W14hoD8g,7170
109
+ convisoappsec/flowcli/sbom/generate.py,sha256=YEhl4fVRXRscSApTMkTynyka3ygsVgJD7vhUHipUp18,7190
107
110
  convisoappsec/flowcli/sca/__init__.py,sha256=xnVoxwbpe4LrEemmWJ6svr3zdpo9S4kEIvM4HVRsLX8,47
108
- convisoappsec/flowcli/sca/entrypoint.py,sha256=LpFoiZ3iljhAyKzfMJ7pdBdHIUhnjh3ZguPgvWJZ-XA,241
111
+ convisoappsec/flowcli/sca/dry_run.py,sha256=fZ_tgc1k8Mn1JMznFWyi_CExR-gpLHLHwQ2erNldnHY,3438
112
+ convisoappsec/flowcli/sca/entrypoint.py,sha256=yEAANSG2fcD3HctUWebhx_win3CW-1Q2CO1i-ZIBUCk,295
109
113
  convisoappsec/flowcli/sca/run.py,sha256=FWnTHgMgnIRvJSpMIkFcb6YMtvWEjEpKJyXEkjAZpA4,16725
110
114
  convisoappsec/flowcli/vulnerability/__init__.py,sha256=c18E0J1KfZBBqpv8XGxF5dv7dxCDquFkjGkdnvFDSYI,67
111
115
  convisoappsec/flowcli/vulnerability/assert_security_rules.py,sha256=j7VcondMeZSRxWz118aWS9qUSGdvsq4Mg-_9UXp8Dyw,6075
@@ -116,8 +120,8 @@ convisoappsec/flowcli/vulnerability/run.py,sha256=6flmvr55cKxj-duX3w4PAFwJgC5AJ0
116
120
  convisoappsec/sast/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
121
  convisoappsec/sast/decision.py,sha256=d7dcNr9yZMzyccpFS_peAmDo0ZtfsE1qXDdYrvCux2U,1025
118
122
  convisoappsec/sast/sastbox.py,sha256=hXZLiYh_F3f6yd1ydPYVOMKg-tNOQOZiBvKmWyedagI,11031
119
- conviso_ast-3.0.1rc1.dist-info/METADATA,sha256=pWFwacLO6PwSQbd1OcCihNv7HRUCyoYlr_OcqONtYA4,1078
120
- conviso_ast-3.0.1rc1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
121
- conviso_ast-3.0.1rc1.dist-info/entry_points.txt,sha256=0IvamweR_V0uG4O5Fo9NpVHTHfpZRwUE9kn7KEVZ668,109
122
- conviso_ast-3.0.1rc1.dist-info/top_level.txt,sha256=ju5r0RSCF1HA7m9JOG10jrQS4SnqQEJzl6-YMCxbSl4,14
123
- conviso_ast-3.0.1rc1.dist-info/RECORD,,
123
+ conviso_ast-3.0.1rc2.dist-info/METADATA,sha256=4FhXjj2DrA123b7c8QnscWGxgzgBRdDDO_gfpLkjBZs,1078
124
+ conviso_ast-3.0.1rc2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
125
+ conviso_ast-3.0.1rc2.dist-info/entry_points.txt,sha256=0IvamweR_V0uG4O5Fo9NpVHTHfpZRwUE9kn7KEVZ668,109
126
+ conviso_ast-3.0.1rc2.dist-info/top_level.txt,sha256=ju5r0RSCF1HA7m9JOG10jrQS4SnqQEJzl6-YMCxbSl4,14
127
+ conviso_ast-3.0.1rc2.dist-info/RECORD,,
@@ -0,0 +1,99 @@
1
+ import click
2
+ import json
3
+ import traceback
4
+ import sys
5
+ from convisoappsec.flowcli import help_option
6
+ from convisoappsec.flowcli.context import pass_flow_context
7
+ from convisoappsec.logger import LOGGER
8
+ from convisoappsec.flowcli.common import on_http_error
9
+ from convisoappsec.common.cleaner import Cleaner
10
+ from convisoappsec.sast.sastbox import SASTBox
11
+ from convisoappsec.flowcli.sast.dry_run import execute_dry_run as execute_sast_dry_run
12
+ from convisoappsec.flowcli.sca.dry_run import execute_dry_run as execute_sca_dry_run
13
+ from convisoappsec.flowcli.iac.dry_run import execute_dry_run as execute_iac_dry_run
14
+
15
+ @click.command(name='dry-run')
16
+ @click.option(
17
+ "-s", "--start-commit", required=False,
18
+ help="If no value is set so the empty tree hash commit is used."
19
+ )
20
+ @click.option(
21
+ "-e", "--end-commit", required=False,
22
+ help="If no value is set so the HEAD commit from the current branch is used"
23
+ )
24
+ @click.option(
25
+ "-r", "--repository-dir", default=".", show_default=True,
26
+ type=click.Path(exists=True, resolve_path=True), required=False,
27
+ help="The source code repository directory."
28
+ )
29
+ @click.option(
30
+ "--sastbox-registry", default="", required=False, hidden=True,
31
+ envvar=("CONVISO_SASTBOX_REGISTRY", "FLOW_SASTBOX_REGISTRY"),
32
+ )
33
+ @click.option(
34
+ "--sastbox-repository-name", default="", required=False, hidden=True,
35
+ envvar=("CONVISO_SASTBOX_REPOSITORY_NAME", "FLOW_SASTBOX_REPOSITORY_NAME"),
36
+ )
37
+ @click.option(
38
+ "--sastbox-tag", default=SASTBox.DEFAULT_TAG, required=False, hidden=True,
39
+ envvar=("CONVISO_SASTBOX_TAG", "FLOW_SASTBOX_TAG"),
40
+ )
41
+ @click.option(
42
+ "--sastbox-skip-login/--sastbox-no-skip-login", default=False, required=False, hidden=True,
43
+ envvar=("CONVISO_SASTBOX_SKIP_LOGIN", "FLOW_SASTBOX_SKIP_LOGIN"),
44
+ )
45
+ @click.option(
46
+ "--custom-sca-tags", hidden=True, required=False, multiple=True, type=(str, str),
47
+ help="It should be passed as <repository_name> <image_tag>."
48
+ )
49
+ @click.option(
50
+ "--scanner-timeout", hidden=True, required=False, default=7200, type=int,
51
+ help="Set timeout for each scanner"
52
+ )
53
+ @click.option(
54
+ '--cleanup', default=False, is_flag=True, show_default=True,
55
+ help="Clean up system resources."
56
+ )
57
+ @help_option
58
+ @pass_flow_context
59
+ def dry_run(flow_context, end_commit, start_commit, repository_dir,
60
+ sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login,
61
+ custom_sca_tags, scanner_timeout, cleanup):
62
+ """
63
+ Perform a dry-run AST analysis (SAST, SCA, IaC).
64
+ Checks API Key, runs the scans, and outputs the results in JSON format to stdout.
65
+ Does NOT create assets or deploys on Conviso Platform.
66
+ """
67
+ try:
68
+ results = {}
69
+
70
+ # Run SAST
71
+ sast_results = execute_sast_dry_run(
72
+ flow_context, end_commit, start_commit, repository_dir,
73
+ sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login
74
+ )
75
+ results['sast'] = sast_results
76
+
77
+ # Run SCA
78
+ sca_results = execute_sca_dry_run(
79
+ flow_context, repository_dir, custom_sca_tags, scanner_timeout
80
+ )
81
+ results['sca'] = sca_results
82
+
83
+ # Run IaC
84
+ iac_results = execute_iac_dry_run(
85
+ flow_context, repository_dir, scanner_timeout
86
+ )
87
+ results['iac'] = iac_results
88
+
89
+ print(json.dumps(results, indent=2))
90
+
91
+ if cleanup:
92
+ LOGGER.info("🧹 Cleaning up ...")
93
+ cleaner = Cleaner()
94
+ cleaner.cleanup()
95
+
96
+ except Exception as e:
97
+ traceback.print_exc(file=sys.stderr)
98
+ on_http_error(e)
99
+ sys.exit(1)
@@ -15,7 +15,7 @@ from convisoappsec.flow import GitAdapter
15
15
  from convisoappsec.flowcli.context import pass_flow_context
16
16
  from convisoappsec.logger import LOGGER, log_and_notify_ast_event
17
17
  from convisoappsec.common.cleaner import Cleaner
18
-
18
+ from .dry_run import dry_run
19
19
 
20
20
  def get_default_params_values(cmd_params):
21
21
  """ Further information in https://click.palletsprojects.com/en/8.1.x/api/?highlight=params#click.Command.params
@@ -425,3 +425,4 @@ def ast():
425
425
 
426
426
 
427
427
  ast.add_command(run)
428
+ ast.add_command(dry_run)
@@ -0,0 +1,94 @@
1
+ import click
2
+ import click_log
3
+ import json
4
+ import traceback
5
+ import sys
6
+ from convisoappsec.common.box import ContainerWrapper
7
+ from convisoappsec.flowcli import help_option
8
+ from convisoappsec.flowcli.context import pass_flow_context
9
+ from convisoappsec.logger import LOGGER
10
+ from convisoappsec.flowcli.common import on_http_error
11
+ from convisoappsec.common.cleaner import Cleaner
12
+
13
+ def execute_dry_run(flow_context, repository_dir, scanner_timeout):
14
+ REQUIRED_CODEBASE_PATH = '/code'
15
+ IAC_IMAGE_NAME = 'iac_scanner_checkov'
16
+ IAC_SCAN_FILENAME = '/{}.json'.format(IAC_IMAGE_NAME)
17
+ containers_map = {
18
+ IAC_IMAGE_NAME: {
19
+ 'repository_dir': repository_dir,
20
+ 'repository_name': IAC_IMAGE_NAME,
21
+ 'tag': 'unstable',
22
+ 'command': [
23
+ '-c', REQUIRED_CODEBASE_PATH,
24
+ '-o', IAC_SCAN_FILENAME,
25
+ ],
26
+ },
27
+ }
28
+
29
+ conviso_rest_api = flow_context.create_conviso_rest_api_client()
30
+ token = conviso_rest_api.docker_registry.get_sast_token()
31
+
32
+ LOGGER.info('💬 Preparing Environment...')
33
+ scanners_wrapper = ContainerWrapper(
34
+ token=token,
35
+ containers_map=containers_map,
36
+ logger=LOGGER,
37
+ timeout=scanner_timeout
38
+ )
39
+
40
+ LOGGER.info('💬 Starting IaC...')
41
+ scanners_wrapper.run()
42
+
43
+ results_list = []
44
+ for r in scanners_wrapper.scanners:
45
+ report_filepath = r.results
46
+ if report_filepath:
47
+ try:
48
+ with open(report_filepath, 'r') as f:
49
+ results_list.append(json.load(f))
50
+ except Exception as e:
51
+ click.echo(f"Error reading result file {report_filepath}: {e}", file=sys.stderr)
52
+
53
+ if len(results_list) == 1:
54
+ return results_list[0]
55
+ return results_list
56
+
57
+ @click.command(name='dry-run')
58
+ @click.option(
59
+ '-r', '--repository-dir', default=".", show_default=True,
60
+ type=click.Path(exists=True, resolve_path=True), required=False,
61
+ help="The source code repository directory."
62
+ )
63
+ @click.option(
64
+ "--scanner-timeout", hidden=True, required=False, default=7200, type=int,
65
+ help="Set timeout for each scanner"
66
+ )
67
+ @click.option(
68
+ '--cleanup', default=False, is_flag=True, show_default=True,
69
+ help="Clean up system resources."
70
+ )
71
+ @help_option
72
+ @pass_flow_context
73
+ def dry_run(flow_context, repository_dir, scanner_timeout, cleanup):
74
+ """
75
+ Perform a dry-run IAC analysis.
76
+ Checks API Key, runs the scan, and outputs the results in JSON format to stdout.
77
+ Does NOT create assets or deploys on Conviso Platform.
78
+ """
79
+ try:
80
+ results = execute_dry_run(flow_context, repository_dir, scanner_timeout)
81
+
82
+ if results:
83
+ print(json.dumps(results, indent=2))
84
+ else:
85
+ print(json.dumps({}, indent=2))
86
+
87
+ if cleanup:
88
+ LOGGER.info("🧹 Cleaning up ...")
89
+ cleaner = Cleaner()
90
+ cleaner.cleanup()
91
+
92
+ except Exception as e:
93
+ on_http_error(e)
94
+ sys.exit(1)
@@ -2,6 +2,7 @@ import click
2
2
 
3
3
  from convisoappsec.flowcli import help_option
4
4
  from .run import run
5
+ from .dry_run import dry_run
5
6
 
6
7
 
7
8
  @click.group()
@@ -11,6 +12,7 @@ def iac():
11
12
 
12
13
 
13
14
  iac.add_command(run)
15
+ iac.add_command(dry_run)
14
16
 
15
17
  iac.epilog = '''
16
18
  Run flow iac COMMAND --help for more information on a command.
@@ -181,6 +181,8 @@ def deploy_results_to_conviso(
181
181
  except ResponseError as error:
182
182
  if error.code == 'RECORD_NOT_UNIQUE':
183
183
  continue
184
+ elif error.code == "Record not found" or "Record not found" in str(error):
185
+ continue
184
186
  else:
185
187
  retry_handler = RetryHandler(
186
188
  flow_context=flow_context, company_id=company_id, asset_id=asset_id
@@ -0,0 +1,159 @@
1
+ import sys
2
+ import click
3
+ import traceback
4
+ import json
5
+ from convisoappsec.sast.sastbox import SASTBox
6
+ from docker.errors import APIError
7
+ import time
8
+ from convisoappsec.flow import GitAdapter
9
+ from convisoappsec.flowcli import help_option
10
+ from convisoappsec.flowcli.context import pass_flow_context
11
+ from convisoappsec.logger import LOGGER
12
+ from convisoappsec.common.cleaner import Cleaner
13
+ from convisoappsec.flowcli.common import on_http_error
14
+
15
+ class DryRunSASTBox(SASTBox):
16
+ def recovery_technologies_file(self):
17
+ # Skip technology recovery and update for dry-run
18
+ pass
19
+
20
+ def perform_dry_run_sastbox_scan(
21
+ conviso_rest_api, sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login, repository_dir, end_commit, start_commit, logger
22
+ ):
23
+ max_retries = 5
24
+ retries = 0
25
+ sastbox = DryRunSASTBox(registry=sastbox_registry, repository_name=sastbox_repository_name, tag=sastbox_tag)
26
+ pull_progress_bar = click.progressbar(length=sastbox.size, label="Performing SAST download...")
27
+
28
+ while retries < max_retries:
29
+ try:
30
+ if not sastbox_skip_login:
31
+ logger("Checking SASTBox authorization...")
32
+ token = conviso_rest_api.docker_registry.get_sast_token()
33
+ sastbox.login(token)
34
+
35
+ with pull_progress_bar as progressbar:
36
+ for downloaded_chunk in sastbox.pull():
37
+ progressbar.update(downloaded_chunk)
38
+ break
39
+ except APIError as e:
40
+ retries += 1
41
+ logger(f"Retrying {retries}/{max_retries}...")
42
+ time.sleep(1)
43
+
44
+ if retries == max_retries:
45
+ logger("Max retries reached. Failed to perform SAST download.")
46
+ raise Exception(f"Max retries reached. Could not complete the SAST download. Error: {str(e)}")
47
+
48
+ logger("Starting SAST scan diff...")
49
+
50
+ reports = sastbox.run_scan_diff(repository_dir, end_commit, start_commit, log=logger)
51
+
52
+ logger("SAST scan diff done.")
53
+
54
+ results_filepaths = []
55
+ for r in reports:
56
+ try:
57
+ file_path = str(r)
58
+ results_filepaths.append(file_path)
59
+ except Exception as e:
60
+ click.echo(f"Error decoding file path: {r} with error {e}.", file=sys.stderr)
61
+
62
+ return results_filepaths
63
+
64
+ def log_func(msg, new_line=True):
65
+ click.echo(msg, nl=new_line, err=True)
66
+
67
+ def execute_dry_run(flow_context, end_commit, start_commit, repository_dir,
68
+ sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login):
69
+ git_adapter = GitAdapter(repository_dir)
70
+ end_commit = end_commit or git_adapter.head_commit
71
+ start_commit = start_commit or git_adapter.empty_repository_tree_commit
72
+
73
+ if start_commit == end_commit:
74
+ return {}
75
+
76
+ conviso_rest_api = flow_context.create_conviso_rest_api_client()
77
+
78
+ results_filepaths = perform_dry_run_sastbox_scan(
79
+ conviso_rest_api, sastbox_registry, sastbox_repository_name, sastbox_tag,
80
+ sastbox_skip_login, repository_dir, end_commit, start_commit, log_func
81
+ )
82
+
83
+ results_list = []
84
+ for path in results_filepaths:
85
+ try:
86
+ with open(path, 'r') as f:
87
+ results_list.append(json.load(f))
88
+ except Exception as e:
89
+ click.echo(f"Error reading result file {path}: {e}", file=sys.stderr)
90
+
91
+ if len(results_list) == 1:
92
+ return results_list[0]
93
+ return results_list
94
+
95
+ @click.command(name='dry-run')
96
+ @click.option(
97
+ "-s", "--start-commit", required=False,
98
+ help="If no value is set so the empty tree hash commit is used."
99
+ )
100
+ @click.option(
101
+ "-e", "--end-commit", required=False,
102
+ help="If no value is set so the HEAD commit from the current branch is used"
103
+ )
104
+ @click.option(
105
+ "-r", "--repository-dir", default=".", show_default=True,
106
+ type=click.Path(exists=True, resolve_path=True), required=False,
107
+ help="The source code repository directory."
108
+ )
109
+ @click.option(
110
+ "--sastbox-registry", default="", required=False, hidden=True,
111
+ envvar=("CONVISO_SASTBOX_REGISTRY", "FLOW_SASTBOX_REGISTRY"),
112
+ )
113
+ @click.option(
114
+ "--sastbox-repository-name", default="", required=False, hidden=True,
115
+ envvar=("CONVISO_SASTBOX_REPOSITORY_NAME", "FLOW_SASTBOX_REPOSITORY_NAME"),
116
+ )
117
+ @click.option(
118
+ "--sastbox-tag", default=SASTBox.DEFAULT_TAG, required=False, hidden=True,
119
+ envvar=("CONVISO_SASTBOX_TAG", "FLOW_SASTBOX_TAG"),
120
+ )
121
+ @click.option(
122
+ "--sastbox-skip-login/--sastbox-no-skip-login", default=False, required=False, hidden=True,
123
+ envvar=("CONVISO_SASTBOX_SKIP_LOGIN", "FLOW_SASTBOX_SKIP_LOGIN"),
124
+ )
125
+ @click.option(
126
+ '--cleanup', default=False, is_flag=True, show_default=True,
127
+ help="Clean up system resources."
128
+ )
129
+ @click.option(
130
+ "-o", "--output", required=False, help="Output the results to a JSON file."
131
+ )
132
+ @help_option
133
+ @pass_flow_context
134
+ def dry_run(flow_context, end_commit, start_commit, repository_dir,
135
+ sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login, cleanup, output):
136
+ try:
137
+ results = execute_dry_run(
138
+ flow_context, end_commit, start_commit, repository_dir,
139
+ sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login
140
+ )
141
+
142
+ if output:
143
+ with open(output, "w") as f:
144
+ json.dump(results if results else {}, f, indent=2)
145
+ LOGGER.info(f"Results saved to {output}")
146
+ elif results:
147
+ print(json.dumps(results, indent=2))
148
+ else:
149
+ print(json.dumps({}, indent=2))
150
+
151
+ if cleanup:
152
+ LOGGER.info("🧹 Cleaning up ...")
153
+ cleaner = Cleaner()
154
+ cleaner.cleanup()
155
+
156
+ except Exception as e:
157
+ traceback.print_exc(file=sys.stderr)
158
+ on_http_error(e)
159
+ sys.exit(1)
@@ -2,6 +2,7 @@ import click
2
2
 
3
3
  from convisoappsec.flowcli import help_option
4
4
  from .run import run
5
+ from .dry_run import dry_run
5
6
 
6
7
 
7
8
  @click.group()
@@ -11,6 +12,7 @@ def sast():
11
12
 
12
13
 
13
14
  sast.add_command(run)
15
+ sast.add_command(dry_run)
14
16
 
15
17
  sast.epilog = '''
16
18
  Run flow sast COMMAND --help for more information on a command.
@@ -182,7 +182,7 @@ def generate(context, flow_context, asset_id, company_id, repository_dir, send_t
182
182
  stderr=subprocess.DEVNULL
183
183
  )
184
184
  command = [f"./conviso/syft scan {repository_dir} -o cyclonedx-json={file_name} "
185
- f"--select-catalogers '{','.join(catalogers)}'"]
185
+ f"--select-catalogers '{','.join(catalogers)}' --exclude ./conviso"]
186
186
 
187
187
  subprocess.run(command, shell=True, check=True, capture_output=True)
188
188
 
@@ -0,0 +1,108 @@
1
+ import click
2
+ import click_log
3
+ import traceback
4
+ import json
5
+ import sys
6
+ from convisoappsec.common.box import ContainerWrapper
7
+ from convisoappsec.flowcli import help_option
8
+ from convisoappsec.flowcli.context import pass_flow_context
9
+ from convisoappsec.logger import LOGGER
10
+ from convisoappsec.flowcli.common import on_http_error
11
+ from convisoappsec.common.cleaner import Cleaner
12
+
13
+ def log_func(msg, new_line=True):
14
+ click.echo(msg, nl=new_line, err=True)
15
+
16
+ def execute_dry_run(flow_context, repository_dir, custom_sca_tags, scanner_timeout):
17
+ REQUIRED_CODEBASE_PATH = '/code'
18
+ OSV_SCANNER_IMAGE_NAME = 'osv_scanner'
19
+
20
+ scanners = {
21
+ OSV_SCANNER_IMAGE_NAME: {
22
+ 'repository_name': OSV_SCANNER_IMAGE_NAME,
23
+ 'tag': 'latest',
24
+ 'command': [
25
+ '-c', REQUIRED_CODEBASE_PATH,
26
+ '-f', 'json',
27
+ '-o', '/{}.json'.format(OSV_SCANNER_IMAGE_NAME)
28
+ ],
29
+ 'repository_dir': repository_dir
30
+ },
31
+ }
32
+
33
+ if custom_sca_tags:
34
+ for custom_tag in custom_sca_tags:
35
+ scan_name, tag = custom_tag
36
+ if scan_name in scanners.keys():
37
+ scanners[scan_name]['tag'] = tag
38
+
39
+ conviso_rest_api = flow_context.create_conviso_rest_api_client()
40
+ token = conviso_rest_api.docker_registry.get_sast_token()
41
+
42
+ LOGGER.info('💬 Preparing Environment...')
43
+ scabox = ContainerWrapper(
44
+ token=token,
45
+ containers_map=scanners,
46
+ logger=LOGGER,
47
+ timeout=scanner_timeout
48
+ )
49
+ LOGGER.info('💬 Starting SCA...')
50
+ scabox.run()
51
+
52
+ results_list = []
53
+ for unit in scabox.scanners:
54
+ file_path = unit.results
55
+ if file_path:
56
+ try:
57
+ with open(file_path, 'r') as f:
58
+ results_list.append(json.load(f))
59
+ except Exception as e:
60
+ click.echo(f"Error reading result file {file_path}: {e}", file=sys.stderr)
61
+
62
+ if len(results_list) == 1:
63
+ return results_list[0]
64
+ return results_list
65
+
66
+
67
+ @click.command(name='dry-run')
68
+ @click.option(
69
+ '-r', '--repository-dir', default=".", show_default=True,
70
+ type=click.Path(exists=True, resolve_path=True), required=False,
71
+ help="The source code repository directory."
72
+ )
73
+ @click.option(
74
+ "--custom-sca-tags", hidden=True, required=False, multiple=True, type=(str, str),
75
+ help="It should be passed as <repository_name> <image_tag>."
76
+ )
77
+ @click.option(
78
+ "--scanner-timeout", hidden=True, required=False, default=7200, type=int,
79
+ help="Set timeout for each scanner"
80
+ )
81
+ @click.option(
82
+ '--cleanup', default=False, is_flag=True, show_default=True,
83
+ help="Clean up system resources."
84
+ )
85
+ @help_option
86
+ @pass_flow_context
87
+ def dry_run(flow_context, repository_dir, custom_sca_tags, scanner_timeout, cleanup):
88
+ """
89
+ Perform a dry-run SCA analysis.
90
+ Checks API Key, runs the scan, and outputs the results in JSON format to stdout.
91
+ Does NOT create assets or deploys on Conviso Platform.
92
+ """
93
+ try:
94
+ results = execute_dry_run(flow_context, repository_dir, custom_sca_tags, scanner_timeout)
95
+
96
+ if results:
97
+ print(json.dumps(results, indent=2))
98
+ else:
99
+ print(json.dumps({}, indent=2))
100
+
101
+ if cleanup:
102
+ LOGGER.info("🧹 Cleaning up ...")
103
+ cleaner = Cleaner()
104
+ cleaner.cleanup()
105
+
106
+ except Exception as e:
107
+ on_http_error(e)
108
+ sys.exit(1)
@@ -2,6 +2,7 @@ import click
2
2
 
3
3
  from convisoappsec.flowcli import help_option
4
4
  from .run import run
5
+ from .dry_run import dry_run
5
6
 
6
7
 
7
8
  @click.group()
@@ -11,6 +12,7 @@ def sca():
11
12
 
12
13
 
13
14
  sca.add_command(run)
15
+ sca.add_command(dry_run)
14
16
 
15
17
  sca.epilog = '''
16
18
  Run flow sca COMMAND --help for more information on a command.
convisoappsec/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '3.0.1-rc.1'
1
+ __version__ = '3.0.1-rc.2'