nat-engine 1__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.
- mannf/__init__.py +33 -0
- mannf/__main__.py +10 -0
- mannf/_version.py +8 -0
- mannf/agents/__init__.py +7 -0
- mannf/agents/analyzer_agent.py +9 -0
- mannf/agents/base.py +9 -0
- mannf/agents/bdi_agent.py +9 -0
- mannf/agents/belief_state.py +9 -0
- mannf/agents/coordinator_agent.py +9 -0
- mannf/agents/executor_agent.py +9 -0
- mannf/agents/monitor_agent.py +9 -0
- mannf/agents/oracle_agent.py +9 -0
- mannf/agents/planner_agent.py +9 -0
- mannf/agents/test_agent.py +9 -0
- mannf/anomaly/__init__.py +7 -0
- mannf/anomaly/enhanced_detector.py +9 -0
- mannf/cli.py +9 -0
- mannf/core/__init__.py +26 -0
- mannf/core/agents/__init__.py +52 -0
- mannf/core/agents/accessibility_scanner_agent.py +245 -0
- mannf/core/agents/analyzer_agent.py +224 -0
- mannf/core/agents/autonomous_loop_agent.py +1086 -0
- mannf/core/agents/autonomous_loop_models.py +62 -0
- mannf/core/agents/autonomous_run_differ.py +427 -0
- mannf/core/agents/base.py +128 -0
- mannf/core/agents/bdi_agent.py +330 -0
- mannf/core/agents/belief_state.py +202 -0
- mannf/core/agents/browser_coordinator_agent.py +224 -0
- mannf/core/agents/browser_executor_agent.py +410 -0
- mannf/core/agents/coordinator_agent.py +262 -0
- mannf/core/agents/executor_agent.py +222 -0
- mannf/core/agents/monitor_agent.py +188 -0
- mannf/core/agents/oracle_agent.py +150 -0
- mannf/core/agents/performance_testing_agent.py +279 -0
- mannf/core/agents/planner_agent.py +128 -0
- mannf/core/agents/test_agent.py +249 -0
- mannf/core/agents/visual_regression_agent.py +311 -0
- mannf/core/agents/web_crawler_agent.py +510 -0
- mannf/core/agents/worker_pool.py +366 -0
- mannf/core/anomaly/__init__.py +14 -0
- mannf/core/anomaly/enhanced_detector.py +541 -0
- mannf/core/browser/__init__.py +63 -0
- mannf/core/browser/accessibility_scanner.py +424 -0
- mannf/core/browser/discovery_model.py +178 -0
- mannf/core/browser/dom_snapshot.py +349 -0
- mannf/core/browser/ingestor_bridge.py +371 -0
- mannf/core/browser/performance_metrics.py +217 -0
- mannf/core/browser/reflection_analyzer.py +442 -0
- mannf/core/browser/scenario_generator.py +1100 -0
- mannf/core/browser/security_scenario_generator.py +695 -0
- mannf/core/browser/visual_comparer.py +159 -0
- mannf/core/diagnostics/__init__.py +28 -0
- mannf/core/diagnostics/failure_clusterer.py +211 -0
- mannf/core/diagnostics/flake_detector.py +233 -0
- mannf/core/diagnostics/root_cause_analyzer.py +273 -0
- mannf/core/distributed/__init__.py +16 -0
- mannf/core/distributed/endpoint.py +139 -0
- mannf/core/distributed/system_under_test.py +207 -0
- mannf/core/functional_orchestrator.py +428 -0
- mannf/core/messaging/__init__.py +11 -0
- mannf/core/messaging/bus.py +113 -0
- mannf/core/messaging/messages.py +89 -0
- mannf/core/nat_orchestrator.py +342 -0
- mannf/core/neural/__init__.py +183 -0
- mannf/core/orchestrator.py +272 -0
- mannf/core/prioritization/__init__.py +17 -0
- mannf/core/prioritization/adaptive_controller.py +509 -0
- mannf/core/prioritization/belief_prioritizer.py +231 -0
- mannf/core/prioritization/risk_scorer.py +430 -0
- mannf/core/reporting/__init__.py +12 -0
- mannf/core/reporting/unified_report.py +664 -0
- mannf/core/testing/__init__.py +17 -0
- mannf/core/testing/adaptive_controller.py +149 -0
- mannf/core/testing/models.py +179 -0
- mannf/core/validation/__init__.py +10 -0
- mannf/core/validation/self_validation_runner.py +180 -0
- mannf/dashboard/__init__.py +7 -0
- mannf/dashboard/app.py +9 -0
- mannf/dashboard/models.py +9 -0
- mannf/dashboard/static/index.html +2538 -0
- mannf/dashboard/telemetry.py +9 -0
- mannf/distributed/__init__.py +7 -0
- mannf/distributed/endpoint.py +9 -0
- mannf/distributed/system_under_test.py +9 -0
- mannf/healing/__init__.py +7 -0
- mannf/healing/graphql_schema_diff.py +9 -0
- mannf/healing/healer.py +9 -0
- mannf/healing/models.py +9 -0
- mannf/healing/schema_diff.py +9 -0
- mannf/integrations/__init__.py +7 -0
- mannf/integrations/auth.py +9 -0
- mannf/integrations/graphql_parser.py +9 -0
- mannf/integrations/graphql_sut.py +9 -0
- mannf/integrations/http_sut.py +9 -0
- mannf/integrations/openapi_parser.py +9 -0
- mannf/integrations/postman_parser.py +9 -0
- mannf/llm/__init__.py +7 -0
- mannf/llm/anthropic_provider.py +9 -0
- mannf/llm/base.py +9 -0
- mannf/llm/config.py +9 -0
- mannf/llm/factory.py +9 -0
- mannf/llm/openai_provider.py +9 -0
- mannf/llm/prompts.py +9 -0
- mannf/messaging/__init__.py +7 -0
- mannf/messaging/bus.py +9 -0
- mannf/messaging/messages.py +9 -0
- mannf/nat_orchestrator.py +9 -0
- mannf/neural/__init__.py +7 -0
- mannf/orchestrator.py +9 -0
- mannf/prioritization/__init__.py +7 -0
- mannf/prioritization/adaptive_controller.py +9 -0
- mannf/prioritization/belief_prioritizer.py +9 -0
- mannf/prioritization/risk_scorer.py +9 -0
- mannf/product/__init__.py +29 -0
- mannf/product/admin/__init__.py +3 -0
- mannf/product/admin/routes.py +514 -0
- mannf/product/auth/__init__.py +5 -0
- mannf/product/auth/saml.py +212 -0
- mannf/product/billing/__init__.py +5 -0
- mannf/product/billing/audit.py +160 -0
- mannf/product/billing/feature_gates.py +180 -0
- mannf/product/billing/metering.py +179 -0
- mannf/product/billing/notifications.py +181 -0
- mannf/product/billing/plans.py +133 -0
- mannf/product/billing/rate_limits.py +35 -0
- mannf/product/billing/stripe_billing.py +906 -0
- mannf/product/billing/tenant_auth.py +233 -0
- mannf/product/billing/tenant_manager.py +873 -0
- mannf/product/cli.py +3900 -0
- mannf/product/cli_admin.py +408 -0
- mannf/product/dashboard/__init__.py +61 -0
- mannf/product/dashboard/app.py +3567 -0
- mannf/product/dashboard/models.py +460 -0
- mannf/product/dashboard/static/index.html +6347 -0
- mannf/product/dashboard/static/manifest.json +25 -0
- mannf/product/dashboard/static/pwa-icon-192.png +0 -0
- mannf/product/dashboard/static/pwa-icon-512.png +0 -0
- mannf/product/dashboard/static/sw.js +64 -0
- mannf/product/dashboard/telemetry.py +547 -0
- mannf/product/database.py +145 -0
- mannf/product/demo.py +844 -0
- mannf/product/doctor.py +509 -0
- mannf/product/exporters/__init__.py +65 -0
- mannf/product/exporters/azuredevops_exporter.py +257 -0
- mannf/product/exporters/base.py +307 -0
- mannf/product/exporters/bugzilla_exporter.py +200 -0
- mannf/product/exporters/dedup.py +275 -0
- mannf/product/exporters/finding_adapter.py +216 -0
- mannf/product/exporters/github_exporter.py +197 -0
- mannf/product/exporters/gitlab_exporter.py +215 -0
- mannf/product/exporters/jira_exporter.py +180 -0
- mannf/product/exporters/linear_exporter.py +195 -0
- mannf/product/exporters/loader.py +233 -0
- mannf/product/exporters/pagerduty_exporter.py +363 -0
- mannf/product/exporters/sentry_exporter.py +322 -0
- mannf/product/exporters/servicenow_exporter.py +240 -0
- mannf/product/exporters/shortcut_exporter.py +231 -0
- mannf/product/exporters/webhook_exporter.py +383 -0
- mannf/product/formatters/__init__.py +18 -0
- mannf/product/formatters/allure_formatter.py +161 -0
- mannf/product/formatters/ctrf_formatter.py +149 -0
- mannf/product/healing/__init__.py +30 -0
- mannf/product/healing/graphql_schema_diff.py +152 -0
- mannf/product/healing/healer.py +141 -0
- mannf/product/healing/models.py +175 -0
- mannf/product/healing/schema_diff.py +251 -0
- mannf/product/ingestors/__init__.py +77 -0
- mannf/product/ingestors/base.py +256 -0
- mannf/product/ingestors/bgstm_ingestor.py +764 -0
- mannf/product/ingestors/curl_ingestor.py +1019 -0
- mannf/product/ingestors/cypress_ingestor.py +487 -0
- mannf/product/ingestors/gherkin_ingestor.py +967 -0
- mannf/product/ingestors/graphql_ingestor.py +845 -0
- mannf/product/ingestors/grpc_ingestor.py +591 -0
- mannf/product/ingestors/har_ingestor.py +976 -0
- mannf/product/ingestors/loader.py +284 -0
- mannf/product/ingestors/models.py +146 -0
- mannf/product/ingestors/openapi_ingestor.py +606 -0
- mannf/product/ingestors/playwright_ingestor.py +449 -0
- mannf/product/ingestors/postman_ingestor.py +631 -0
- mannf/product/ingestors/traffic_ingestor.py +679 -0
- mannf/product/ingestors/websocket_ingestor.py +526 -0
- mannf/product/integrations/__init__.py +21 -0
- mannf/product/integrations/auth.py +190 -0
- mannf/product/integrations/graphql_parser.py +436 -0
- mannf/product/integrations/graphql_sut.py +247 -0
- mannf/product/integrations/grpc_sut.py +469 -0
- mannf/product/integrations/http_sut.py +237 -0
- mannf/product/integrations/kafka_adapter.py +342 -0
- mannf/product/integrations/openapi_parser.py +513 -0
- mannf/product/integrations/postman_parser.py +467 -0
- mannf/product/integrations/webhook_receiver.py +344 -0
- mannf/product/integrations/websocket_sut.py +434 -0
- mannf/product/llm/__init__.py +25 -0
- mannf/product/llm/anthropic_provider.py +94 -0
- mannf/product/llm/base.py +267 -0
- mannf/product/llm/config.py +48 -0
- mannf/product/llm/factory.py +42 -0
- mannf/product/llm/openai_provider.py +93 -0
- mannf/product/llm/prompts.py +403 -0
- mannf/product/llm/root_cause_service.py +311 -0
- mannf/product/llm/test_plan_models.py +78 -0
- mannf/product/metrics.py +149 -0
- mannf/product/middleware/__init__.py +3 -0
- mannf/product/middleware/audit_middleware.py +112 -0
- mannf/product/middleware/tenant_isolation.py +114 -0
- mannf/product/models.py +347 -0
- mannf/product/notifications/__init__.py +24 -0
- mannf/product/notifications/dispatcher.py +411 -0
- mannf/product/onboarding.py +190 -0
- mannf/product/orchestration/__init__.py +39 -0
- mannf/product/orchestration/ingest_scan_orchestrator.py +339 -0
- mannf/product/orchestration/pipeline.py +401 -0
- mannf/product/orchestrator.py +987 -0
- mannf/product/orchestrator_models.py +269 -0
- mannf/product/regression/__init__.py +36 -0
- mannf/product/regression/differ.py +172 -0
- mannf/product/regression/masking.py +100 -0
- mannf/product/regression/models.py +232 -0
- mannf/product/regression/recorder.py +124 -0
- mannf/product/regression/replayer.py +168 -0
- mannf/product/reports/__init__.py +10 -0
- mannf/product/reports/pdf.py +132 -0
- mannf/product/scheduling/__init__.py +57 -0
- mannf/product/scheduling/cron_utils.py +251 -0
- mannf/product/scheduling/engine.py +473 -0
- mannf/product/scheduling/models.py +86 -0
- mannf/product/scheduling/queue.py +894 -0
- mannf/product/scheduling/store.py +235 -0
- mannf/product/security/__init__.py +21 -0
- mannf/product/security/belief_guided.py +143 -0
- mannf/product/security/checks/__init__.py +55 -0
- mannf/product/security/checks/base.py +69 -0
- mannf/product/security/checks/bfla.py +77 -0
- mannf/product/security/checks/bola.py +77 -0
- mannf/product/security/checks/bopla.py +80 -0
- mannf/product/security/checks/broken_auth.py +86 -0
- mannf/product/security/checks/graphql_security.py +299 -0
- mannf/product/security/checks/inventory.py +70 -0
- mannf/product/security/checks/misconfig.py +158 -0
- mannf/product/security/checks/resource_consumption.py +70 -0
- mannf/product/security/checks/sensitive_flows.py +80 -0
- mannf/product/security/checks/ssrf.py +101 -0
- mannf/product/security/checks/unsafe_consumption.py +120 -0
- mannf/product/security/models.py +92 -0
- mannf/product/security/plugin_loader.py +182 -0
- mannf/product/security/reporter.py +92 -0
- mannf/product/security/scanner.py +183 -0
- mannf/product/server.py +6220 -0
- mannf/product/setup_wizard.py +873 -0
- mannf/product/status.py +404 -0
- mannf/product/storage/__init__.py +10 -0
- mannf/product/storage/artifact_store.py +343 -0
- mannf/product/telemetry.py +300 -0
- mannf/product/uninstall.py +169 -0
- mannf/product/upgrade.py +139 -0
- mannf/product/weights/__init__.py +13 -0
- mannf/product/weights/blob_store.py +299 -0
- mannf/product/weights/factory.py +42 -0
- mannf/product/weights/registry.py +159 -0
- mannf/product/weights/store.py +210 -0
- mannf/regression/__init__.py +7 -0
- mannf/regression/differ.py +9 -0
- mannf/regression/masking.py +9 -0
- mannf/regression/models.py +9 -0
- mannf/regression/recorder.py +9 -0
- mannf/regression/replayer.py +9 -0
- mannf/security/__init__.py +7 -0
- mannf/security/belief_guided.py +9 -0
- mannf/security/checks/__init__.py +7 -0
- mannf/security/checks/base.py +9 -0
- mannf/security/checks/bfla.py +9 -0
- mannf/security/checks/bola.py +9 -0
- mannf/security/checks/bopla.py +9 -0
- mannf/security/checks/broken_auth.py +9 -0
- mannf/security/checks/graphql_security.py +9 -0
- mannf/security/checks/inventory.py +9 -0
- mannf/security/checks/misconfig.py +9 -0
- mannf/security/checks/resource_consumption.py +9 -0
- mannf/security/checks/sensitive_flows.py +9 -0
- mannf/security/checks/ssrf.py +9 -0
- mannf/security/checks/unsafe_consumption.py +9 -0
- mannf/security/models.py +9 -0
- mannf/security/reporter.py +9 -0
- mannf/security/scanner.py +9 -0
- mannf/server.py +9 -0
- mannf/testing/__init__.py +7 -0
- mannf/testing/adaptive_controller.py +9 -0
- mannf/testing/models.py +9 -0
- mannf/weights/__init__.py +7 -0
- mannf/weights/registry.py +9 -0
- mannf/weights/store.py +9 -0
- nat_engine-1.dist-info/METADATA +555 -0
- nat_engine-1.dist-info/RECORD +299 -0
- nat_engine-1.dist-info/WHEEL +5 -0
- nat_engine-1.dist-info/entry_points.txt +4 -0
- nat_engine-1.dist-info/licenses/LICENSE +651 -0
- nat_engine-1.dist-info/licenses/NOTICE +178 -0
- nat_engine-1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# Copyright (C) 2026 Brad Guider
|
|
2
|
+
# This file is part of NAT (Neural Agent Testing Framework).
|
|
3
|
+
# Licensed under the AGPL-3.0. See LICENSE for details.
|
|
4
|
+
# Commercial licensing available — see COMMERCIAL_LICENSE.md.
|
|
5
|
+
|
|
6
|
+
"""Higher-level Pipeline builder for composing ingestor → orchestrator → exporter chains.
|
|
7
|
+
|
|
8
|
+
The :class:`Pipeline` class provides a fluent builder API for constructing and
|
|
9
|
+
running a complete Ingest → Prioritize → Scan → Export → Learn workflow from
|
|
10
|
+
configuration or CLI arguments.
|
|
11
|
+
|
|
12
|
+
Usage::
|
|
13
|
+
|
|
14
|
+
from mannf.product.orchestration import Pipeline
|
|
15
|
+
|
|
16
|
+
result = await (
|
|
17
|
+
Pipeline()
|
|
18
|
+
.with_source("openapi.yaml", format="openapi")
|
|
19
|
+
.with_base_url("http://localhost:8080")
|
|
20
|
+
.with_auth_token("Bearer eyJ...")
|
|
21
|
+
.with_severity_threshold("high")
|
|
22
|
+
.with_exporter("github-issues", repo="org/repo", token="ghp_...")
|
|
23
|
+
.with_feedback_path("/tmp/nat_feedback.json")
|
|
24
|
+
.run()
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
print(f"Found {len(result.scan_results.findings)} findings")
|
|
28
|
+
print(f"Exported {result.export_summaries[0].succeeded} issues")
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import logging
|
|
34
|
+
from typing import Any
|
|
35
|
+
|
|
36
|
+
from mannf.product.exporters.base import ExporterPlugin
|
|
37
|
+
from mannf.product.ingestors.models import IngestResult
|
|
38
|
+
from mannf.product.orchestration.ingest_scan_orchestrator import IngestScanOrchestrator
|
|
39
|
+
from mannf.product.orchestrator_models import (
|
|
40
|
+
OrchestrationConfig,
|
|
41
|
+
OrchestrationResult,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
logger = logging.getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Pipeline:
|
|
48
|
+
"""Fluent builder for a complete Ingest → Prioritize → Scan → Export → Learn run.
|
|
49
|
+
|
|
50
|
+
Each ``with_*`` method returns ``self`` to allow method chaining. Call
|
|
51
|
+
:meth:`run` (or :meth:`plan`) to execute the pipeline.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
config:
|
|
56
|
+
Optional pre-built :class:`~mannf.product.orchestrator_models.OrchestrationConfig`.
|
|
57
|
+
If provided, all ``with_*`` calls merge into/override this base config.
|
|
58
|
+
extra_exporters:
|
|
59
|
+
Optional additional exporter plugin instances to make available in
|
|
60
|
+
addition to the built-in exporters.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
config: OrchestrationConfig | None = None,
|
|
66
|
+
extra_exporters: list[ExporterPlugin] | None = None,
|
|
67
|
+
) -> None:
|
|
68
|
+
self._config = config or OrchestrationConfig()
|
|
69
|
+
self._extra_exporters: list[ExporterPlugin] = extra_exporters or []
|
|
70
|
+
|
|
71
|
+
# ------------------------------------------------------------------
|
|
72
|
+
# Builder methods
|
|
73
|
+
# ------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
def with_source(
|
|
76
|
+
self,
|
|
77
|
+
path_or_url: str,
|
|
78
|
+
*,
|
|
79
|
+
format: str | None = None, # noqa: A002
|
|
80
|
+
ingestor_name: str | None = None,
|
|
81
|
+
ingestor_config: dict | None = None,
|
|
82
|
+
) -> "Pipeline":
|
|
83
|
+
"""Set the ingestion source.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
path_or_url:
|
|
88
|
+
File system path or URL of the source to ingest.
|
|
89
|
+
format:
|
|
90
|
+
Optional format hint (e.g. ``"openapi"``, ``"har"``).
|
|
91
|
+
ingestor_name:
|
|
92
|
+
Optional explicit ingestor plugin name (overrides auto-detection).
|
|
93
|
+
ingestor_config:
|
|
94
|
+
Extra config dict passed to the ingestor's ``ingest()`` method.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
Pipeline
|
|
99
|
+
"""
|
|
100
|
+
if path_or_url.startswith("http://") or path_or_url.startswith("https://"):
|
|
101
|
+
self._config.source_url = path_or_url
|
|
102
|
+
self._config.source_path = None
|
|
103
|
+
else:
|
|
104
|
+
self._config.source_path = path_or_url
|
|
105
|
+
self._config.source_url = None
|
|
106
|
+
if format is not None:
|
|
107
|
+
self._config.source_format = format
|
|
108
|
+
if ingestor_name is not None:
|
|
109
|
+
self._config.ingestor_name = ingestor_name
|
|
110
|
+
if ingestor_config is not None:
|
|
111
|
+
self._config.ingestor_config = {**self._config.ingestor_config, **ingestor_config}
|
|
112
|
+
return self
|
|
113
|
+
|
|
114
|
+
def with_base_url(self, base_url: str) -> "Pipeline":
|
|
115
|
+
"""Set the API base URL for the scan engine.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
base_url:
|
|
120
|
+
The root URL of the API under test (e.g. ``"http://localhost:8080"``).
|
|
121
|
+
|
|
122
|
+
Returns
|
|
123
|
+
-------
|
|
124
|
+
Pipeline
|
|
125
|
+
"""
|
|
126
|
+
self._config.scan_config = {**self._config.scan_config, "base_url": base_url}
|
|
127
|
+
return self
|
|
128
|
+
|
|
129
|
+
def with_auth_token(self, token: str) -> "Pipeline":
|
|
130
|
+
"""Set a Bearer auth token for scan requests.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
token:
|
|
135
|
+
Bearer token (``"Bearer eyJ..."`` or just the token value).
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
Pipeline
|
|
140
|
+
"""
|
|
141
|
+
self._config.scan_config = {**self._config.scan_config, "auth_token": token}
|
|
142
|
+
return self
|
|
143
|
+
|
|
144
|
+
def with_api_key(self, api_key: str, header: str = "X-API-Key") -> "Pipeline":
|
|
145
|
+
"""Set an API key for scan requests.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
api_key:
|
|
150
|
+
The API key value.
|
|
151
|
+
header:
|
|
152
|
+
The header name to send the key in (default ``"X-API-Key"``).
|
|
153
|
+
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
Pipeline
|
|
157
|
+
"""
|
|
158
|
+
self._config.scan_config = {
|
|
159
|
+
**self._config.scan_config,
|
|
160
|
+
"api_key": api_key,
|
|
161
|
+
"api_key_header": header,
|
|
162
|
+
}
|
|
163
|
+
return self
|
|
164
|
+
|
|
165
|
+
def with_severity_threshold(self, severity: str) -> "Pipeline":
|
|
166
|
+
"""Set the minimum finding severity to include in the report.
|
|
167
|
+
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
severity:
|
|
171
|
+
One of ``"critical"``, ``"high"``, ``"medium"``, ``"low"``,
|
|
172
|
+
``"info"``.
|
|
173
|
+
|
|
174
|
+
Returns
|
|
175
|
+
-------
|
|
176
|
+
Pipeline
|
|
177
|
+
"""
|
|
178
|
+
self._config.scan_config = {
|
|
179
|
+
**self._config.scan_config,
|
|
180
|
+
"severity_threshold": severity,
|
|
181
|
+
}
|
|
182
|
+
return self
|
|
183
|
+
|
|
184
|
+
def with_exporter(self, exporter_name: str, **kwargs: Any) -> "Pipeline":
|
|
185
|
+
"""Configure an exporter plugin to run after scanning.
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
exporter_name:
|
|
190
|
+
Name of the exporter plugin (e.g. ``"github-issues"``, ``"jira"``).
|
|
191
|
+
**kwargs:
|
|
192
|
+
Exporter-specific config (e.g. ``repo="org/repo"``, ``token="ghp_..."``).
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
Pipeline
|
|
197
|
+
"""
|
|
198
|
+
self._config.exporter_config = {
|
|
199
|
+
"exporter_name": exporter_name,
|
|
200
|
+
**kwargs,
|
|
201
|
+
}
|
|
202
|
+
return self
|
|
203
|
+
|
|
204
|
+
def with_feedback_path(self, path: str) -> "Pipeline":
|
|
205
|
+
"""Set the file path to persist feedback JSON after scanning.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
path:
|
|
210
|
+
Absolute or relative path to write the feedback JSON.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
Pipeline
|
|
215
|
+
"""
|
|
216
|
+
self._config.feedback_config = {**self._config.feedback_config, "save_path": path}
|
|
217
|
+
return self
|
|
218
|
+
|
|
219
|
+
def with_belief_guided(self, enabled: bool = True) -> "Pipeline":
|
|
220
|
+
"""Enable or disable belief-guided prioritization.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
enabled:
|
|
225
|
+
Whether to use the belief-guided prioritizer (default ``True``).
|
|
226
|
+
|
|
227
|
+
Returns
|
|
228
|
+
-------
|
|
229
|
+
Pipeline
|
|
230
|
+
"""
|
|
231
|
+
self._config.scan_config = {
|
|
232
|
+
**self._config.scan_config,
|
|
233
|
+
"belief_guided": enabled,
|
|
234
|
+
}
|
|
235
|
+
return self
|
|
236
|
+
|
|
237
|
+
def with_scan_config(self, **kwargs: Any) -> "Pipeline":
|
|
238
|
+
"""Merge arbitrary key/value pairs into the scan config.
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
----------
|
|
242
|
+
**kwargs:
|
|
243
|
+
Arbitrary scan-level config overrides.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
Pipeline
|
|
248
|
+
"""
|
|
249
|
+
self._config.scan_config = {**self._config.scan_config, **kwargs}
|
|
250
|
+
return self
|
|
251
|
+
|
|
252
|
+
def with_extra_exporters(self, *exporters: ExporterPlugin) -> "Pipeline":
|
|
253
|
+
"""Register additional exporter plugin instances.
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
*exporters:
|
|
258
|
+
One or more :class:`~mannf.product.exporters.base.ExporterPlugin`
|
|
259
|
+
instances to make available alongside the built-in exporters.
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
Pipeline
|
|
264
|
+
"""
|
|
265
|
+
self._extra_exporters.extend(exporters)
|
|
266
|
+
return self
|
|
267
|
+
|
|
268
|
+
def with_llm(
|
|
269
|
+
self,
|
|
270
|
+
provider: str = "openai",
|
|
271
|
+
*,
|
|
272
|
+
model: str = "",
|
|
273
|
+
api_key: str | None = None,
|
|
274
|
+
priority_threshold: float = 0.7,
|
|
275
|
+
n_cases: int = 3,
|
|
276
|
+
**kwargs: Any,
|
|
277
|
+
) -> "Pipeline":
|
|
278
|
+
"""Enable LLM-augmented edge-case generation for high-priority endpoints.
|
|
279
|
+
|
|
280
|
+
When enabled, the orchestrator will call the configured LLM to generate
|
|
281
|
+
additional test cases for every endpoint whose priority score meets or
|
|
282
|
+
exceeds *priority_threshold* before running the security scan.
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
provider:
|
|
287
|
+
LLM provider name: ``"openai"`` or ``"anthropic"``
|
|
288
|
+
(default ``"openai"``).
|
|
289
|
+
model:
|
|
290
|
+
Model identifier. Defaults to the provider's built-in default
|
|
291
|
+
(``"gpt-4o-mini"`` for OpenAI, ``"claude-3-haiku-20240307"`` for
|
|
292
|
+
Anthropic).
|
|
293
|
+
api_key:
|
|
294
|
+
API key. When ``None``, the provider reads from the environment
|
|
295
|
+
variable (``OPENAI_API_KEY`` / ``ANTHROPIC_API_KEY``).
|
|
296
|
+
priority_threshold:
|
|
297
|
+
Minimum ``priority_score`` (0–1) for a target to receive LLM-generated
|
|
298
|
+
test cases (default ``0.7``).
|
|
299
|
+
n_cases:
|
|
300
|
+
Number of edge-case test cases to generate per qualifying endpoint
|
|
301
|
+
(default ``3``).
|
|
302
|
+
**kwargs:
|
|
303
|
+
Additional key/value pairs merged into
|
|
304
|
+
:class:`~mannf.product.llm.config.LLMConfig`.
|
|
305
|
+
|
|
306
|
+
Returns
|
|
307
|
+
-------
|
|
308
|
+
Pipeline
|
|
309
|
+
"""
|
|
310
|
+
llm_cfg: dict[str, Any] = {
|
|
311
|
+
"enabled": True,
|
|
312
|
+
"provider": provider,
|
|
313
|
+
"priority_threshold": priority_threshold,
|
|
314
|
+
"n_cases": n_cases,
|
|
315
|
+
**kwargs,
|
|
316
|
+
}
|
|
317
|
+
if model:
|
|
318
|
+
llm_cfg["model"] = model
|
|
319
|
+
if api_key is not None:
|
|
320
|
+
llm_cfg["api_key"] = api_key
|
|
321
|
+
self._config.llm_config = llm_cfg
|
|
322
|
+
return self
|
|
323
|
+
|
|
324
|
+
# ------------------------------------------------------------------
|
|
325
|
+
# Execution
|
|
326
|
+
# ------------------------------------------------------------------
|
|
327
|
+
|
|
328
|
+
async def run(self) -> OrchestrationResult:
|
|
329
|
+
"""Execute the full Ingest → Prioritize → Scan → Export → Learn pipeline.
|
|
330
|
+
|
|
331
|
+
Returns
|
|
332
|
+
-------
|
|
333
|
+
OrchestrationResult
|
|
334
|
+
Complete pipeline result.
|
|
335
|
+
"""
|
|
336
|
+
orch = IngestScanOrchestrator(self._config, self._extra_exporters)
|
|
337
|
+
return await orch.orchestrate()
|
|
338
|
+
|
|
339
|
+
async def plan(self) -> OrchestrationResult:
|
|
340
|
+
"""Dry-run mode: ingest + prioritize without scanning.
|
|
341
|
+
|
|
342
|
+
Returns
|
|
343
|
+
-------
|
|
344
|
+
OrchestrationResult
|
|
345
|
+
Pipeline result with ``scan_plan`` populated but no scan results.
|
|
346
|
+
"""
|
|
347
|
+
orch = IngestScanOrchestrator(self._config, self._extra_exporters)
|
|
348
|
+
return await orch.plan()
|
|
349
|
+
|
|
350
|
+
async def run_from_result(self, ingest_result: IngestResult) -> OrchestrationResult:
|
|
351
|
+
"""Run the pipeline from a pre-built :class:`~mannf.product.ingestors.models.IngestResult`.
|
|
352
|
+
|
|
353
|
+
Bypasses the file/URL ingest phase — useful when the caller already holds
|
|
354
|
+
an ``IngestResult`` from any ingestor plugin.
|
|
355
|
+
|
|
356
|
+
Parameters
|
|
357
|
+
----------
|
|
358
|
+
ingest_result:
|
|
359
|
+
A completed ingest result.
|
|
360
|
+
|
|
361
|
+
Returns
|
|
362
|
+
-------
|
|
363
|
+
OrchestrationResult
|
|
364
|
+
Complete pipeline result.
|
|
365
|
+
"""
|
|
366
|
+
orch = IngestScanOrchestrator(self._config, self._extra_exporters)
|
|
367
|
+
return await orch.orchestrate_from_result(ingest_result)
|
|
368
|
+
|
|
369
|
+
async def plan_from_result(self, ingest_result: IngestResult) -> OrchestrationResult:
|
|
370
|
+
"""Dry-run from a pre-built :class:`~mannf.product.ingestors.models.IngestResult`.
|
|
371
|
+
|
|
372
|
+
Parameters
|
|
373
|
+
----------
|
|
374
|
+
ingest_result:
|
|
375
|
+
A completed ingest result.
|
|
376
|
+
|
|
377
|
+
Returns
|
|
378
|
+
-------
|
|
379
|
+
OrchestrationResult
|
|
380
|
+
Pipeline result with ``scan_plan`` populated but no scan results.
|
|
381
|
+
"""
|
|
382
|
+
orch = IngestScanOrchestrator(self._config, self._extra_exporters)
|
|
383
|
+
return await orch.plan_from_result(ingest_result)
|
|
384
|
+
|
|
385
|
+
# ------------------------------------------------------------------
|
|
386
|
+
# Properties
|
|
387
|
+
# ------------------------------------------------------------------
|
|
388
|
+
|
|
389
|
+
@property
|
|
390
|
+
def config(self) -> OrchestrationConfig:
|
|
391
|
+
"""The current :class:`~mannf.product.orchestrator_models.OrchestrationConfig`."""
|
|
392
|
+
return self._config
|
|
393
|
+
|
|
394
|
+
def build(self) -> IngestScanOrchestrator:
|
|
395
|
+
"""Build and return the :class:`IngestScanOrchestrator` without running it.
|
|
396
|
+
|
|
397
|
+
Returns
|
|
398
|
+
-------
|
|
399
|
+
IngestScanOrchestrator
|
|
400
|
+
"""
|
|
401
|
+
return IngestScanOrchestrator(self._config, self._extra_exporters)
|