Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ jobs:
run: pip install --upgrade hatch

- name: Run static analysis
run: |
# hatch fmt --check
echo linter errors will be fixed in a separate PR
run: hatch fmt --check

- name: Run tests
run: hatch test --python ${{ matrix.python-version }} --cover --randomize --parallel --retries 2 --retry-delay 1
102 changes: 102 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,105 @@ exclude_lines = [
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]

[tool.ruff]
line-length = 120

[tool.ruff.lint]
ignore = [
# E402: Module level import not at top of file
# Ignored because logging configuration needs to be set before imports
"E402",
# FBT001/FBT002: Boolean typed positional/default argument in function definition
# Ignored because this is a common pattern in API design
"FBT001",
"FBT002",
# N802: Function name should be lowercase
# Ignored to allow acronyms like GHSA in function names
"N802",
# RUF013: PEP 484 prohibits implicit Optional
# Ignored as explicit Optional is verbose and the pattern is clear
"RUF013",
# FA100/FA102: Add from __future__ import annotations
# Ignored as this is a style preference and PEP 604 union syntax is valid in Python 3.10+
"FA100",
"FA102",
# A001/A002/A003: Variable/argument/class attribute is shadowing a Python builtin
# Ignored as 'next', 'id', 'type' are common parameter names in this codebase
"A001",
"A002",
"A003",
# PLR2004: Magic value used in comparison
# Ignored as magic values are acceptable in this codebase for simple comparisons
"PLR2004",
# G004: Logging statement uses f-string
# Ignored as f-strings in logging are acceptable
"G004",
# T201: print found
# Ignored in MCP servers where print is used for output
"T201",
# S607: Starting a process with a partial executable path
# Ignored as we trust the environment configuration
"S607",
# ARG001/ARG002: Unused function/method argument
# Ignored as some arguments may be required for API compatibility
"ARG001",
"ARG002",
# TID252: Prefer absolute imports over relative imports
# Ignored as relative imports are acceptable within the same package
"TID252",
# RET504: Unnecessary assignment before return statement
# Ignored as this pattern can improve readability
"RET504",
# TRY003: Avoid specifying long messages outside the exception class
# Ignored as inline error messages are acceptable for simple cases
"TRY003",
# EM102: Exception must not use an f-string literal
# Ignored as f-strings in exceptions are acceptable
"EM102",
# TRY300: Consider moving this statement to an else block
# Ignored as the current pattern is acceptable
"TRY300",
# BLE001: Do not catch blind exception
# Ignored as catching Exception is sometimes necessary for error handling
"BLE001",
# SIM117: Use a single with statement with multiple contexts
# Ignored as nested with statements can be more readable
"SIM117",
# PLW0602: Using global for variable but no assignment is done
# Ignored as globals may be used for module-level configuration
"PLW0602",
# PIE810: Call startswith/endswith once with a tuple
# Ignored as multiple calls can be more readable
"PIE810",
# SIM102: Use a single if statement instead of nested if statements
# Ignored as nested if can be more readable in some cases
"SIM102",
# SIM101: Use a single if statement instead of multiple nested if statements
# Ignored as nested if can be more readable in some cases
"SIM101",
# PERF401: Use list.extend to create a transformed list
# Ignored as append in a loop can be more readable
"PERF401",
# PERF102: When using only the keys/values of a dict use keys()/values()
# Ignored as items() usage can be intentional
"PERF102",
# LOG015: debug() call on root logger
# Ignored as root logger usage is acceptable for simple logging
"LOG015",
# PLC0206: Cannot have defined parameters for properties
# Ignored as this is an intentional pattern
"PLC0206",
# RUF015: Prefer next(...) over single element slice
# Ignored as slice can be more readable
"RUF015",
# B008: Do not perform function call in argument defaults
# Ignored as Field() defaults are common in Pydantic
"B008",
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = [
# S101: Use of assert detected
"S101",
]
2 changes: 2 additions & 0 deletions src/seclab_taskflows/mcp_servers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: 2024 GitHub <securitylab@github.com>
# SPDX-License-Identifier: MIT
31 changes: 19 additions & 12 deletions src/seclab_taskflows/mcp_servers/alert_results_models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
# SPDX-FileCopyrightText: 2025 GitHub
# SPDX-License-Identifier: MIT

from sqlalchemy import String, Text, Integer, ForeignKey, Column
from sqlalchemy.orm import DeclarativeBase, mapped_column, Mapped, relationship
from typing import Optional

from sqlalchemy import Column, ForeignKey, Integer, Text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship


class Base(DeclarativeBase):
pass


class AlertResults(Base):
__tablename__ = 'alert_results'
__tablename__ = "alert_results"

canonical_id: Mapped[int] = mapped_column(primary_key=True)
alert_id: Mapped[str]
Expand All @@ -22,25 +25,29 @@ class AlertResults(Base):
valid: Mapped[bool] = mapped_column(nullable=False, default=True)
completed: Mapped[bool] = mapped_column(nullable=False, default=False)

relationship('AlertFlowGraph', cascade='all, delete')
relationship("AlertFlowGraph", cascade="all, delete")

def __repr__(self):
return (f"<AlertResults(alert_id={self.alert_id}, repo={self.repo}, "
f"rule={self.rule}, language={self.language}, location={self.location}, "
f"result={self.result}, created_at={self.created}, valid={self.valid}, completed={self.completed})>")
return (
f"<AlertResults(alert_id={self.alert_id}, repo={self.repo}, "
f"rule={self.rule}, language={self.language}, location={self.location}, "
f"result={self.result}, created_at={self.created}, valid={self.valid}, completed={self.completed})>"
)


class AlertFlowGraph(Base):
__tablename__ = 'alert_flow_graph'
__tablename__ = "alert_flow_graph"

id: Mapped[int] = mapped_column(primary_key=True)
alert_canonical_id = Column(Integer, ForeignKey('alert_results.canonical_id', ondelete='CASCADE'))
alert_canonical_id = Column(Integer, ForeignKey("alert_results.canonical_id", ondelete="CASCADE"))
flow_data: Mapped[str] = mapped_column(Text)
repo: Mapped[str]
prev: Mapped[Optional[str]]
next: Mapped[Optional[str]]
started: Mapped[bool] = mapped_column(nullable=False, default=False)

def __repr__(self):
return (f"<AlertFlowGraph(alert_canonical_id={self.alert_canonical_id}, "
f"flow_data={self.flow_data}, repo={self.repo}, prev={self.prev}, next={self.next}, started={self.started})>")

return (
f"<AlertFlowGraph(alert_canonical_id={self.alert_canonical_id}, "
f"flow_data={self.flow_data}, repo={self.repo}, prev={self.prev}, next={self.next}, started={self.started})>"
)
2 changes: 2 additions & 0 deletions src/seclab_taskflows/mcp_servers/codeql_python/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: 2024 GitHub <securitylab@github.com>
# SPDX-License-Identifier: MIT
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# SPDX-FileCopyrightText: 2025 GitHub
# SPDX-License-Identifier: MIT

from sqlalchemy import Text
from sqlalchemy.orm import DeclarativeBase, mapped_column, Mapped
from typing import Optional

from sqlalchemy import Text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
pass


class Source(Base):
__tablename__ = 'source'
__tablename__ = "source"

id: Mapped[int] = mapped_column(primary_key=True)
repo: Mapped[str]
Expand All @@ -20,6 +22,8 @@ class Source(Base):
notes: Mapped[Optional[str]] = mapped_column(Text, nullable=True)

def __repr__(self):
return (f"<Source(id={self.id}, repo={self.repo}, "
f"location={self.source_location}, line={self.line}, source_type={self.source_type}, "
f"notes={self.notes})>")
return (
f"<Source(id={self.id}, repo={self.repo}, "
f"location={self.source_location}, line={self.line}, source_type={self.source_type}, "
f"notes={self.notes})>"
)
Loading