agentdiscover 2.9.2__tar.gz → 2.9.4__tar.gz
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.
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/PKG-INFO +1 -1
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/sample-repo/agents/crewai_agent.py +4 -1
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/sample-repo/agents/langchain_agent.py +3 -1
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/pyproject.toml +1 -1
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/aibom.py +46 -0
- agentdiscover-2.9.4/src/agent_discover_scanner/audit_reports.py +170 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/cli.py +3 -1
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/correlator.py +159 -15
- agentdiscover-2.9.4/src/agent_discover_scanner/generation_defaults.py +250 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/network_monitor.py +9 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/platform.py +23 -1
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/process_introspection.py +77 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/sarif_output.py +6 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/scan_runner.py +3 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/signatures.py +264 -1
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/visitor.py +22 -0
- agentdiscover-2.9.4/tests/__init__.py +0 -0
- agentdiscover-2.9.4/tests/test_declared_model.py +322 -0
- agentdiscover-2.9.4/tests/test_generation_params.py +421 -0
- agentdiscover-2.9.2/src/agent_discover_scanner/audit_reports.py +0 -83
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/.claude/commands/grow-scanner.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/.github/workflows/aibom.yml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/.github/workflows/ci.yml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/.github/workflows/scan.yml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/.gitignore +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/.python-version +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/CHANGELOG.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/CLAUDE.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/COMMIT_NOTES.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/CONTRIBUTING.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/Dockerfile +0 -0
- /agentdiscover-2.9.2/src/agent_discover_scanner/detectors/__init__.py → /agentdiscover-2.9.4/FETCH_HEAD +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/LICENSE +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/README.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/README_BADGES.txt +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/RELEASE_NOTES.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/ROADMAP.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/SECURITY.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/action.yml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/k8s/agents-configmap.yaml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/k8s/crewai-agent.yaml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/k8s/langchain-agent.yaml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/k8s/shadow-agent.yaml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/mcp-governance/DESIGN.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/mcp-governance/README.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/mcp-governance/sample-policy.yaml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/run-demo.sh +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/sample-repo/agents/autogen_agent.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/sample-repo/agents/direct_client.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/sample-repo/requirements.txt +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/demo/setup.sh +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/deployment/kubernetes/tracing-policy.yaml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/deployment/systemd/README.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/deployment/systemd/agent-discover-scanner.service +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docker-compose.yml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/EXAMPLES.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/TETRAGON_SETUP.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/architecture.svg +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/comparisons/vs-grep.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/comparisons/vs-nudge-security.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/ghost-detection.svg +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/integrations/elastic.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/integrations/github-actions.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/integrations/kubernetes-daemonset.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/integrations/splunk.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/known-apps-example.txt +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/layer4-setup.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/docs/nist-mapping.md +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/examples/k8s/test-workload.yaml +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/examples/tetragon-sample-events.json +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/install.sh +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/__init__.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/behavioral_patterns.py +0 -0
- {agentdiscover-2.9.2/src/agent_discover_scanner/exporters → agentdiscover-2.9.4/src/agent_discover_scanner/detectors}/__init__.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/cloud_audit/__init__.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/cloud_audit/aws_cloudtrail.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/cloud_audit/azure_monitor.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/cloud_audit/base.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/cloud_audit/gcp_audit.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/cloudtrail.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/sse_proxy/__init__.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/sse_proxy/base.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/sse_proxy/netskope.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/sse_proxy/prisma_access.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/detectors/sse_proxy/zscaler_zia.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/errors.py +0 -0
- {agentdiscover-2.9.2/src/agent_discover_scanner/interceptors/sse → agentdiscover-2.9.4/src/agent_discover_scanner/exporters}/__init__.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/exporters/mcpfw_policy.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/git_scanner.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/high_risk_agents.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/interceptors/__init__.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/interceptors/base.py +0 -0
- {agentdiscover-2.9.2/src/agent_discover_scanner/layer4 → agentdiscover-2.9.4/src/agent_discover_scanner/interceptors/sse}/__init__.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/interceptors/sse/netskope.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/interceptors/sse/prisma_access.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/interceptors/sse/umbrella.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/interceptors/sse/zscaler.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/js_signatures.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/known_apps.py +0 -0
- {agentdiscover-2.9.2/tests → agentdiscover-2.9.4/src/agent_discover_scanner/layer4}/__init__.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/layer4/osquery_executor.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/layer4/osquery_queries.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/layer4/result_parser.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/macos_detector.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/mcp_detector.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/models/endpoint_discovery.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/monitors/__init__.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/monitors/json_output.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/monitors/k8s_monitor.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/monitors/tetragon_events.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/monitors/tetragon_monitor.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/monitors/vendor_mapping.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/reports/layer4_report.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/saas_detector.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/sbom_analyzer.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/scanner.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/src/agent_discover_scanner/windows_detector.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/autogen_safe.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/autogen_unsafe.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/bedrock_safe.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/bedrock_unsafe.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/clean_code.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/crewai_safe.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/crewai_unsafe.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/langchain_agents.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/langchain_js_agent.js +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/langgraph_workflow.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/safe_with_gateway.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/shadow_anthropic.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/shadow_openai.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/fixtures/shadow_openai_js.js +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_aibom.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_audit_bundle.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_bedrock_signatures.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_behavioral_patterns.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_cloud_audit_layer5.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_cloudtrail.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_cloudtrail_wiring.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_correlator.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_correlator_layer5.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_forward_dns.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_git_scanner.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_install.sh +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_interceptors.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_l2_process_introspection.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_mcpfw_export.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_network_monitor.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_platform_enrichment.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_scanner.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/tests/test_sse_proxy.py +0 -0
- {agentdiscover-2.9.2 → agentdiscover-2.9.4}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentdiscover
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.4
|
|
4
4
|
Summary: Discover every AI agent in your infrastructure. 5-layer detection: static analysis, network monitoring, eBPF/Kubernetes runtime, endpoint, and cloud audit (CloudTrail). Company-level correlation. AIBOM export. MCP server detection.
|
|
5
5
|
Project-URL: Homepage, https://defendai.ai
|
|
6
6
|
Project-URL: Documentation, https://github.com/Defend-AI-Tech-Inc/agent-discover-scanner/blob/main/README.md
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
from crewai import Agent, Task, Crew
|
|
2
2
|
|
|
3
|
+
MODEL_NAME = "gpt-4-turbo"
|
|
4
|
+
|
|
3
5
|
researcher = Agent(
|
|
4
6
|
role="Researcher",
|
|
5
7
|
goal="Research topics thoroughly",
|
|
6
8
|
backstory="Expert researcher",
|
|
7
|
-
allow_code_execution=False
|
|
9
|
+
allow_code_execution=False,
|
|
10
|
+
model=MODEL_NAME,
|
|
8
11
|
)
|
|
9
12
|
|
|
10
13
|
task = Task(description="Research AI security", agent=researcher)
|
|
@@ -3,7 +3,9 @@ from langchain.llms import OpenAI
|
|
|
3
3
|
|
|
4
4
|
llm = OpenAI(temperature=0)
|
|
5
5
|
tools = [Tool(name="search", func=lambda x: x, description="search")]
|
|
6
|
-
agent = initialize_agent(
|
|
6
|
+
agent = initialize_agent(
|
|
7
|
+
tools, llm, agent="zero-shot-react-description", model="gpt-4"
|
|
8
|
+
)
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
def run(query: str):
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentdiscover"
|
|
7
|
-
version = "2.9.
|
|
7
|
+
version = "2.9.4"
|
|
8
8
|
description = "Discover every AI agent in your infrastructure. 5-layer detection: static analysis, network monitoring, eBPF/Kubernetes runtime, endpoint, and cloud audit (CloudTrail). Company-level correlation. AIBOM export. MCP server detection."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -52,6 +52,52 @@ def generate_aibom(inventory_json: Path, output_path: Path) -> dict[str, Any]:
|
|
|
52
52
|
"value": ",".join(str(x) for x in layers),
|
|
53
53
|
}
|
|
54
54
|
)
|
|
55
|
+
if agent.get("declared_model"):
|
|
56
|
+
comp["properties"].append(
|
|
57
|
+
{
|
|
58
|
+
"name": "agent-discover:declared_model",
|
|
59
|
+
"value": str(agent["declared_model"]),
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
if agent.get("declared_model_source"):
|
|
63
|
+
comp["properties"].append(
|
|
64
|
+
{
|
|
65
|
+
"name": "agent-discover:declared_model_source",
|
|
66
|
+
"value": str(agent["declared_model_source"]),
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
if agent.get("runtime_env_model"):
|
|
70
|
+
comp["properties"].append(
|
|
71
|
+
{
|
|
72
|
+
"name": "agent-discover:runtime_env_model",
|
|
73
|
+
"value": str(agent["runtime_env_model"]),
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
# Generation/sampling parameters (facts only — provenance-tagged raw
|
|
77
|
+
# values, same conditional-append pattern as declared_model above).
|
|
78
|
+
# Threshold flags (HIGH_TEMPERATURE/UNBOUNDED_MAX_TOKENS/ZERO_TEMPERATURE)
|
|
79
|
+
# are deliberately NEVER written here — a formal BOM artifact shouldn't
|
|
80
|
+
# carry subjective/informational flags; see audit_reports.py instead.
|
|
81
|
+
generation_params = agent.get("generation_params")
|
|
82
|
+
if isinstance(generation_params, dict):
|
|
83
|
+
for param_name, param_data in generation_params.items():
|
|
84
|
+
if not isinstance(param_data, dict):
|
|
85
|
+
continue
|
|
86
|
+
source = param_data.get("source")
|
|
87
|
+
if source:
|
|
88
|
+
comp["properties"].append(
|
|
89
|
+
{
|
|
90
|
+
"name": f"agent-discover:declared_{param_name}_source",
|
|
91
|
+
"value": str(source),
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
if "value" in param_data and param_data["value"] is not None:
|
|
95
|
+
comp["properties"].append(
|
|
96
|
+
{
|
|
97
|
+
"name": f"agent-discover:declared_{param_name}",
|
|
98
|
+
"value": str(param_data["value"]),
|
|
99
|
+
}
|
|
100
|
+
)
|
|
55
101
|
components.append(comp)
|
|
56
102
|
|
|
57
103
|
try:
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Markdown reports for the audit command."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def write_ghost_agents_markdown(inventory_json: Path, dest: Path) -> None:
|
|
10
|
+
data = json.loads(inventory_json.read_text(encoding="utf-8"))
|
|
11
|
+
ghosts = (data.get("inventory") or {}).get("ghost") or []
|
|
12
|
+
lines = [
|
|
13
|
+
"# Ghost agents",
|
|
14
|
+
"",
|
|
15
|
+
"Runtime signals without matching source inventory (from latest correlation).",
|
|
16
|
+
"",
|
|
17
|
+
]
|
|
18
|
+
if not ghosts:
|
|
19
|
+
lines.append("_No ghost agents in this run._")
|
|
20
|
+
else:
|
|
21
|
+
for g in ghosts:
|
|
22
|
+
lines.append(f"## {g.get('agent_id', 'unknown')}")
|
|
23
|
+
lines.append("")
|
|
24
|
+
lines.append(f"- **Risk:** {g.get('risk_level', '')}")
|
|
25
|
+
lines.append(f"- **Provider / framework:** {g.get('network_provider') or g.get('framework') or '—'}")
|
|
26
|
+
lines.append(f"- **Process:** {g.get('process_name') or '—'}")
|
|
27
|
+
lines.append(f"- **Last seen:** {g.get('last_seen') or '—'}")
|
|
28
|
+
lines.append("")
|
|
29
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
30
|
+
dest.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def write_mcp_markdown(mcp_result: dict[str, Any], dest: Path) -> None:
|
|
34
|
+
servers = mcp_result.get("servers") or []
|
|
35
|
+
lines = [
|
|
36
|
+
"# MCP servers",
|
|
37
|
+
"",
|
|
38
|
+
"Detected from configuration, process, registry, and optional network/endpoint context.",
|
|
39
|
+
"",
|
|
40
|
+
"[MCP] Enforce policies on detected servers → pip install mcpfw-defendai | mcpfw.dev",
|
|
41
|
+
"",
|
|
42
|
+
]
|
|
43
|
+
if not servers:
|
|
44
|
+
lines.append("_No MCP servers detected in this run._")
|
|
45
|
+
else:
|
|
46
|
+
for s in servers:
|
|
47
|
+
name = s.get("server_name") or s.get("name") or "unknown"
|
|
48
|
+
lines.append(f"## {name}")
|
|
49
|
+
lines.append("")
|
|
50
|
+
lines.append(f"- **Vendor:** {s.get('vendor', '—')}")
|
|
51
|
+
lines.append(f"- **Publisher verified:** {s.get('publisher_verified', False)}")
|
|
52
|
+
lines.append(f"- **Local script:** {s.get('is_local_script', False)}")
|
|
53
|
+
lines.append("")
|
|
54
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
55
|
+
dest.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_GENERATION_PARAM_ORDER = (
|
|
59
|
+
"temperature",
|
|
60
|
+
"top_p",
|
|
61
|
+
"top_k",
|
|
62
|
+
"max_tokens",
|
|
63
|
+
"frequency_penalty",
|
|
64
|
+
"presence_penalty",
|
|
65
|
+
"stop",
|
|
66
|
+
"seed",
|
|
67
|
+
"n",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def write_generation_params_markdown(inventory_json: Path, dest: Path) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Write generation/sampling parameter facts + informational threshold flags.
|
|
74
|
+
|
|
75
|
+
This report is intentionally separate from ghost-agents.md / the SARIF
|
|
76
|
+
security findings: HIGH_TEMPERATURE / UNBOUNDED_MAX_TOKENS / ZERO_TEMPERATURE
|
|
77
|
+
are opinions about configuration, not security findings, and must never
|
|
78
|
+
influence risk_level or agent classification (see correlator.py /
|
|
79
|
+
generation_defaults.py). This file is the only place they are surfaced.
|
|
80
|
+
"""
|
|
81
|
+
data = json.loads(inventory_json.read_text(encoding="utf-8"))
|
|
82
|
+
inventory = data.get("inventory") or {}
|
|
83
|
+
|
|
84
|
+
agents_with_params = []
|
|
85
|
+
for classification, agents in inventory.items():
|
|
86
|
+
if not isinstance(agents, list):
|
|
87
|
+
continue
|
|
88
|
+
for agent in agents:
|
|
89
|
+
if isinstance(agent, dict) and agent.get("generation_params"):
|
|
90
|
+
agents_with_params.append((classification, agent))
|
|
91
|
+
|
|
92
|
+
lines = [
|
|
93
|
+
"# Generation / sampling parameters",
|
|
94
|
+
"",
|
|
95
|
+
"Raw detected values for temperature, top_p, top_k, max_tokens, "
|
|
96
|
+
"frequency_penalty, presence_penalty, stop, seed, and n, with honest "
|
|
97
|
+
"provenance per parameter — explicit (found in code), framework_default "
|
|
98
|
+
"(backfilled from a maintained provider/wrapper table), not_applicable "
|
|
99
|
+
"(fixed/rejected by the model family), or unknown (no reliable default "
|
|
100
|
+
"applies).",
|
|
101
|
+
"",
|
|
102
|
+
"**This report is informational only.** Flags below (HIGH_TEMPERATURE, "
|
|
103
|
+
"UNBOUNDED_MAX_TOKENS, ZERO_TEMPERATURE) are configuration observations, "
|
|
104
|
+
"not security findings — they do NOT affect risk_level, agent "
|
|
105
|
+
"classification, or `aibom.json`. No prescriptive \"recommended value\" "
|
|
106
|
+
"is given; that is a deliberately separate, out-of-scope decision.",
|
|
107
|
+
"",
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
if not agents_with_params:
|
|
111
|
+
lines.append("_No generation/sampling parameters detected in this run._")
|
|
112
|
+
else:
|
|
113
|
+
for classification, agent in agents_with_params:
|
|
114
|
+
lines.append(f"## {agent.get('agent_id', 'unknown')}")
|
|
115
|
+
lines.append("")
|
|
116
|
+
lines.append(f"- **Classification:** {classification}")
|
|
117
|
+
lines.append(f"- **Framework:** {agent.get('framework') or '—'}")
|
|
118
|
+
if agent.get("declared_model"):
|
|
119
|
+
lines.append(f"- **Declared model:** {agent['declared_model']}")
|
|
120
|
+
lines.append("")
|
|
121
|
+
lines.append("| Parameter | Value | Provenance |")
|
|
122
|
+
lines.append("|---|---|---|")
|
|
123
|
+
params = agent.get("generation_params") or {}
|
|
124
|
+
for param_name in _GENERATION_PARAM_ORDER:
|
|
125
|
+
param_data = params.get(param_name)
|
|
126
|
+
if not isinstance(param_data, dict):
|
|
127
|
+
continue
|
|
128
|
+
source = param_data.get("source", "unknown")
|
|
129
|
+
value = param_data.get("value", "—")
|
|
130
|
+
lines.append(f"| `{param_name}` | {value} | {source} |")
|
|
131
|
+
lines.append("")
|
|
132
|
+
flags = agent.get("generation_param_flags") or []
|
|
133
|
+
if flags:
|
|
134
|
+
lines.append("**Notes:**")
|
|
135
|
+
lines.append("")
|
|
136
|
+
for flag in flags:
|
|
137
|
+
lines.append(f"- `{flag.get('flag')}`: {flag.get('message')}")
|
|
138
|
+
lines.append("")
|
|
139
|
+
|
|
140
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
141
|
+
dest.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def write_audit_summary(report: dict[str, Any], dest: Path, raw_dir: Path) -> None:
|
|
145
|
+
s = report.get("summary") or {}
|
|
146
|
+
lines = [
|
|
147
|
+
"# Audit summary",
|
|
148
|
+
"",
|
|
149
|
+
f"**Generated:** {report.get('generated_at', '')}",
|
|
150
|
+
"",
|
|
151
|
+
"## Counts",
|
|
152
|
+
"",
|
|
153
|
+
f"- Confirmed: {s.get('confirmed', 0)}",
|
|
154
|
+
f"- Unknown: {s.get('unknown', 0)}",
|
|
155
|
+
f"- Ghost: {s.get('ghost', 0)}",
|
|
156
|
+
f"- Zombie: {s.get('zombie', 0)}",
|
|
157
|
+
f"- Shadow AI usage: {s.get('shadow_ai_usage', 0)}",
|
|
158
|
+
"",
|
|
159
|
+
"## Artifacts",
|
|
160
|
+
"",
|
|
161
|
+
f"- Raw scan directory: `{raw_dir}`",
|
|
162
|
+
"- `aibom.json` — CycloneDX-oriented AIBOM",
|
|
163
|
+
"- `ghost-agents.md` — Ghost agent detail",
|
|
164
|
+
"- `mcp-report.md` — MCP inventory",
|
|
165
|
+
"- `generation-params.md` — Generation/sampling parameter facts (informational)",
|
|
166
|
+
"- `summary.md` — This file",
|
|
167
|
+
"",
|
|
168
|
+
]
|
|
169
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
170
|
+
dest.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
@@ -1012,11 +1012,12 @@ def audit(
|
|
|
1012
1012
|
):
|
|
1013
1013
|
"""
|
|
1014
1014
|
Run the full scan-all pipeline into output/raw/, then write AIBOM JSON and Markdown reports
|
|
1015
|
-
(ghost-agents.md, mcp-report.md, summary.md) beside raw/.
|
|
1015
|
+
(ghost-agents.md, mcp-report.md, generation-params.md, summary.md) beside raw/.
|
|
1016
1016
|
"""
|
|
1017
1017
|
from agent_discover_scanner.aibom import generate_aibom
|
|
1018
1018
|
from agent_discover_scanner.audit_reports import (
|
|
1019
1019
|
write_audit_summary,
|
|
1020
|
+
write_generation_params_markdown,
|
|
1020
1021
|
write_ghost_agents_markdown,
|
|
1021
1022
|
write_mcp_markdown,
|
|
1022
1023
|
)
|
|
@@ -1068,6 +1069,7 @@ def audit(
|
|
|
1068
1069
|
|
|
1069
1070
|
generate_aibom(inv_path, output / "aibom.json")
|
|
1070
1071
|
write_ghost_agents_markdown(inv_path, output / "ghost-agents.md")
|
|
1072
|
+
write_generation_params_markdown(inv_path, output / "generation-params.md")
|
|
1071
1073
|
write_audit_summary(report, output / "summary.md", raw_dir)
|
|
1072
1074
|
|
|
1073
1075
|
nf: list = []
|
|
@@ -13,6 +13,12 @@ from typing import Any, Dict, List, Optional, Tuple
|
|
|
13
13
|
|
|
14
14
|
import os
|
|
15
15
|
|
|
16
|
+
from agent_discover_scanner.generation_defaults import (
|
|
17
|
+
classify_wrapper_key,
|
|
18
|
+
compute_threshold_flags,
|
|
19
|
+
has_any_signal,
|
|
20
|
+
resolve_generation_params,
|
|
21
|
+
)
|
|
16
22
|
from agent_discover_scanner.known_apps import build_known_apps, is_known_desktop_app
|
|
17
23
|
|
|
18
24
|
|
|
@@ -131,6 +137,45 @@ class AgentInventoryItem:
|
|
|
131
137
|
saas_connections: Dict[str, Any] = field(default_factory=dict)
|
|
132
138
|
risk_flags: List[str] = field(default_factory=list)
|
|
133
139
|
|
|
140
|
+
# Model identification (v2.7.1+) — local, free-pipeline model identity tiers.
|
|
141
|
+
# Distinct from (and never merged with) Layer 5's invocation-confirmed
|
|
142
|
+
# model_id / metadata.models_called, which remains the highest-confidence tier.
|
|
143
|
+
#
|
|
144
|
+
# declared_model / declared_model_source: Layer 1 static analysis — a model
|
|
145
|
+
# identifier found via source (see signatures.py: extract_declared_model).
|
|
146
|
+
# declared_model_source is "literal" (string constant kwarg) or
|
|
147
|
+
# "resolved_constant" (kwarg was a Name resolved to a same-file constant).
|
|
148
|
+
declared_model: Optional[str] = None
|
|
149
|
+
declared_model_source: Optional[str] = None
|
|
150
|
+
# runtime_env_model / runtime_env_source: Layer 2 live process introspection —
|
|
151
|
+
# read from the actual environment of the running process (higher confidence
|
|
152
|
+
# than declared_model since it reflects live process memory, not source code).
|
|
153
|
+
# runtime_env_source is the matched env var name (e.g. "MODEL_NAME").
|
|
154
|
+
runtime_env_model: Optional[str] = None
|
|
155
|
+
runtime_env_source: Optional[str] = None
|
|
156
|
+
|
|
157
|
+
# Generation/sampling parameters (v2.10.0+) — Layer 1 static analysis.
|
|
158
|
+
# Facts only: raw values with honest provenance, one entry per tracked
|
|
159
|
+
# parameter (see generation_defaults.ALL_GENERATION_PARAMS), e.g.
|
|
160
|
+
# {"temperature": {"value": 0.7, "source": "framework_default"},
|
|
161
|
+
# "max_tokens": {"source": "unknown"}, ...}
|
|
162
|
+
# `source` is one of "literal" / "resolved_constant" (both mean: found
|
|
163
|
+
# explicitly in code — the "explicit" tier), "framework_default"
|
|
164
|
+
# (backfilled from the maintained defaults table), "not_applicable"
|
|
165
|
+
# (fixed/rejected by this provider+wrapper/model family — never has a
|
|
166
|
+
# `value`), or "unknown" (not found, no reliable default — never has a
|
|
167
|
+
# `value`). Only populated when there's real signal (a resolvable
|
|
168
|
+
# provider+wrapper OR at least one explicit value) — see
|
|
169
|
+
# generation_defaults.has_any_signal.
|
|
170
|
+
generation_params: Optional[Dict[str, Dict[str, Any]]] = None
|
|
171
|
+
# Threshold flags derived from generation_params (HIGH_TEMPERATURE,
|
|
172
|
+
# UNBOUNDED_MAX_TOKENS, ZERO_TEMPERATURE) — INFORMATIONAL ONLY.
|
|
173
|
+
# Deliberately never merged into risk_level/risk_flags/classification, and
|
|
174
|
+
# deliberately excluded from aibom.json (a formal BOM artifact shouldn't
|
|
175
|
+
# carry subjective flags). Consumed only by audit_reports.py's
|
|
176
|
+
# generation-params.md.
|
|
177
|
+
generation_param_flags: List[Dict[str, str]] = field(default_factory=list)
|
|
178
|
+
|
|
134
179
|
def __post_init__(self):
|
|
135
180
|
if self.discovered_at is None:
|
|
136
181
|
self.discovered_at = datetime.now().isoformat()
|
|
@@ -211,12 +256,24 @@ def _populate_saas_and_risk_flags(
|
|
|
211
256
|
|
|
212
257
|
risk_flags = []
|
|
213
258
|
detected = saas.get("detected", [])
|
|
259
|
+
is_shadow = item.classification == "shadow_ai_usage"
|
|
260
|
+
|
|
214
261
|
if item.classification == "ghost" and detected:
|
|
215
262
|
risk_flags.append("critical_ghost")
|
|
216
|
-
|
|
263
|
+
|
|
264
|
+
# cloud_credentials_present and database_credentials_present require evidence of
|
|
265
|
+
# actual credentials (API keys, env vars, .env files) — not just network connections
|
|
266
|
+
# to cloud services. Shadow AI agents are browsers / desktop apps: their network
|
|
267
|
+
# traffic to Azure/GCP/AWS is normal browsing, not credential usage. Only flag
|
|
268
|
+
# when credential_files_found exist, or when the agent is code-backed (non-shadow).
|
|
269
|
+
has_actual_cred_evidence = bool(
|
|
270
|
+
saas.get("credential_files_found") or (not is_shadow)
|
|
271
|
+
)
|
|
272
|
+
if saas.get("has_cloud_provider") and has_actual_cred_evidence:
|
|
217
273
|
risk_flags.append("cloud_credentials_present")
|
|
218
|
-
if saas.get("has_database_access"):
|
|
274
|
+
if saas.get("has_database_access") and has_actual_cred_evidence:
|
|
219
275
|
risk_flags.append("database_credentials_present")
|
|
276
|
+
|
|
220
277
|
if saas.get("has_external_api_calls") and item.classification == "unknown":
|
|
221
278
|
risk_flags.append("unverified_saas_access")
|
|
222
279
|
item.risk_flags = risk_flags
|
|
@@ -246,19 +303,23 @@ class CorrelationEngine:
|
|
|
246
303
|
findings = []
|
|
247
304
|
for run in sarif.get("runs", []):
|
|
248
305
|
for result in run.get("results", []):
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
"
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
"
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
306
|
+
finding = {
|
|
307
|
+
"rule_id": result.get("ruleId"),
|
|
308
|
+
"file_path": result["locations"][0]["physicalLocation"][
|
|
309
|
+
"artifactLocation"
|
|
310
|
+
]["uri"],
|
|
311
|
+
"line": result["locations"][0]["physicalLocation"]["region"][
|
|
312
|
+
"startLine"
|
|
313
|
+
],
|
|
314
|
+
"message": result["message"]["text"],
|
|
315
|
+
"level": result.get("level", "warning"),
|
|
316
|
+
}
|
|
317
|
+
# Structured data (e.g. declared_model tier) carried via SARIF's
|
|
318
|
+
# standard properties bag — see sarif_output.py / signatures.py.
|
|
319
|
+
properties = result.get("properties")
|
|
320
|
+
if properties:
|
|
321
|
+
finding["extracted"] = properties
|
|
322
|
+
findings.append(finding)
|
|
262
323
|
|
|
263
324
|
return findings
|
|
264
325
|
except (json.JSONDecodeError, KeyError):
|
|
@@ -692,6 +753,12 @@ class CorrelationEngine:
|
|
|
692
753
|
"framework_confidence": nf.get("framework_confidence"), # "high"/"medium"/None
|
|
693
754
|
"entry_script": nf.get("entry_script"), # resolved script path
|
|
694
755
|
"detected_host": nf.get("detected_host"), # DNS-correlated hostname
|
|
756
|
+
# runtime_env_model tier — read live from the process's own
|
|
757
|
+
# environment via psutil (process_introspection.py). Highest
|
|
758
|
+
# local-scan confidence: reflects what the process actually
|
|
759
|
+
# has in memory, not what's hardcoded in source.
|
|
760
|
+
"runtime_env_model": nf.get("runtime_env_model"),
|
|
761
|
+
"runtime_env_source": nf.get("runtime_env_source"),
|
|
695
762
|
}
|
|
696
763
|
|
|
697
764
|
# Index Layer 3 by provider (first match per provider for filling k8s fields)
|
|
@@ -711,8 +778,44 @@ class CorrelationEngine:
|
|
|
711
778
|
# Track which Layer 3 findings were matched to a code finding (for GHOST: unmatched = ghost)
|
|
712
779
|
matched_l3_keys: set = set() # (namespace, pod) tuples
|
|
713
780
|
|
|
781
|
+
# DAI008 (declared_model) findings are a metadata-enrichment signal, not an
|
|
782
|
+
# independent agent signal — they never become their own inventory item.
|
|
783
|
+
# Instead, index them by (file_path, line) so their `extracted` payload can
|
|
784
|
+
# be merged onto a co-located framework/provider finding (DAI001-DAI007 at
|
|
785
|
+
# the same call site) below.
|
|
786
|
+
model_by_location: Dict[Tuple[str, int], Dict] = {}
|
|
787
|
+
for cf in code_findings:
|
|
788
|
+
if cf.get("rule_id") == "DAI008" and cf.get("extracted", {}).get("declared_model"):
|
|
789
|
+
model_by_location[(cf.get("file_path"), cf.get("line"))] = cf["extracted"]
|
|
790
|
+
|
|
791
|
+
# DAI009 (generation params) findings are likewise metadata-enrichment,
|
|
792
|
+
# never an independent agent signal. Unlike DAI008 (matched by exact
|
|
793
|
+
# file+line), generation params are commonly set on a *different* call
|
|
794
|
+
# than the one that anchors the primary framework finding (e.g. the
|
|
795
|
+
# LLM wrapper constructor `ChatOpenAI(temperature=0.7)` vs. the
|
|
796
|
+
# `initialize_agent(...)` call it's passed into) — so these are
|
|
797
|
+
# indexed by file only and merged (union across all DAI009 findings
|
|
798
|
+
# in that file) onto every primary finding in the same file below.
|
|
799
|
+
genparams_by_file: Dict[str, Dict[str, Dict]] = {}
|
|
800
|
+
wrapper_hint_by_file: Dict[str, str] = {}
|
|
801
|
+
for cf in code_findings:
|
|
802
|
+
if cf.get("rule_id") not in ("DAI008", "DAI009"):
|
|
803
|
+
continue
|
|
804
|
+
extracted = cf.get("extracted") or {}
|
|
805
|
+
file_path = cf.get("file_path")
|
|
806
|
+
if not file_path:
|
|
807
|
+
continue
|
|
808
|
+
if extracted.get("wrapper_hint"):
|
|
809
|
+
wrapper_hint_by_file[file_path] = extracted["wrapper_hint"]
|
|
810
|
+
if cf.get("rule_id") == "DAI009":
|
|
811
|
+
found = extracted.get("generation_params") or {}
|
|
812
|
+
genparams_by_file.setdefault(file_path, {}).update(found)
|
|
813
|
+
|
|
714
814
|
# Process code findings (Layer 1)
|
|
715
815
|
for cf in code_findings:
|
|
816
|
+
if cf.get("rule_id") in ("DAI008", "DAI009"):
|
|
817
|
+
continue # metadata-only; merged into same-file findings above
|
|
818
|
+
|
|
716
819
|
agent_id = f"{cf['file_path']}:{cf['line']}"
|
|
717
820
|
framework = cls.extract_framework_from_rule(cf["rule_id"])
|
|
718
821
|
|
|
@@ -730,6 +833,31 @@ class CorrelationEngine:
|
|
|
730
833
|
|
|
731
834
|
possible = cls._code_finding_providers(cf)
|
|
732
835
|
|
|
836
|
+
# declared_model tier (Layer 1, static): either this finding directly
|
|
837
|
+
# carries `extracted` (e.g. Bedrock model-ID string literal), or a
|
|
838
|
+
# co-located DAI008 finding at the same file+line does.
|
|
839
|
+
_model_extracted = cf.get("extracted") or model_by_location.get(
|
|
840
|
+
(cf.get("file_path"), cf.get("line"))
|
|
841
|
+
)
|
|
842
|
+
declared_model = (_model_extracted or {}).get("declared_model")
|
|
843
|
+
declared_model_source = (_model_extracted or {}).get("declared_model_source")
|
|
844
|
+
|
|
845
|
+
# Generation/sampling parameters (facts, informational flags are separate
|
|
846
|
+
# — see module docstring on AgentInventoryItem.generation_param_flags).
|
|
847
|
+
_file_path_key = cf.get("file_path")
|
|
848
|
+
_explicit_genparams = genparams_by_file.get(_file_path_key, {})
|
|
849
|
+
_wrapper_hint = wrapper_hint_by_file.get(_file_path_key)
|
|
850
|
+
generation_params = None
|
|
851
|
+
generation_param_flags: List[Dict[str, str]] = []
|
|
852
|
+
_wrapper_key = classify_wrapper_key(
|
|
853
|
+
cf.get("rule_id"), cf.get("message"), _wrapper_hint, declared_model
|
|
854
|
+
)
|
|
855
|
+
if has_any_signal(_wrapper_key, _explicit_genparams):
|
|
856
|
+
generation_params = resolve_generation_params(
|
|
857
|
+
_wrapper_key, declared_model, _explicit_genparams
|
|
858
|
+
)
|
|
859
|
+
generation_param_flags = compute_threshold_flags(generation_params)
|
|
860
|
+
|
|
733
861
|
# Layer 5 enrichment locals — filled by supplemental blocks below.
|
|
734
862
|
model_id = None
|
|
735
863
|
l5_framework = None
|
|
@@ -812,6 +940,8 @@ class CorrelationEngine:
|
|
|
812
940
|
l2_entry_script: str | None = None
|
|
813
941
|
l2_detected_host: str | None = None
|
|
814
942
|
l2_framework_confidence: str | None = None
|
|
943
|
+
l2_runtime_env_model: str | None = None
|
|
944
|
+
l2_runtime_env_source: str | None = None
|
|
815
945
|
|
|
816
946
|
if match_provider:
|
|
817
947
|
if match_provider in active_providers:
|
|
@@ -822,6 +952,8 @@ class CorrelationEngine:
|
|
|
822
952
|
l2_entry_script = info.get("entry_script")
|
|
823
953
|
l2_detected_host = info.get("detected_host")
|
|
824
954
|
l2_framework_confidence = info.get("framework_confidence")
|
|
955
|
+
l2_runtime_env_model = info.get("runtime_env_model")
|
|
956
|
+
l2_runtime_env_source = info.get("runtime_env_source")
|
|
825
957
|
network_provider = match_provider
|
|
826
958
|
if matched_l3 is not None:
|
|
827
959
|
k8s_pod = matched_l3.get("pod")
|
|
@@ -910,6 +1042,12 @@ class CorrelationEngine:
|
|
|
910
1042
|
detected_hosts=[l2_detected_host] if l2_detected_host else [],
|
|
911
1043
|
framework_confidence=l2_framework_confidence,
|
|
912
1044
|
evidence=_build_evidence_tags(detection_layers, l2_entry_script, l2_detected_host),
|
|
1045
|
+
declared_model=declared_model,
|
|
1046
|
+
declared_model_source=declared_model_source,
|
|
1047
|
+
runtime_env_model=l2_runtime_env_model,
|
|
1048
|
+
runtime_env_source=l2_runtime_env_source,
|
|
1049
|
+
generation_params=generation_params,
|
|
1050
|
+
generation_param_flags=generation_param_flags,
|
|
913
1051
|
)
|
|
914
1052
|
try:
|
|
915
1053
|
_file_path = (cf.get("file_path") or "") or ""
|
|
@@ -943,6 +1081,8 @@ class CorrelationEngine:
|
|
|
943
1081
|
entry_script = info.get("entry_script") # resolved script path
|
|
944
1082
|
detected_host = info.get("detected_host") # DNS-correlated hostname
|
|
945
1083
|
l2_fw_confidence = info.get("framework_confidence") # "high"/"medium"/None
|
|
1084
|
+
l2_rt_env_model = info.get("runtime_env_model") # runtime_env_model tier
|
|
1085
|
+
l2_rt_env_source = info.get("runtime_env_source")
|
|
946
1086
|
|
|
947
1087
|
if is_known_desktop_app(process_name, _known_apps):
|
|
948
1088
|
# Known consumer AI tool (Cursor, Claude Desktop, etc.) → Shadow AI
|
|
@@ -985,6 +1125,8 @@ class CorrelationEngine:
|
|
|
985
1125
|
detected_hosts=[detected_host] if detected_host else [],
|
|
986
1126
|
framework_confidence=l2_fw_confidence,
|
|
987
1127
|
evidence=_build_evidence_tags(["layer2"], entry_script, detected_host),
|
|
1128
|
+
runtime_env_model=l2_rt_env_model,
|
|
1129
|
+
runtime_env_source=l2_rt_env_source,
|
|
988
1130
|
)
|
|
989
1131
|
try:
|
|
990
1132
|
_populate_saas_and_risk_flags(
|
|
@@ -1010,6 +1152,8 @@ class CorrelationEngine:
|
|
|
1010
1152
|
detection_layers=["layer2"],
|
|
1011
1153
|
detected_hosts=[detected_host] if detected_host else [],
|
|
1012
1154
|
evidence=_build_evidence_tags(["layer2"], None, detected_host),
|
|
1155
|
+
runtime_env_model=l2_rt_env_model,
|
|
1156
|
+
runtime_env_source=l2_rt_env_source,
|
|
1013
1157
|
)
|
|
1014
1158
|
try:
|
|
1015
1159
|
_populate_saas_and_risk_flags(
|