Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
33 changes: 32 additions & 1 deletion src/seclab_taskflows/mcp_servers/repo_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from seclab_taskflow_agent.path_utils import mcp_data_dir, log_file_name

from .repo_context_models import Application, EntryPoint, UserAction, WebEntryPoint, ApplicationIssue, AuditResult, Base
from .repo_context_models import LowSeverityAuditResult
from .utils import process_repo

logging.basicConfig(
Expand All @@ -26,7 +27,6 @@

MEMORY = mcp_data_dir("seclab-taskflows", "repo_context", "REPO_CONTEXT_DIR")


def app_to_dict(result):
return {
"app_id": result.id,
Expand Down Expand Up @@ -107,6 +107,7 @@ def __init__(self, memcache_state_dir: str):
WebEntryPoint.__table__,
ApplicationIssue.__table__,
AuditResult.__table__,
LowSeverityAuditResult.__table__,
],
Comment on lines 101 to 111
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clear_repo() currently deletes rows from AuditResult but not from the new low_severity_audit_result table. Because this is SQLite and bulk deletes are used, relying on ON DELETE CASCADE may not actually remove low-severity rows, so repo cleanup can leave orphan/old data behind. Consider explicitly deleting LowSeverityAuditResult rows for the repo (or enabling/enforcing FK cascades consistently).

Copilot uses AI. Check for mistakes.
)

Expand Down Expand Up @@ -239,6 +240,22 @@ def store_new_user_action(self, repo, app_id, file, line, notes, update=False):
session.add(new_user_action)
session.commit()
return f"Updated or added user action for {file} and {line} in {repo}."

def store_low_severity_reason(self, repo, component_id, result_id, reason):
with Session(self.engine) as session:
existing = session.query(LowSeverityAuditResult).filter_by(repo=repo, result_id=result_id).first()
if existing:
existing.reason_for_rejection += reason
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

store_low_severity_reason updates existing.reason_for_rejection, but LowSeverityAuditResult defines the column as reason. This will raise an AttributeError at runtime and prevent saving updates. Use the correct attribute (and consider adding a separator like a newline if appending multiple reasons).

Suggested change
existing.reason_for_rejection += reason
if existing.reason:
existing.reason = f"{existing.reason}\n{reason}"
else:
existing.reason = reason

Copilot uses AI. Check for mistakes.
else:
new_low_severity_result = LowSeverityAuditResult(
repo=repo,
component_id=component_id,
result_id=result_id,
reason=reason,
)
session.add(new_low_severity_result)
session.commit()
return f"Updated or added low severity result for {repo} and result id {result_id}"

def get_app(self, repo, location):
with Session(self.engine) as session:
Expand Down Expand Up @@ -294,6 +311,7 @@ def get_app_audit_results(self, repo, component_id, has_non_security_error, has_
"repo": app.repo,
"issue_type": issue.issue_type,
"issue_id": issue.issue_id,
"result_id" : issue.id,
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s a formatting inconsistency in the returned dict key ("result_id" : issue.id has an extra space before the colon). This is minor, but keeping key formatting consistent improves readability and reduces noisy diffs later.

Suggested change
"result_id" : issue.id,
"result_id": issue.id,

Copilot uses AI. Check for mistakes.
"notes": issue.notes,
"has_vulnerability": issue.has_vulnerability,
"has_non_security_error": issue.has_non_security_error,
Expand Down Expand Up @@ -782,6 +800,19 @@ def get_potential_audit_results_for_repo(
backend.get_app_audit_results(repo, component_id=None, has_non_security_error=True, has_vulnerability=None)
)

@mcp.tool()
def store_low_severity_reason(
owner: str = Field(description="The owner of the GitHub repository"),
repo: str = Field(description="The name of the GitHub repository"),
component_id: int = Field(description="The ID of the component"),
result_id: int = Field(description="The ID of the audit result"),
reason: str = Field(description="The reason why this issue is not considered high severity"),
):
"""
Store the reason for auditing an issue as low severity.
"""
repo = process_repo(owner, repo)
return backend.store_low_severity_reason(repo, component_id, result_id, reason)

@mcp.tool()
def clear_repo(
Expand Down
13 changes: 13 additions & 0 deletions src/seclab_taskflows/mcp_servers/repo_context_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ def __repr__(self):
f"component_id={self.component_id}, issue_type={self.issue_type}, issue_id={self.issue_id}, notes={self.notes})>"
)

class LowSeverityAuditResult(Base):
__tablename__ = "low_severity_audit_result"
id: Mapped[int] = mapped_column(primary_key=True)
repo: Mapped[str]
component_id = Column(Integer, ForeignKey("application.id", ondelete="CASCADE"))
result_id = Column(Integer, ForeignKey("audit_result.id", ondelete="CASCADE"))
reason: Mapped[str] = mapped_column(Text)

def __repr__(self):
return (
f"<LowSeverityAuditResult(id={self.id}, repo={self.repo}, "
f"component_id={self.component_id}, result_id={self.result_id}, reason={self.reason})>"
)

class EntryPoint(Base):
__tablename__ = "entry_point"
Expand Down
73 changes: 73 additions & 0 deletions src/seclab_taskflows/taskflows/audit/filter_severity.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# SPDX-FileCopyrightText: 2025 GitHub
# SPDX-License-Identifier: MIT

seclab-taskflow-agent:
filetype: taskflow
version: "1.0"

model_config: seclab_taskflows.configs.model_config

globals:
repo:
taskflow:
- task:
must_complete: true
exclude_from_context: true
agents:
- seclab_taskflow_agent.personalities.assistant
model: general_tasks
user_prompt: |
Fetch the vulnerable issues in {{ globals.repo }}.
toolboxes:
- seclab_taskflows.toolboxes.repo_context
- task:
must_complete: true
repeat_prompt: true
async: true
agents:
- seclab_taskflows.personalities.web_application_security_expert
model: code_analysis
toolboxes:
- seclab_taskflows.toolboxes.repo_context
- seclab_taskflows.toolboxes.local_file_viewer
user_prompt: |
The audit result has id {{ result.result_id }}. It is in the component with id {{ result.component_id }}
at the location {{ result.location }}. It is in the repository {{ result.repo }}.
The notes for the audit result are as follows:

{{ result.notes }}

Determine the low severity results based on the following criteria:
- blind SSRF that has limited on the information it discloses. For example, an SSRF that only gives a different
HTTP status for a specific type of endpoint and rejects all other endpoints (e.g. the endpoint must respond with a certain type of payload), and does not disclose any information other than the HTTP status code.
- Path traversal or partial path traversal that can only read access to a specific directory with limited types of files (e.g. it can only access log files, or it can only access files with a specific extension).
- Path traversal or partial path traversal that only discloses the existence of files or directories, and does not disclose any additional information about the file or directory.
- Information disclosure that only discloses whether a specific condition is true, or some
id number, publicly available resources, such as source code files or documentation,
and does not disclose any additional information. For example, an information disclosure that only discloses whether a specific user exists,
or an information disclosure that only discloses whether a specific issue id exists in the system.
- Issues that requires a malicious admin users to exploit during installation, configuration or other
set up process.
- When running CLI tools or installers, we assume the user already has control on the device.
Any issues that only allows a local user to gain access of the device with running these tools or during installation
is considered low severity.
However, do not exclude issues in installation or configuration that can be exploited by non-admin users.
- CSRF or XSS issues that can only be exploited during a very specific time window. For example,
CSRF or XSS that can only be exploited during the installation process.
- Stored XSS that requires an admin user to upload malicious contents.
- Issue with very limited impact, such as
forcing a user to log out, rate limiting, DoS issues.
- Misclassified issues that does not have a valid attack scenario or impact.
- Issues that requires knowledge of the victim's private information (e.g. secret tokens, private keys, credentials)
in order to exploit.
- Issues that requires physical access to the device or system, or sharing a device with the victim.
- Blind SQL injection that does not return any data, and only returns a different HTTP status code for a specific type of endpoint, and does not disclose any information other than the HTTP status code.

If you decided to reject an issue, provide the reason, and then
store a low severity reason for the result with the id {{ result.result_id }}. Otherwise,
you can finish the task.

DO NOT change or store anything for the current audit result.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task prompt instructs the agent to store a low-severity reason (and even names the result id), but later says "DO NOT change or store anything for the current audit result." This is contradictory and may prevent the intended store_low_severity_reason call. Clarify that the agent should not modify the AuditResult itself, but should store a low-severity reason record when applicable.

Suggested change
DO NOT change or store anything for the current audit result.
Do not modify the existing AuditResult record itself (for example, do not change its severity, status, or notes); only store a separate low-severity reason record for the current audit result when applicable.

Copilot uses AI. Check for mistakes.



Loading