regscale-cli 6.19.2.0__py3-none-any.whl → 6.20.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.

Potentially problematic release.


This version of regscale-cli might be problematic. Click here for more details.

Files changed (27) hide show
  1. regscale/__init__.py +1 -1
  2. regscale/airflow/config.py +2 -0
  3. regscale/airflow/tasks/groups.py +11 -47
  4. regscale/core/app/internal/login.py +49 -43
  5. regscale/core/app/internal/model_editor.py +2 -1
  6. regscale/dev/code_gen.py +2 -5
  7. regscale/integrations/commercial/synqly/assets.py +10 -0
  8. regscale/integrations/public/fedramp/appendix_parser.py +499 -104
  9. regscale/integrations/public/fedramp/fedramp_five.py +89 -43
  10. regscale/models/integration_models/cisa_kev_data.json +171 -21
  11. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  12. regscale/models/regscale_models/__init__.py +5 -0
  13. regscale/models/regscale_models/business_impact_assessment.py +71 -0
  14. regscale/models/regscale_models/control_implementation.py +15 -0
  15. regscale/models/regscale_models/master_assessment.py +19 -0
  16. regscale/models/regscale_models/policy.py +90 -0
  17. regscale/models/regscale_models/question.py +30 -2
  18. regscale/models/regscale_models/questionnaire.py +4 -3
  19. regscale/models/regscale_models/questionnaire_instance.py +37 -14
  20. regscale/models/regscale_models/rbac.py +0 -1
  21. regscale/models/regscale_models/risk_trend.py +67 -0
  22. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.0.0.dist-info}/METADATA +114 -55
  23. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.0.0.dist-info}/RECORD +27 -24
  24. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.0.0.dist-info}/LICENSE +0 -0
  25. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.0.0.dist-info}/WHEEL +0 -0
  26. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.0.0.dist-info}/entry_points.txt +0 -0
  27. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.0.0.dist-info}/top_level.txt +0 -0
regscale/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "6.19.2.0"
1
+ __version__ = "6.20.0.0"
@@ -1,6 +1,7 @@
1
1
  """Provide configurations for Airflow."""
2
2
 
3
3
  from datetime import datetime, timedelta
4
+ from regscale.airflow.tasks.groups import email_on_fail
4
5
 
5
6
 
6
7
  def yesterday():
@@ -18,6 +19,7 @@ DEFAULT_ARGS = {
18
19
  "retries": 2,
19
20
  "retry_delay": timedelta(minutes=2),
20
21
  "execution_timeout": timedelta(hours=3),
22
+ "on_failure_callback": email_on_fail,
21
23
  # 'queue': 'whatever queue we want to implement', # left here for an example
22
24
  # 'pool': 'backfill', # another example default arg
23
25
  # 'priority_weight': 10, # give this a high priority weight
@@ -2,14 +2,10 @@
2
2
 
3
3
  from uuid import uuid4
4
4
 
5
- from airflow import AirflowException, DAG
6
- from airflow.models import TaskInstance
7
5
  from airflow.operators.python import PythonOperator
8
- from airflow.utils.db import provide_session
9
- from airflow.utils.state import State
10
6
  from airflow.utils.task_group import TaskGroup
11
- from airflow.utils.trigger_rule import TriggerRule
12
7
 
8
+ from airflow import AirflowException, DAG
13
9
  from regscale.airflow.hierarchy import AIRFLOW_CLICK_OPERATORS as OPERATORS
14
10
  from regscale.airflow.tasks.click import execute_click_command
15
11
 
@@ -54,63 +50,31 @@ def setup_task_group(
54
50
  return setup
55
51
 
56
52
 
57
- @provide_session
58
- def _email_on_fail(task, execution_date, dag, session=None, **kwargs):
53
+ def email_on_fail(task, **_):
59
54
  """
60
55
  Send an email if any of the upstream DAGs failed
61
56
 
62
- :param DAG dag: The DAG to add the operator to
63
- :param execution_date: The execution date of the DAG
64
- :param session: DAG session
57
+ :param task: The task that has failed
65
58
  :return: The PythonOperator to send an email if any of the DAG tasks fail
66
59
  :rtype: TaskGroup
67
60
  """
68
61
  from regscale.core.app.application import Application
69
62
  from regscale.models import Email
70
63
 
71
- upstream_task_instances = (
72
- session.query(TaskInstance)
73
- .filter(
74
- TaskInstance.dag_id == dag.dag_id,
75
- TaskInstance.execution_date == execution_date,
76
- TaskInstance.task_id.in_(task.upstream_task_ids),
77
- )
78
- .all()
79
- )
80
- upstream_states = [ti.state for ti in upstream_task_instances]
81
- fail_this_task = State.FAILED in upstream_states
82
- dag_config = kwargs["dag_run"].conf
83
- app = Application(config=dag_config)
64
+ dag = task["dag"]
65
+ config = task["params"]
66
+ app = Application(config=config)
84
67
 
85
- if email := kwargs["dag_run"].conf.get("email"):
68
+ if email := config.get("email"):
86
69
  email = Email(
87
70
  fromEmail="Support@RegScale.com",
88
71
  emailSenderId=app.config["userId"],
89
72
  to=email,
90
- subject=f"An Automated Job Has Failed: {dag_config['jobName']} ({dag_config['cadence']})",
91
- body=f"{dag.dag_id} with ID: {dag_config['dag_run_id']} job has failed. "
92
- + f"Please check the logs for more information: {dag_config['job_url']}",
73
+ subject=f"An Automated Job Has Failed: {config['jobName']} ({config['cadence']})",
74
+ body=f"{dag.dag_id} with ID: {task['run_id']} job has failed. "
75
+ + f"Please check the logs for more information: {config['job_url']}",
93
76
  )
94
77
 
95
- if fail_this_task:
96
78
  if email.send():
97
- print(f"Email sent to {email} about {dag.dag_id} job failure.")
79
+ app.logger.info(f"Email sent to {email} about {task['run_id']} job failure.")
98
80
  raise AirflowException("Failing task because one or more upstream tasks failed.")
99
-
100
-
101
- def email_on_fail_operator(dag, email_tag: str = None) -> PythonOperator:
102
- """
103
- Create a PythonOperator to send an email if any of the DAGs fail
104
-
105
- :param DAG dag: The DAG to add the operator to
106
- :param str email_tag: A unique identifier for the task, if not provided one will be generated
107
- :return: The PythonOperator to send an email if any of the DAG tasks fail
108
- :rtype: TaskGroup
109
- """
110
- return PythonOperator(
111
- task_id=generate_name("email", email_tag),
112
- python_callable=_email_on_fail,
113
- trigger_rule=TriggerRule.ALL_DONE,
114
- provide_context=True,
115
- dag=dag,
116
- )
@@ -74,35 +74,47 @@ def login(
74
74
  else:
75
75
  config["domain"] = host or config["domain"]
76
76
  token = token or os.getenv("REGSCALE_TOKEN")
77
+ if token is not None:
78
+ token = token if token.startswith("Bearer ") else f"Bearer {token}"
77
79
  if config and token:
78
- if verify_token(app, token):
79
- config["userId"] = parse_user_id_from_jwt(app, token)
80
- config["token"] = token if token.startswith("Bearer ") else f"Bearer {token}"
81
- if not running_in_airflow:
82
- logger.info("RegScale Token and userId has been saved in init.yaml")
83
- app.save_config(conf=config)
84
- return token
85
- else:
86
- logger.error("Invalid token provided.")
80
+ try:
81
+ if verify_token(app, token):
82
+ config["userId"] = parse_user_id_from_jwt(app, token)
83
+ config["token"] = token
84
+ if not running_in_airflow:
85
+ logger.info("RegScale Token and userId has been saved in init.yaml")
86
+ app.save_config(conf=config)
87
+ return token
88
+ else:
89
+ logger.error("Invalid token provided.")
90
+ sys.exit(1)
91
+ except AttributeError as e:
92
+ logger.error("Unexpected error when verifying token: %s", e)
87
93
  sys.exit(1)
88
94
  from regscale.core.app.api import Api
89
95
 
90
- if str_user and str_password:
91
- if config and "REGSCALE_DOMAIN" not in os.environ and host is None:
92
- host = config["domain"]
93
- regscale_auth = RegScaleAuth.authenticate(
94
- api=Api(),
95
- username=str_user,
96
- password=str_password,
97
- domain=host,
98
- mfa_token=mfa_token,
99
- )
100
- else:
101
- regscale_auth = RegScaleAuth.authenticate(Api(), mfa_token=mfa_token)
102
- if config and config["domain"] is None:
103
- raise ValueError("No domain set in the init.yaml configuration file.")
104
- if config and config["domain"] == "":
105
- raise ValueError("The domain is blank in the init.yaml configuration file.")
96
+ try:
97
+ if str_user and str_password:
98
+ if config and "REGSCALE_DOMAIN" not in os.environ and host is None:
99
+ host = config["domain"]
100
+ regscale_auth = RegScaleAuth.authenticate(
101
+ api=Api(),
102
+ username=str_user,
103
+ password=str_password,
104
+ domain=host,
105
+ mfa_token=mfa_token,
106
+ )
107
+ else:
108
+ regscale_auth = RegScaleAuth.authenticate(Api(), mfa_token=mfa_token)
109
+ if config and config["domain"] is None:
110
+ raise ValueError("No domain set in the init.yaml configuration file.")
111
+ if config and config["domain"] == "":
112
+ raise ValueError("The domain is blank in the init.yaml configuration file.")
113
+ except TypeError as ex:
114
+ logger.error("TypeError: %s", ex)
115
+ except SSLCertVerificationError as sslex:
116
+ logger.error("SSLError, python requests requires a valid ssl certificate.\n%s", sslex)
117
+ sys.exit(1)
106
118
 
107
119
  # create object to authenticate
108
120
  auth = {
@@ -112,24 +124,18 @@ def login(
112
124
  "mfaToken": mfa_token,
113
125
  }
114
126
  if auth["password"]:
115
- try:
116
- # update init file from login
117
- if config:
118
- config["token"] = regscale_auth.token
119
- config["userId"] = regscale_auth.user_id
120
- config["domain"] = regscale_auth.domain
121
- # write the changes back to file
122
- app.save_config(config)
123
- # set variables
124
- logger.info("User ID: %s", regscale_auth.user_id)
125
- logger.info("New RegScale Token has been updated and saved in init.yaml")
126
- # Truncate token for logging purposes
127
- logger.debug("Token: %s", regscale_auth.token[:20])
128
- except TypeError as ex:
129
- logger.error("TypeError: %s", ex)
130
- except SSLCertVerificationError as sslex:
131
- logger.error("SSLError, python requests requires a valid ssl certificate.\n%s", sslex)
132
- sys.exit(1)
127
+ # update init file from login
128
+ if config:
129
+ config["token"] = regscale_auth.token
130
+ config["userId"] = regscale_auth.user_id
131
+ config["domain"] = regscale_auth.domain
132
+ # write the changes back to file
133
+ app.save_config(config)
134
+ # set variables
135
+ logger.info("User ID: %s", regscale_auth.user_id)
136
+ logger.info("New RegScale Token has been updated and saved in init.yaml")
137
+ # Truncate token for logging purposes
138
+ logger.debug("Token: %s", regscale_auth.token[:20])
133
139
  config["domain"] = host
134
140
  app.save_config(config)
135
141
  return regscale_auth.token
@@ -944,10 +944,11 @@ def map_workbook_to_lookups(file_path: str, workbook_data: Optional["pd.DataFram
944
944
  wb_data = workbook_data
945
945
  else:
946
946
  wb_data = pd.read_excel(file_path)
947
+
948
+ wb_data = wb_data.dropna()
947
949
  for cur_row in obj_fields:
948
950
  if len(cur_row.lookup_field) > 0 and cur_row.lookup_field != "module":
949
951
  if cur_row.column_name in wb_data.columns:
950
- wb_data.fillna("None")
951
952
  lookup_wb = pd.read_excel(file_path, sheet_name=cur_row.column_name)
952
953
  if cur_row.lookup_field == "user":
953
954
  lookup_wb = lookup_wb.rename(
regscale/dev/code_gen.py CHANGED
@@ -80,14 +80,13 @@ from airflow import DAG
80
80
 
81
81
  from regscale.airflow.config import DEFAULT_ARGS, yesterday
82
82
  from regscale.airflow.hierarchy import AIRFLOW_CLICK_OPERATORS as OPERATORS
83
- from regscale.airflow.tasks.groups import email_on_fail_operator
84
83
 
85
84
  DAG_NAME = "{integration}"
86
85
 
87
86
  dag = DAG(
88
87
  DAG_NAME,
89
88
  default_args=DEFAULT_ARGS,
90
- schedule_interval=None,
89
+ schedule=None,
91
90
  start_date=yesterday(),
92
91
  description=__doc__,
93
92
  is_paused_upon_creation=False,
@@ -100,9 +99,7 @@ integration_operator = OPERATORS["{integration.split('_')[0].lower()}__sync_{int
100
99
  op_kwargs={{{op_kwargs}\n }},
101
100
  )
102
101
 
103
- email = email_on_fail_operator(dag, email_tag=DAG_NAME)
104
-
105
- integration_operator >> email
102
+ integration_operator
106
103
  """
107
104
  with open(f"{save_dir}/{integration_name.lower()}.py", "w") as f:
108
105
  f.write("# flake8: noqa E501\n# pylint: disable=line-too-long\n")
@@ -53,4 +53,14 @@ def sync_servicenow(regscale_ssp_id: int) -> None:
53
53
  assets_servicenow.run_sync(regscale_ssp_id=regscale_ssp_id)
54
54
 
55
55
 
56
+ @assets.command(name="sync_tanium_cloud")
57
+ @regscale_ssp_id()
58
+ def sync_tanium_cloud(regscale_ssp_id: int) -> None:
59
+ """Sync Assets from Tanium Cloud to RegScale."""
60
+ from regscale.models.integration_models.synqly_models.connectors import Assets
61
+
62
+ assets_tanium_cloud = Assets("tanium_cloud")
63
+ assets_tanium_cloud.run_sync(regscale_ssp_id=regscale_ssp_id)
64
+
65
+
56
66
  # pylint: enable=line-too-long