Skip to content

Commit

Permalink
chore: fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
somehowchris committed Jan 28, 2025
1 parent d7178c7 commit 868dd17
Show file tree
Hide file tree
Showing 27 changed files with 576 additions and 258 deletions.
6 changes: 5 additions & 1 deletion src/riskmatrix/layouts/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ def steps(context: 'Organization', request: 'IRequest') -> 'RenderData':
'#',
disabled=True
),
Step(_("Finish Assessment"), request.route_url('finish_assessment'), disabled=False),
Step(
_("Finish Assessment"),
request.route_url('finish_assessment'),
disabled=False
),
]
}

Expand Down
8 changes: 6 additions & 2 deletions src/riskmatrix/mail/mailer.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,12 @@ def create_or_update_template(
name = f'[{self.stream}] {organization.name}: {template.name}'
if len(name) > 100:
name = name[:97] + '...'
html_content: str = markdown_to_html(template.email_content)
plain_content = markdown_to_plaintext(template.email_content)
html_content: str = markdown_to_html( # noqa: F821
template.email_content
)
plain_content = markdown_to_plaintext( # noqa: F821
template.email_content
)

# replace logos with appropriate placeholders.
html_content = html_content.replace(
Expand Down
2 changes: 1 addition & 1 deletion src/riskmatrix/mail/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,4 @@ class TemplateMailParams(
RequiredTemplateMailParams,
OptionalTemplateMailParams
):
pass
pass
2 changes: 1 addition & 1 deletion src/riskmatrix/models/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class Asset(Base, SoftDeleteMixin, SerializerMixin):
assessments: Mapped[list['RiskAssessment']] = relationship(
back_populates='asset'
)

organization: Mapped['Organization'] = relationship(
back_populates='assets'
)
Expand Down
2 changes: 1 addition & 1 deletion src/riskmatrix/models/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Organization(Base):
risks: Mapped[list['Risk']] = relationship(
back_populates='organization',
)
risk_catalogs: Mapped[list['RiskCatalog']] = relationship(
risk_catalogs: Mapped[list[RiskCatalog]] = relationship(
back_populates='organization',
)
users: Mapped[list['User']] = relationship(
Expand Down
2 changes: 1 addition & 1 deletion src/riskmatrix/models/password_change_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,4 @@ def expired(self) -> bool:
expiring_time = self.time_requested + timedelta(hours=48)
if datetime.utcnow() > expiring_time:
return True
return False
return False
1 change: 1 addition & 0 deletions src/riskmatrix/models/risk.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

from sqlalchemy_serializer import SerializerMixin


class Risk(SoftDeleteMixin, Base, SerializerMixin):

__tablename__ = 'risk'
Expand Down
3 changes: 1 addition & 2 deletions src/riskmatrix/models/risk_assessment.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from riskmatrix.orm.meta import UUIDStr
from riskmatrix.orm.meta import UUIDStrPK
from sqlalchemy.types import JSON
from dataclasses import dataclass
from sqlalchemy_serializer import SerializerMixin

from typing import Any, ClassVar
Expand Down Expand Up @@ -82,7 +81,7 @@ def __init__(
self,
asset: Asset,
risk: Risk,
info: 'RiskAssessmentInfo',
info: RiskAssessmentInfo,
**meta: Any
):
self.id = str(uuid4())
Expand Down
10 changes: 2 additions & 8 deletions src/riskmatrix/models/risk_assessment_info.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from dataclasses import dataclass
from sedate import utcnow
from riskmatrix.orm.meta import Base
import enum
Expand All @@ -8,16 +7,13 @@
from sqlalchemy.orm import Mapped
from riskmatrix.orm.meta import UUIDStr
from riskmatrix.orm.meta import UUIDStrPK
from sqlalchemy import UniqueConstraint
from sqlalchemy import Column
from sqlalchemy_serializer import SerializerMixin
from datetime import datetime

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from riskmatrix.models import RiskAssessment




class RiskAssessmentState(enum.Enum):
Expand All @@ -31,8 +27,6 @@ def __str__(self) -> str:
}
return names[self]

from sqlalchemy_serializer import SerializerMixin


class RiskAssessmentInfo(Base, SerializerMixin):

Expand All @@ -43,7 +37,7 @@ class RiskAssessmentInfo(Base, SerializerMixin):
ForeignKey('organization.id', ondelete='CASCADE'),
index=True,
)

name: Mapped[str] = mapped_column(nullable=True)

state: Mapped[RiskAssessmentState] = mapped_column(
Expand Down
4 changes: 3 additions & 1 deletion src/riskmatrix/models/risk_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ class RiskCatalog(Base, SoftDeleteMixin, SerializerMixin):
modified: Mapped[datetime | None] = mapped_column(onupdate=utcnow)

risks: Mapped[list['Risk']] = relationship(back_populates='catalog')
organization: Mapped['Organization'] = relationship(back_populates='risk_catalogs')
organization: Mapped['Organization'] = relationship(
back_populates='risk_catalogs'
)

def __init__(
self,
Expand Down
Empty file.
19 changes: 9 additions & 10 deletions src/riskmatrix/orm/softdelete_base.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
from sqlalchemy_easy_softdelete.mixin import generate_soft_delete_mixin_class
from sqlalchemy_easy_softdelete.hook import IgnoredTable
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer
from datetime import datetime

from sqlalchemy.sql import func

# Create a Class that inherits from our class builder
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from datetime import datetime


class SoftDeleteMixin(generate_soft_delete_mixin_class(
# This table will be ignored by the hook
# even if the table has the soft-delete column
ignored_tables=[]
)):
# type hint for autocomplete IDE support
deleted_at: datetime
deleted_at: 'datetime'

def soft_delete(self):
self.deleted_at = func.now()
self.deleted_at = func.now()
17 changes: 17 additions & 0 deletions src/riskmatrix/prompts/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import json
from pathlib import Path


def load_json_file(filename: str) -> dict:
"""Load a JSON file from the prompts directory."""
current_dir = Path(__file__).parent
file_path = current_dir / filename

with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)


# Load all prompt configurations
system_prompts = load_json_file('system_prompts.json')
user_prompts = load_json_file('user_prompts.json')
examples = load_json_file('examples.json')
50 changes: 50 additions & 0 deletions src/riskmatrix/prompts/examples.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"risks": [
{
"name": "Hardwaredefekt Ausrüstung Serverraum",
"description": "Eine HW-Komponete im Serverraum fällt aus. Die Komponente muss ersetzt werden."
},
{
"name": "Softwareänderungen",
"description": "In einer wichtigen produktiven Anwendung kommt es zu vielen (kritischen) Änderungen. Diese werden nur ungenügend getestet. Es stellt sich heraus, dass ein Softwarefehler bei Kunden zu beträchtlichem Schaden und finanziellen Verlusten geführt hat."
},
{
"name": "Offenlegung von Kundendaten nach Softwaretest",
"description": "Nach einem Softwaretest werden die dazu benötigten Kundendaten nicht gelöscht. Sie bleiben längere Zeit auf einem nicht geschützten Server verfügbar. Es besteht der Verdacht, dass die Daten weitergegeben wurden."
},
{
"name": "Ransomware",
"description": "Ein Angreifer dringt in Systeme ein und verschlüsselt die Daten. Für die Entschlüsselung wird Lösegeld gefordert."
},
{
"name": "CEO-Fraud",
"description": "Im Namen des Firmenchefs wird die Buchhaltung angewiesen, eine Zahlung auf ein (typischerweise ausländisches) Konto der Betrüger vorzunehmen."
}
],
"catalogs": [
{
"name": "Software Entwicklung",
"description": "In der IT-Branche birgt die Softwareentwicklung Risiken hinsichtlich der Einhaltung von Zeitplänen, Budgets und Qualitätsstandards. Fehler in der Codebasis oder inkompatible Systemintegrationen können zu Verzögerungen in der Produktveröffentlichung oder zu Sicherheitslücken führen."
},
{
"name": "Human Resources",
"description": "HR-Risiken in der IT-Branche umfassen Schwierigkeiten bei der Anwerbung und Bindung qualifizierter Fachkräfte, da der Wettbewerb um Talente hoch ist. Zudem kann eine unzureichende Personalentwicklung die Innovation und Anpassungsfähigkeit des Unternehmens beeinträchtigen."
},
{
"name": "Public Relations",
"description": "PR-Risiken beziehen sich auf das Management der öffentlichen Wahrnehmung und Markenreputation. Fehltritte in der Kommunikation oder Skandale können das Vertrauen von Kunden und Partnern schnell untergraben."
},
{
"name": "Sales & Marketing",
"description": "Im Vertrieb und Marketing bestehen Risiken in der effektiven Positionierung von Produkten und Dienstleistungen in einem sich schnell verändernden Technologiemarkt. Eine fehlgeleitete Strategie kann zu Umsatzeinbußen und einem Verlust der Marktposition führen."
},
{
"name": "Infrastruktur",
"description": "Infrastrukturelle Risiken in der IT-Branche umfassen die Zuverlässigkeit und Sicherheit von physischen und virtuellen Netzwerken. Ausfälle, Datenverluste oder Cyberangriffe können erhebliche operationelle und finanzielle Schäden verursachen."
},
{
"name": "Externe Services",
"description": "Die Abhängigkeit von externen Dienstleistern und Zulieferern birgt Risiken in Bezug auf Qualität, Zuverlässigkeit und Compliance. Probleme bei diesen Partnern können zu Betriebsunterbrechungen und Reputationsverlust führen."
}
]
}
4 changes: 4 additions & 0 deletions src/riskmatrix/prompts/system_prompts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"risks": "You are a helpful tool for creating and managing risk assessments for information security purposes. You help manage and monitor the risks associated with information security for organisations and prompt relevant risks for all security in software development, operations, management and all over information security. Formulate the risks in neutral language, as they suggest a state rather than a negative comment. Avoid using the word 'risk' or any form that indicates negative incidents, it should be objective, be concise, use the same language to respond as the user answers his questions",
"catalogs": "You are a helpful tool for creating and managing risk assessments for information security purposes. You help manage and monitor the risks associated with information security for organisations and prompt relevant risks for all security in software development, operations, management and all over information security. Formulate the risks in neutral language, as they suggest a state rather than a negative comment. Avoid using the word 'risk' or any form that indicates negative incidents, it should be objective, be concise, use the same language to respond as the user answers his questions"
}
4 changes: 4 additions & 0 deletions src/riskmatrix/prompts/user_prompts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"risks": "Create up to 10 novel, distinct and relevant risks related to information security and IT operations for solely single (1) and ONLY the one provided risk catalog. Get inspired by the examples but don't copy them. You are given the risk catalog name and description. All risks generated need to be within the context of the given catalog. \nRespond in the same language for the creation of these risk as the users has answered the questions. Directly respond in markdown notated text format with the following format, be concise: \n #### <risk catalog title>\n- [ ] __<risk name>__: <risk description>\n- [ ] __<risk name>__: <risk description>\n...<further risks>...\n\n",
"catalogs": "Create 2 novel and for the business relevan risk catalogs. et inspired by the examples but don't copy them. Just please respond in the same language for the creation of the risk catalog objects as the user has answerein the questions. Directly respond as a json object with the keys being a single work nickname of the riskand with each object containing the field name and description, but first add a key to the object named lang containing the iso language code which the user responded to the questions. You are given example catalogs with names and description. All risk catalogs generated need to be within the context of the given organization."
}
41 changes: 29 additions & 12 deletions src/riskmatrix/scripts/seantis_import_risk_excel.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
print()
sys.exit(1)

from riskmatrix.models.risk_assessment_info import RiskAssessmentInfo, RiskAssessmentState
from riskmatrix.models.risk_assessment_info import (
RiskAssessmentInfo,
RiskAssessmentState
)
import sqlalchemy
from pyramid.paster import bootstrap
from pyramid.paster import get_appsettings
Expand Down Expand Up @@ -78,10 +81,14 @@ def get_or_create_asset(
if asset := session.scalars(q).one_or_none():
return asset

asset = Asset(asset_name, organization, meta={"catalogs": [risk_catalog.id]})
asset = Asset(
asset_name,
organization,
meta={"catalogs": [risk_catalog.id]}
)
asset.organization_id = organization.id
asset.meta = {"catalogs": [risk_catalog.id]}

session.add(asset)
return asset

Expand Down Expand Up @@ -119,29 +126,37 @@ def get_or_create_risk_assessment(

if assessment := session.scalars(q).one_or_none():
return assessment

# check if theres an existing instance of RiskAssessmentInfo which is open for the organization

# check if theres an existing instance of RiskAssessmentInfo
# which is open for the organization
q = select(RiskAssessmentInfo).where(
RiskAssessmentInfo.organization_id == organization.id,
RiskAssessmentInfo.state == RiskAssessmentState.OPEN
)
if risk_assessment_info := session.scalars(q).one_or_none():
assessment = RiskAssessment(risk=risk, asset=asset, info=risk_assessment_info)
assessment = RiskAssessment(
risk=risk,
asset=asset,
info=risk_assessment_info
)
session.add(assessment)
return assessment

risk_assessment_info = RiskAssessmentInfo(organization_id=organization.id)

risk_assessment_info.state = RiskAssessmentState.OPEN
risk_assessment_info.organization_id = organization.id

session.add(risk_assessment_info)
# flush
session.flush()
session.refresh(risk_assessment_info)


assessment = RiskAssessment(risk=risk, asset=asset, info=risk_assessment_info)
assessment = RiskAssessment(
risk=risk,
asset=asset,
info=risk_assessment_info
)
session.add(assessment)
return assessment

Expand All @@ -163,7 +178,9 @@ def populate_catalog(
risk.category = risk_details['category']
risk.description = risk_details['desc']

assessment = get_or_create_risk_assessment(risk, asset, catalog.organization, session)
assessment = get_or_create_risk_assessment(
risk, asset, catalog.organization, session
)
assessment.likelihood = risk_details['likelihood']
assessment.impact = risk_details['impact']

Expand Down
1 change: 1 addition & 0 deletions src/riskmatrix/scripts/upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,5 +212,6 @@ def main() -> None:

upgrade(args)


if __name__ == '__main__':
main()
8 changes: 6 additions & 2 deletions src/riskmatrix/static/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,13 @@ def css(
jquery = js('jquery.min.js')
datatable_core = js('jquery.dataTables.min.js', depends=[jquery])
bootstrap_core = js('bootstrap.bundle.min.js', depends=[jquery, popper])
bootstrap_js = js('bootstrap_custom.js', depends=[jquery, bootstrap_core, popper])
bootstrap_js = js(
'bootstrap_custom.js',
depends=[jquery, bootstrap_core, popper]
)
datatable_bootstrap = js(
'dataTables.bootstrap5.min.js', depends=[bootstrap_core, datatable_core]
'dataTables.bootstrap5.min.js',
depends=[bootstrap_core, datatable_core]
)
datatable_js = js('datatables_custom.js', depends=[datatable_bootstrap])
xhr_edit_js = js('xhr_edit.js', depends=[datatable_js])
9 changes: 7 additions & 2 deletions src/riskmatrix/subscribers.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ def sentry_context(event: NewRequest) -> None:
with configure_scope() as scope:
scope.user = {'id': request.user.id}

def request_nonce_generator(event: 'NewRequest') -> None:

def request_nonce_generator(event: NewRequest) -> None:
request = event.request
request.set_property(lambda r: secrets.token_urlsafe(), 'csp_nonce', reify=True)
request.set_property(
lambda r: secrets.token_urlsafe(),
'csp_nonce',
reify=True
)


def includeme(config: 'Configurator') -> None:
Expand Down
Loading

0 comments on commit 868dd17

Please sign in to comment.