From 47c81c784dd5067a106306882f5e98326ea836fa Mon Sep 17 00:00:00 2001 From: Florian Kromer Date: Sat, 11 Oct 2025 19:37:19 +0200 Subject: [PATCH 1/8] ruff: setup formatter --- Makefile | 1 + poetry.lock | 51 +++++++++++++++++++++++++++++++++++++++----------- pyproject.toml | 17 ++++++++++++++++- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 62f07c6e..dfe01e7c 100644 --- a/Makefile +++ b/Makefile @@ -69,4 +69,5 @@ docs: docs/pytm/index.html docs/threats.md .PHONY: fmt fmt: + poetry run ruff format black $(wildcard pytm/*.py) $(wildcard tests/*.py) $(wildcard *.py) diff --git a/poetry.lock b/poetry.lock index a299b41c..0efb7edb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,17 +1,46 @@ +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. + [[package]] -category = "main" -description = "a pure Python Database Abstraction Layer (for python version 2.7 and 3.x)" name = "pydal" +version = "20200714.1" +description = "a pure Python Database Abstraction Layer (for python version 2.7 and 3.x)" optional = false python-versions = "*" -version = "20200714.1" - -[metadata] -content-hash = "74d2d05a266a2c8725df1f70157ccff43acaed599da9ca304291ec35f8a993cd" -lock-version = "1.0" -python-versions = "^3.8" - -[metadata.files] -pydal = [ +groups = ["main"] +files = [ {file = "pydal-20200714.1.tar.gz", hash = "sha256:dd35b8ecb009099cce7efa72a40707d2e9bdcdf85924f30683a52d5172d1242f"}, ] + +[[package]] +name = "ruff" +version = "0.14.0" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.14.0-py3-none-linux_armv6l.whl", hash = "sha256:58e15bffa7054299becf4bab8a1187062c6f8cafbe9f6e39e0d5aface455d6b3"}, + {file = "ruff-0.14.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:838d1b065f4df676b7c9957992f2304e41ead7a50a568185efd404297d5701e8"}, + {file = "ruff-0.14.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:703799d059ba50f745605b04638fa7e9682cc3da084b2092feee63500ff3d9b8"}, + {file = "ruff-0.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ba9a8925e90f861502f7d974cc60e18ca29c72bb0ee8bfeabb6ade35a3abde7"}, + {file = "ruff-0.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e41f785498bd200ffc276eb9e1570c019c1d907b07cfb081092c8ad51975bbe7"}, + {file = "ruff-0.14.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30a58c087aef4584c193aebf2700f0fbcfc1e77b89c7385e3139956fa90434e2"}, + {file = "ruff-0.14.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f8d07350bc7af0a5ce8812b7d5c1a7293cf02476752f23fdfc500d24b79b783c"}, + {file = "ruff-0.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eec3bbbf3a7d5482b5c1f42d5fc972774d71d107d447919fca620b0be3e3b75e"}, + {file = "ruff-0.14.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16b68e183a0e28e5c176d51004aaa40559e8f90065a10a559176713fcf435206"}, + {file = "ruff-0.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb732d17db2e945cfcbbc52af0143eda1da36ca8ae25083dd4f66f1542fdf82e"}, + {file = "ruff-0.14.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c958f66ab884b7873e72df38dcabee03d556a8f2ee1b8538ee1c2bbd619883dd"}, + {file = "ruff-0.14.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7eb0499a2e01f6e0c285afc5bac43ab380cbfc17cd43a2e1dd10ec97d6f2c42d"}, + {file = "ruff-0.14.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c63b2d99fafa05efca0ab198fd48fa6030d57e4423df3f18e03aa62518c565f"}, + {file = "ruff-0.14.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:668fce701b7a222f3f5327f86909db2bbe99c30877c8001ff934c5413812ac02"}, + {file = "ruff-0.14.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a86bf575e05cb68dcb34e4c7dfe1064d44d3f0c04bbc0491949092192b515296"}, + {file = "ruff-0.14.0-py3-none-win32.whl", hash = "sha256:7450a243d7125d1c032cb4b93d9625dea46c8c42b4f06c6b709baac168e10543"}, + {file = "ruff-0.14.0-py3-none-win_amd64.whl", hash = "sha256:ea95da28cd874c4d9c922b39381cbd69cb7e7b49c21b8152b014bd4f52acddc2"}, + {file = "ruff-0.14.0-py3-none-win_arm64.whl", hash = "sha256:f42c9495f5c13ff841b1da4cb3c2a42075409592825dada7c5885c2c844ac730"}, + {file = "ruff-0.14.0.tar.gz", hash = "sha256:62ec8969b7510f77945df916de15da55311fade8d6050995ff7f680afe582c57"}, +] + +[metadata] +lock-version = "2.1" +python-versions = "^3.11 || ^3.10 || ^3.9 || ^3.8" +content-hash = "27bf4f774115fb8213a98bc578bcd5fbc46deaf877bde86ab8ff435e2c48f4a2" diff --git a/pyproject.toml b/pyproject.toml index eb209f8e..a3b2cf4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,8 +9,23 @@ license = "MIT License" python = "^3.11 || ^3.10 || ^3.9 || ^3.8" pydal = "~20200714.1" -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] +ruff = "^0.14.0" [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api" + +[tool.ruff] +target-version = "py310" + +# https://docs.astral.sh/ruff/settings/#format +[tool.ruff.format] +docstring-code-format = true +docstring-code-line-length = "dynamic" +exclude = [] +indent-style = "space" +line-ending = "lf" +preview = false +quote-style = "double" +skip-magic-trailing-comma = false From 31b39d93b4fd2c8f71a4de593f5dfb5794de130e Mon Sep 17 00:00:00 2001 From: Florian Kromer Date: Sat, 11 Oct 2025 19:39:02 +0200 Subject: [PATCH 2/8] ruff: format code --- pytm/flows.py | 18 ++++++------- pytm/pytm.py | 46 +++++++++++++++++++++------------ pytm/report_util.py | 40 ++++++++++++++++++---------- pytm/template_engine.py | 53 +++++++++++++++++++------------------- tests/test_private_func.py | 33 ++++++++++++++++-------- tests/test_pytmfunc.py | 4 +-- tm.py | 1 - 7 files changed, 115 insertions(+), 80 deletions(-) diff --git a/pytm/flows.py b/pytm/flows.py index a1e882e4..b05c0cab 100644 --- a/pytm/flows.py +++ b/pytm/flows.py @@ -3,7 +3,7 @@ def req_reply(src: Element, dest: Element, req_name: str, reply_name=None) -> (DF, DF): - ''' + """ This function creates two datflows where one dataflow is a request and the second dataflow is the corresponding reply to the newly created request. @@ -22,9 +22,9 @@ def req_reply(src: Element, dest: Element, req_name: str, reply_name=None) -> (D Returns: a tuple of two dataflows, where the first is the request and the second is the reply. - ''' + """ if not reply_name: - reply_name = f'Reply to {req_name}' + reply_name = f"Reply to {req_name}" req = DF(src, dest, req_name) reply = DF(dest, src, name=reply_name) reply.responseTo = req @@ -32,7 +32,7 @@ def req_reply(src: Element, dest: Element, req_name: str, reply_name=None) -> (D def reply(req: DF, **kwargs) -> DF: - ''' + """ This function takes a dataflow as an argument and returns a new dataflow, which is a response to the given dataflow. Args: @@ -45,12 +45,12 @@ def reply(req: DF, **kwargs) -> DF: client_reply = reply(client_query) Returns: a Dataflow which is a reply to the given datadlow req - ''' - if 'name' not in kwargs: - name = f'Reply to {req.name}' + """ + if "name" not in kwargs: + name = f"Reply to {req.name}" else: - name = kwargs['name'] - del kwargs['name'] + name = kwargs["name"] + del kwargs["name"] reply = DF(req.sink, req.source, name, **kwargs) reply.responseTo = req return req, reply diff --git a/pytm/pytm.py b/pytm/pytm.py index a7116878..c9f955bc 100644 --- a/pytm/pytm.py +++ b/pytm/pytm.py @@ -142,8 +142,9 @@ class varElement(var): def __set__(self, instance, value): if not isinstance(value, Element): raise ValueError( - "expecting an Element (or inherited) " - "value, got a {}".format(type(value)) + "expecting an Element (or inherited) value, got a {}".format( + type(value) + ) ) super().__set__(instance, value) @@ -262,9 +263,7 @@ def __str__(self): class varControls(var): def __set__(self, instance, value): if not isinstance(value, Controls): - raise ValueError( - f"expecting an Controls value, got a {type(value)}" - ) + raise ValueError(f"expecting an Controls value, got a {type(value)}") super().__set__(instance, value) @@ -283,9 +282,7 @@ def __set__(self, instance, value): class varAssumption(var): def __set__(self, instance, value): if not isinstance(value, Assumption): - raise ValueError( - f"expecting an Assumption value, got a {type(value)}" - ) + raise ValueError(f"expecting an Assumption value, got a {type(value)}") super().__set__(instance, value) @@ -560,7 +557,7 @@ def _describe_classes(classes): docs.append("required") if attr.default or isinstance(attr.default, bool): docs.append("default: {}".format(attr.default)) - lpadding = f'\n{" ":<{longest+2}}' + lpadding = f"\n{' ':<{longest + 2}}" print(f" {i:<{longest}}{lpadding.join(docs)}") print() @@ -687,7 +684,11 @@ class Finding: threat_id = varString("", required=True, doc="Threat ID") references = varString("", required=True, doc="Threat references") condition = varString("", required=True, doc="Threat condition") - assumption = varAssumption(None, required=False, doc="The assumption, that caused this finding to be excluded") + assumption = varAssumption( + None, + required=False, + doc="The assumption, that caused this finding to be excluded", + ) response = varString( "", required=False, @@ -799,7 +800,7 @@ class TM: excluded_findings = varFindings( [], doc="Threats found for elements of this model, " - "that were excluded on a per-element basis, using the Assumptions class" + "that were excluded on a per-element basis, using the Assumptions class", ) onDuplicates = varAction( Action.NO_ACTION, @@ -845,7 +846,9 @@ def _add_threats(self): raise UIError( e, f"while trying to open the the threat file ({self.threatsFile})." ) - active_threats = (threat for threat in threats_json if "DEPRECATED" not in threat) + active_threats = ( + threat for threat in threats_json if "DEPRECATED" not in threat + ) for threat in active_threats: TM._threats.append(Threat(**threat)) @@ -883,7 +886,12 @@ def resolve(self): for assumption in e.assumptions + global_assumptions: # type: Assumption if t.id in assumption.exclude: excluded_finding_count += 1 - f = Finding(e, id=str(excluded_finding_count), threat=t, assumption=assumption) + f = Finding( + e, + id=str(excluded_finding_count), + threat=t, + assumption=assumption, + ) excluded_findings.append(f) _continue = True break @@ -1223,9 +1231,10 @@ def sqlDump(self, filename): from pydal import DAL, Field except ImportError as e: raise UIError( - e, """This feature requires the pyDAL package, + e, + """This feature requires the pyDAL package, Please install the package via pip or your packagemanger of choice. - """ + """, ) @lru_cache(maxsize=None) @@ -1278,7 +1287,6 @@ def get_table(db, klass): db.close() - class Controls: """Controls implemented by/on and Element""" @@ -1383,8 +1391,12 @@ class Assumption: Assumption used by an Element. Used to exclude threats on a per-element basis. """ + name = varString("", required=True) - exclude = varStrings([], doc="A list of threat SIDs to exclude for this assumption. For example: INP01") + exclude = varStrings( + [], + doc="A list of threat SIDs to exclude for this assumption. For example: INP01", + ) description = varString("", doc="An additional description of the assumption") def __init__(self, name, **kwargs): diff --git a/pytm/report_util.py b/pytm/report_util.py index 90df7de6..2d82d775 100644 --- a/pytm/report_util.py +++ b/pytm/report_util.py @@ -1,39 +1,53 @@ - class ReportUtils: @staticmethod def getParentName(element): from pytm import Boundary - if (isinstance(element, Boundary)): + + if isinstance(element, Boundary): parent = element.inBoundary - if (parent is not None): + if parent is not None: return parent.name else: return str("") else: - return "ERROR: getParentName method is not valid for " + element.__class__.__name__ - + return ( + "ERROR: getParentName method is not valid for " + + element.__class__.__name__ + ) @staticmethod def getNamesOfParents(element): from pytm import Boundary - if (isinstance(element, Boundary)): - parents = [p.name for p in element.parents()] - return parents + + if isinstance(element, Boundary): + parents = [p.name for p in element.parents()] + return parents else: - return "ERROR: getNamesOfParents method is not valid for " + element.__class__.__name__ + return ( + "ERROR: getNamesOfParents method is not valid for " + + element.__class__.__name__ + ) @staticmethod def getFindingCount(element): from pytm import Element - if (isinstance(element, Element)): + + if isinstance(element, Element): return str(len(list(element.findings))) else: - return "ERROR: getFindingCount method is not valid for " + element.__class__.__name__ + return ( + "ERROR: getFindingCount method is not valid for " + + element.__class__.__name__ + ) @staticmethod def getElementType(element): from pytm import Element - if (isinstance(element, Element)): + + if isinstance(element, Element): return str(element.__class__.__name__) else: - return "ERROR: getElementType method is not valid for " + element.__class__.__name__ + return ( + "ERROR: getElementType method is not valid for " + + element.__class__.__name__ + ) diff --git a/pytm/template_engine.py b/pytm/template_engine.py index 9118a5d4..aa7bbae5 100644 --- a/pytm/template_engine.py +++ b/pytm/template_engine.py @@ -9,11 +9,10 @@ class SuperFormatter(string.Formatter): """World's simplest Template engine.""" def format_field(self, value, spec): - spec_parts = spec.split(":") if spec.startswith("repeat"): - # Example usage, format, count of spec_parts, exampple format - # object:repeat:template 2 {item.findings:repeat:{{item.id}}, } + # Example usage, format, count of spec_parts, exampple format + # object:repeat:template 2 {item.findings:repeat:{{item.id}}, } template = spec.partition(":")[-1] if type(value) is dict: @@ -21,9 +20,9 @@ def format_field(self, value, spec): return "".join([self.format(template, item=item) for item in value]) elif spec.startswith("call:") and hasattr(value, "__call__"): - # Example usage, format, exampple format - # methood:call {item.display_name:call:} - # methood:call:template {item.parents:call:{{item.name}}, } + # Example usage, format, exampple format + # methood:call {item.display_name:call:} + # methood:call:template {item.parents:call:{{item.name}}, } result = value() if type(result) is list: @@ -33,11 +32,11 @@ def format_field(self, value, spec): return result elif spec.startswith("call:"): - # Example usage, format, exampple format - # object:call:method_name {item:call:getFindingCount} - # object:call:method_name:template {item:call:getNamesOfParents: - # {{item}} - # } + # Example usage, format, exampple format + # object:call:method_name {item:call:getFindingCount} + # object:call:method_name:template {item:call:getNamesOfParents: + # {{item}} + # } method_name = spec_parts[1] @@ -50,26 +49,26 @@ def format_field(self, value, spec): return result - elif (spec.startswith("if") or spec.startswith("not")): - # Example usage, format, exampple format - # object.bool:if:template {item.inScope:if:Is in scope} - # object:if:template {item.findings:if:Has Findings} - # object.method:if:template {item.parents:if:Has Parents} - # - # object.bool:not:template {item.inScope:not:Is not in scope} - # object:not:template {item.findings:not:Has No Findings} - # object.method:not:template {item.parents:not:Has No Parents} - + elif spec.startswith("if") or spec.startswith("not"): + # Example usage, format, exampple format + # object.bool:if:template {item.inScope:if:Is in scope} + # object:if:template {item.findings:if:Has Findings} + # object.method:if:template {item.parents:if:Has Parents} + # + # object.bool:not:template {item.inScope:not:Is not in scope} + # object:not:template {item.findings:not:Has No Findings} + # object.method:not:template {item.parents:not:Has No Parents} + template = spec.partition(":")[-1] - if (hasattr(value, "__call__")): + if hasattr(value, "__call__"): result = value() else: result = value - if (spec.startswith("if")): - return (result and template or "") - else: - return (not result and template or "") + if spec.startswith("if"): + return result and template or "" + else: + return not result and template or "" else: return super(SuperFormatter, self).format_field(value, spec) @@ -77,7 +76,7 @@ def format_field(self, value, spec): def call_util_method(self, method_name, object): module_name = "pytm.report_util" klass_name = "ReportUtils" - module = __import__(module_name, fromlist=['ReportUtils']) + module = __import__(module_name, fromlist=["ReportUtils"]) klass = getattr(module, klass_name) method = getattr(klass, method_name) diff --git a/tests/test_private_func.py b/tests/test_private_func.py index 4ce0816e..8ada296f 100644 --- a/tests/test_private_func.py +++ b/tests/test_private_func.py @@ -86,7 +86,7 @@ def test_defaults(self): tm = TM("TM") user_data = Data("HTTP") user = Actor("User", data=user_data) - user.controls.authenticatesDestination=True + user.controls.authenticatesDestination = True json_data = Data("JSON") server = Server( @@ -99,7 +99,7 @@ def test_defaults(self): protocol="PostgreSQL", data=sql_resp, ) - db.controls.isEncrypted=False + db.controls.isEncrypted = False db.type = DatastoreType.SQL worker = Process("Task queue worker") @@ -110,7 +110,9 @@ def test_defaults(self): result_data = Data("Results") result = Dataflow(db, server, "Results", data=result_data, isResponse=True) resp_get_data = Data("HTTP Response") - resp_get = Dataflow(server, user, "HTTP Response", data=resp_get_data, isResponse=True) + resp_get = Dataflow( + server, user, "HTTP Response", data=resp_get_data, isResponse=True + ) test_assumption = Assumption("test assumption") resp_get.assumptions = [test_assumption] @@ -118,9 +120,11 @@ def test_defaults(self): req_post_data = Data("JSON") req_post = Dataflow(user, server, "HTTP POST", data=req_post_data) resp_post = Dataflow(server, user, "HTTP Response", isResponse=True) - test_assumption_exclude = Assumption("test assumption", exclude=["ABCD", "BCDE"]) + test_assumption_exclude = Assumption( + "test assumption", exclude=["ABCD", "BCDE"] + ) resp_post.assumptions = [test_assumption_exclude] - + sql_data = Data("SQL") worker_query = Dataflow(worker, db, "Query", data=sql_data) Dataflow(db, worker, "Results", isResponse=True) @@ -133,7 +137,8 @@ def test_defaults(self): self.assertEqual(req_get.dstPort, server.port) self.assertEqual(req_get.controls.isEncrypted, server.controls.isEncrypted) self.assertEqual( - req_get.controls.authenticatesDestination, user.controls.authenticatesDestination + req_get.controls.authenticatesDestination, + user.controls.authenticatesDestination, ) self.assertEqual(req_get.protocol, server.protocol) self.assertTrue(user.data.issubset(req_get.data)) @@ -142,7 +147,8 @@ def test_defaults(self): self.assertEqual(server_query.dstPort, db.port) self.assertEqual(server_query.controls.isEncrypted, db.controls.isEncrypted) self.assertEqual( - server_query.controls.authenticatesDestination, server.controls.authenticatesDestination + server_query.controls.authenticatesDestination, + server.controls.authenticatesDestination, ) self.assertEqual(server_query.protocol, db.protocol) self.assertTrue(server.data.issubset(server_query.data)) @@ -167,7 +173,8 @@ def test_defaults(self): self.assertEqual(req_post.dstPort, server.port) self.assertEqual(req_post.controls.isEncrypted, server.controls.isEncrypted) self.assertEqual( - req_post.controls.authenticatesDestination, user.controls.authenticatesDestination + req_post.controls.authenticatesDestination, + user.controls.authenticatesDestination, ) self.assertEqual(req_post.protocol, server.protocol) self.assertTrue(user.data.issubset(req_post.data)) @@ -179,7 +186,9 @@ def test_defaults(self): self.assertEqual(resp_post.protocol, server.protocol) self.assertTrue(server.data.issubset(resp_post.data)) self.assertListEqual(resp_post.assumptions, [test_assumption_exclude]) - self.assertSetEqual(resp_post.assumptions[0].exclude, test_assumption_exclude.exclude) + self.assertSetEqual( + resp_post.assumptions[0].exclude, test_assumption_exclude.exclude + ) self.assertListEqual(server.inputs, [req_get, req_post]) self.assertListEqual(server.outputs, [server_query]) @@ -268,7 +277,7 @@ def test_encode_threat_data(self): cvss="1.234", response="A test response", assumption=Assumption("Test Assumption", exclude=["INP02"]), - ) + ), ] encoded_findings = encode_threat_data(findings) @@ -279,7 +288,9 @@ def test_encode_threat_data(self): self.assertEqual(encoded_findings[0].threat_id, "INP01") self.assertEqual(encoded_findings[0].cvss, "9.876") self.assertEqual(encoded_findings[0].response, "A test response") - self.assertEqual(encoded_findings[1].description, "An escape test <script>") + self.assertEqual( + encoded_findings[1].description, "An escape test <script>" + ) self.assertEqual(encoded_findings[1].severity, "Medium") self.assertEqual(encoded_findings[1].id, "2") self.assertEqual(encoded_findings[1].threat_id, "INP02") diff --git a/tests/test_pytmfunc.py b/tests/test_pytmfunc.py index f94d042c..4e88aa7f 100644 --- a/tests/test_pytmfunc.py +++ b/tests/test_pytmfunc.py @@ -36,7 +36,8 @@ ) as threat_file: threats = {t["SID"]: Threat(**t) for t in json.load(threat_file)} -output_path=tempfile.gettempdir() +output_path = tempfile.gettempdir() + class TestTM(unittest.TestCase): def test_seq(self): @@ -659,7 +660,6 @@ def test_DS01(self): self.assertTrue(threat.apply(web)) def test_DE01(self): - with self.subTest("Default case"): user = Actor("User") web = Server("Web Server") diff --git a/tm.py b/tm.py index 9f0cb464..cd6fdc4c 100755 --- a/tm.py +++ b/tm.py @@ -134,4 +134,3 @@ if __name__ == "__main__": tm.process() - From f01cf664a616df9812793d71b391fbc132ef84cd Mon Sep 17 00:00:00 2001 From: Florian Kromer Date: Sat, 11 Oct 2025 19:43:53 +0200 Subject: [PATCH 3/8] fix make fmt --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index dfe01e7c..8a7e30c2 100644 --- a/Makefile +++ b/Makefile @@ -70,4 +70,3 @@ docs: docs/pytm/index.html docs/threats.md .PHONY: fmt fmt: poetry run ruff format - black $(wildcard pytm/*.py) $(wildcard tests/*.py) $(wildcard *.py) From c165734524fe1b9ca5b31a8bfaf99b87631e6ceb Mon Sep 17 00:00:00 2001 From: Florian Kromer Date: Sat, 11 Oct 2025 19:47:42 +0200 Subject: [PATCH 4/8] make: add ruff analyze command --- Makefile | 4 ++++ pyproject.toml | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/Makefile b/Makefile index 8a7e30c2..ecad7bec 100644 --- a/Makefile +++ b/Makefile @@ -70,3 +70,7 @@ docs: docs/pytm/index.html docs/threats.md .PHONY: fmt fmt: poetry run ruff format + +.PHONY: ana +ana: + poetry run ruff analyze graph diff --git a/pyproject.toml b/pyproject.toml index a3b2cf4f..f098b5fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,15 @@ build-backend = "poetry.masonry.api" [tool.ruff] target-version = "py310" +# https://docs.astral.sh/ruff/settings/#analyze +[tool.ruff.analyze] +detect-string-imports = false +direction = "dependencies" +exclude = [] +include-dependencies = {} +preview = false +string-imports-min-dots = 2 + # https://docs.astral.sh/ruff/settings/#format [tool.ruff.format] docstring-code-format = true From d42f096dcbdbff12baba13e3465676d136768cbc Mon Sep 17 00:00:00 2001 From: Florian Kromer Date: Sat, 11 Oct 2025 20:02:05 +0200 Subject: [PATCH 5/8] ruff: add basic linting --- Makefile | 4 ++++ pyproject.toml | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Makefile b/Makefile index ecad7bec..aa77524d 100644 --- a/Makefile +++ b/Makefile @@ -74,3 +74,7 @@ fmt: .PHONY: ana ana: poetry run ruff analyze graph + +.PHONY: check +check: + poetry run ruff check diff --git a/pyproject.toml b/pyproject.toml index f098b5fd..118707a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,3 +38,29 @@ line-ending = "lf" preview = false quote-style = "double" skip-magic-trailing-comma = false + +# https://docs.astral.sh/ruff/settings/#lint +[tool.ruff.lint] +allowed-confusables = [] +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +exclude = [] +explicit-preview-rules = false +extend-fixable = [] +extend-ignore = [] +extend-per-file-ignores = {} +extend-safe-fixes = [] +extend-select = [] +extend-unsafe-fixes = [] +external = [] +fixable = ["ALL"] +future-annotations = false +ignore = [] +ignore-init-module-imports = true +logger-objects = [] +per-file-ignores = {} +preview = false +select = ["E4", "E7", "E9", "F"] +task-tags = ["TODO", "FIXME", "XXX"] +typing-extensions = true +typing-modules = [] +unfixable = [] From 1f8a1bf7d1c1e022d836b52089b5cc8a23a6c4c3 Mon Sep 17 00:00:00 2001 From: Florian Kromer Date: Sat, 11 Oct 2025 20:07:58 +0200 Subject: [PATCH 6/8] make: add fix command --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index aa77524d..4c80c366 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,10 @@ fmt: ana: poetry run ruff analyze graph +.PHONY: fix +fix: + poetry run ruff check . --fix + .PHONY: check check: poetry run ruff check From ef0e09ce9729754f1d4f09ad78020474933b618e Mon Sep 17 00:00:00 2001 From: Florian Kromer Date: Sat, 11 Oct 2025 20:15:28 +0200 Subject: [PATCH 7/8] ruff: fix code --- pytm/json.py | 9 --------- tests/test_pytmfunc.py | 1 - 2 files changed, 10 deletions(-) diff --git a/pytm/json.py b/pytm/json.py index db0c7af7..a6164335 100644 --- a/pytm/json.py +++ b/pytm/json.py @@ -4,17 +4,8 @@ from .pytm import ( TM, Boundary, - Element, Dataflow, - Server, - ExternalEntity, - Datastore, - Actor, - Process, - SetOfProcesses, Action, - Lambda, - Controls, ) diff --git a/tests/test_pytmfunc.py b/tests/test_pytmfunc.py index 4e88aa7f..c2f37d8b 100644 --- a/tests/test_pytmfunc.py +++ b/tests/test_pytmfunc.py @@ -4,7 +4,6 @@ import re import unittest import tempfile -from contextlib import redirect_stdout from pytm import ( pytm, From b233675ce779b6e4b19ab911bb8ce2233ba512f9 Mon Sep 17 00:00:00 2001 From: Florian Kromer Date: Sat, 11 Oct 2025 20:23:12 +0200 Subject: [PATCH 8/8] revert invalid fixes --- pytm/json.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pytm/json.py b/pytm/json.py index a6164335..d7133b5c 100644 --- a/pytm/json.py +++ b/pytm/json.py @@ -5,6 +5,9 @@ TM, Boundary, Dataflow, + Server, + Datastore, + Actor, Action, )