agentdiscover 2.9.0__tar.gz → 2.9.2__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.0 → agentdiscover-2.9.2}/PKG-INFO +29 -8
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/README.md +28 -7
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/pyproject.toml +1 -1
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/known_apps.py +7 -2
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/platform.py +32 -15
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_platform_enrichment.py +104 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/uv.lock +1 -1
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/.claude/commands/grow-scanner.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/.github/workflows/aibom.yml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/.github/workflows/ci.yml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/.github/workflows/scan.yml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/.gitignore +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/.python-version +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/CHANGELOG.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/CLAUDE.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/COMMIT_NOTES.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/CONTRIBUTING.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/Dockerfile +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/LICENSE +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/README_BADGES.txt +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/RELEASE_NOTES.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/ROADMAP.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/SECURITY.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/action.yml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/k8s/agents-configmap.yaml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/k8s/crewai-agent.yaml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/k8s/langchain-agent.yaml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/k8s/shadow-agent.yaml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/mcp-governance/DESIGN.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/mcp-governance/README.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/mcp-governance/sample-policy.yaml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/run-demo.sh +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/sample-repo/agents/autogen_agent.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/sample-repo/agents/crewai_agent.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/sample-repo/agents/direct_client.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/sample-repo/agents/langchain_agent.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/sample-repo/requirements.txt +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/demo/setup.sh +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/deployment/kubernetes/tracing-policy.yaml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/deployment/systemd/README.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/deployment/systemd/agent-discover-scanner.service +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docker-compose.yml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/EXAMPLES.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/TETRAGON_SETUP.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/architecture.svg +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/comparisons/vs-grep.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/comparisons/vs-nudge-security.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/ghost-detection.svg +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/integrations/elastic.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/integrations/github-actions.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/integrations/kubernetes-daemonset.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/integrations/splunk.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/known-apps-example.txt +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/layer4-setup.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/docs/nist-mapping.md +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/examples/k8s/test-workload.yaml +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/examples/tetragon-sample-events.json +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/install.sh +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/aibom.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/audit_reports.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/behavioral_patterns.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/cli.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/correlator.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/cloud_audit/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/cloud_audit/aws_cloudtrail.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/cloud_audit/azure_monitor.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/cloud_audit/base.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/cloud_audit/gcp_audit.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/cloudtrail.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/sse_proxy/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/sse_proxy/base.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/sse_proxy/netskope.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/sse_proxy/prisma_access.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/sse_proxy/zscaler_zia.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/errors.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/exporters/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/exporters/mcpfw_policy.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/git_scanner.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/high_risk_agents.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/base.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/sse/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/sse/netskope.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/sse/prisma_access.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/sse/umbrella.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/sse/zscaler.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/js_signatures.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/layer4/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/layer4/osquery_executor.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/layer4/osquery_queries.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/layer4/result_parser.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/macos_detector.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/mcp_detector.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/models/endpoint_discovery.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/json_output.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/k8s_monitor.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/tetragon_events.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/tetragon_monitor.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/vendor_mapping.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/network_monitor.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/process_introspection.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/reports/layer4_report.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/saas_detector.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/sarif_output.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/sbom_analyzer.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/scan_runner.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/scanner.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/signatures.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/visitor.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/windows_detector.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/__init__.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/autogen_safe.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/autogen_unsafe.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/bedrock_safe.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/bedrock_unsafe.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/clean_code.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/crewai_safe.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/crewai_unsafe.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/langchain_agents.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/langchain_js_agent.js +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/langgraph_workflow.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/safe_with_gateway.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/shadow_anthropic.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/shadow_openai.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/fixtures/shadow_openai_js.js +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_aibom.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_audit_bundle.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_bedrock_signatures.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_behavioral_patterns.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_cloud_audit_layer5.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_cloudtrail.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_cloudtrail_wiring.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_correlator.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_correlator_layer5.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_forward_dns.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_git_scanner.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_install.sh +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_interceptors.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_l2_process_introspection.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_mcpfw_export.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_network_monitor.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_scanner.py +0 -0
- {agentdiscover-2.9.0 → agentdiscover-2.9.2}/tests/test_sse_proxy.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentdiscover
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.2
|
|
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
|
|
@@ -372,12 +372,20 @@ Passive observation of outbound connections to AI providers — OpenAI, Anthropi
|
|
|
372
372
|
|
|
373
373
|
Real scan output:
|
|
374
374
|
```
|
|
375
|
-
[DETECT] Google AI connection from Mail (PID: 776) → generativelanguage.googleapis.com:993
|
|
376
375
|
[DETECT] OpenAI connection from Microsoft Edge Helper (PID: 4172) → api.openai.com:443
|
|
377
376
|
[DETECT] Anthropic connection from Cursor Helper (PID: 61436) → api.anthropic.com:443
|
|
378
377
|
[DETECT] OpenAI connection from OneDrive (PID: 96089) → api.openai.com:443
|
|
379
378
|
```
|
|
380
379
|
|
|
380
|
+
**Forward-DNS correlation — reliable Bedrock detection without a CIDR list.**
|
|
381
|
+
AWS Bedrock Runtime endpoints rotate across hundreds of generic EC2 IPs (e.g. `ec2-52-94-238-10.compute-1.amazonaws.com`). Reverse-DNS alone cannot recover the service name. At startup, Layer 2 proactively resolves all known LLM hostnames — including all regional Bedrock runtime and agent-runtime variants — and caches the resulting `IP → hostname` mappings with a short TTL. When psutil reports a connection to `52.94.238.10`, the forward-DNS cache immediately recovers `bedrock-runtime.us-east-1.amazonaws.com` and classifies the connection correctly, even as the IP rotates.
|
|
382
|
+
|
|
383
|
+
**Port filter — eliminates false positives from shared IP space.**
|
|
384
|
+
External LLM APIs use HTTPS exclusively. Connections to provider-adjacent IPs on non-HTTPS ports (e.g. port 993 IMAP on a Google IP) are dropped before classification. Private IPs (Ollama, local inference servers, internal proxies) are exempt from this filter.
|
|
385
|
+
|
|
386
|
+
**Process introspection — framework detection at runtime.**
|
|
387
|
+
When Layer 2 observes a live connection, it resolves the process cmdline to its Python entry script and runs the same L1 detection rules (DAI001–DAI007) against that file in-process. If a framework is identified (LangChain, CrewAI, AutoGen, etc.), the agent is immediately promoted to **CONFIRMED** — no prior static code scan and no CloudTrail required. Each finding carries a `framework_confidence` of `"high"` (signal in the entry script directly) or `"medium"` (signal in a sibling project file), and the resolved `entry_script` path for identity reconciliation with any parallel L1 scan.
|
|
388
|
+
|
|
381
389
|
### Layer 5 — Cloud Audit (v2.7.0+)
|
|
382
390
|
|
|
383
391
|
**Why Layer 2 misses AWS Bedrock.** AWS Bedrock Runtime endpoints rotate across hundreds of generic EC2 IPs with no published CIDR ranges and no stable reverse-DNS pattern. Passive socket monitoring (psutil) can observe the TCP connection but cannot reliably identify it as Bedrock without a complete, continuously-updated IP allowlist — which does not exist publicly. On VPC endpoints, traffic stays inside the AWS network and never appears on the host's socket table at all.
|
|
@@ -549,7 +557,16 @@ Scans developer machines, CI/CD runners, and workstations via osquery. Finds ins
|
|
|
549
557
|
|
|
550
558
|
After all layers run, the correlator builds a unified agent identity. An agent seen in code (Layer 1), confirmed running in K8s (Layer 3), and observed making network calls (Layer 2) is a single correlated identity — not three separate findings.
|
|
551
559
|
|
|
552
|
-
|
|
560
|
+
**CONFIRMED paths** (any of the following is sufficient):
|
|
561
|
+
- Layer 1 (code) + Layer 2 (live network connection)
|
|
562
|
+
- Layer 1 (code) + Layer 3 (K8s / eBPF runtime event)
|
|
563
|
+
- Layer 1 (code) + Layer 4 (osquery endpoint) + Layer 2 (network)
|
|
564
|
+
- Layer 1 (code) + Layer 5 (CloudTrail Bedrock invocation or SSE proxy event)
|
|
565
|
+
- **Layer 2 alone** — when process introspection identifies a framework from the running process entry script, a live connection is sufficient for CONFIRMED without a prior static scan
|
|
566
|
+
|
|
567
|
+
Agents present at runtime with **no Layer 1 match** and no framework identified by process introspection become GHOST agents.
|
|
568
|
+
|
|
569
|
+
The `entry_script` field carries the resolved Python entry-script path from Layer 2 process introspection. When both a static Layer 1 finding and a Layer 2 runtime finding exist for the same file, the platform merges them into a single agent record rather than treating them as two separate orphans.
|
|
553
570
|
|
|
554
571
|
### SaaS blast radius detection (v2.3.0+)
|
|
555
572
|
|
|
@@ -713,13 +730,17 @@ When connected to the platform, each scan triggers the **correlation engine** wh
|
|
|
713
730
|
- **Cross-machine intelligence** — agent seen on 3 machines and crossed from dev into prod? Automatic risk escalation.
|
|
714
731
|
- **SaaS blast radius** — platform aggregates confirmed SaaS connections across all scans and computes blast radius score.
|
|
715
732
|
|
|
716
|
-
**What the scanner sends (v2.
|
|
733
|
+
**What the scanner sends (v2.9.0+):**
|
|
717
734
|
|
|
718
|
-
The upload payload
|
|
735
|
+
The upload payload includes full Layer 2 and Layer 5 telemetry, not just Layer 1 code findings:
|
|
719
736
|
|
|
720
737
|
| Field | Source | Contents |
|
|
721
738
|
|---|---|---|
|
|
722
739
|
| `agents[]` | All layers | Per-agent metadata including classification, risk, SaaS connections |
|
|
740
|
+
| `agents[].detected_hosts` | Layer 2 ForwardDNSCache | DNS-correlated LLM/provider hostnames (e.g. `bedrock-runtime.us-east-1.amazonaws.com`) recovered before psutil classification — reliable even as provider IPs rotate |
|
|
741
|
+
| `agents[].evidence` | Correlator | Ordered confirmation-source tags: `layer1_code_scan`, `layer2_network`, `layer2_process_introspection`, `layer2_dns_host`, `layer3_k8s_runtime`, `layer4_endpoint`, `layer5_cloudtrail`, `layer5_sse_proxy` |
|
|
742
|
+
| `agents[].entry_script` | Layer 2 process introspection | Absolute path to the Python entry script resolved from the OS process cmdline — used to reconcile L1 `code_file` identity with the L2 runtime finding so the same agent is not reported as two orphans |
|
|
743
|
+
| `agents[].framework_confidence` | Layer 2 process introspection | `"high"` = framework signal found in the entry script directly · `"medium"` = found in a sibling project file · `null` = not detected |
|
|
723
744
|
| `agents[].metadata.bedrock_invocations` | Layer 5 CloudTrail | Number of Bedrock API calls attributed to this agent |
|
|
724
745
|
| `agents[].metadata.models_called` | Layer 5 CloudTrail | Model IDs invoked (e.g. `anthropic.claude-3-5-sonnet-20241022-v2:0`) |
|
|
725
746
|
| `agents[].metadata.iam_users` | Layer 5 CloudTrail | IAM principals observed making calls |
|
|
@@ -947,12 +968,12 @@ Full Kubernetes setup: `install.sh` handles Helm, runtime monitoring setup, and
|
|
|
947
968
|
|
|
948
969
|
## DefendAI platform
|
|
949
970
|
|
|
950
|
-
|
|
971
|
+
agentscanner is the **discovery layer** of the DefendAI platform.
|
|
951
972
|
|
|
952
973
|
| Component | Status | Description |
|
|
953
974
|
| ------------------------- | -------------- | --------------------------------------------------------------------- |
|
|
954
|
-
| **
|
|
955
|
-
| **defendai-
|
|
975
|
+
| **agentscanner** | ✅ Open Source (v2.9.0) | Discover and classify AI agents across your environment |
|
|
976
|
+
| **defendai-sensor** | 🧪 Beta | MITM proxy for real-time AI traffic inspection and policy enforcement |
|
|
956
977
|
| **Correlation Engine** | ✅ Available | Cross-machine identity resolution and behavioral drift detection |
|
|
957
978
|
| **Policy Engine** | 🚧 Coming Soon | Define and enforce agent behavior rules |
|
|
958
979
|
| **DefendAI Platform** | 💼 Enterprise | Full lifecycle governance for autonomous AI |
|
|
@@ -329,12 +329,20 @@ Passive observation of outbound connections to AI providers — OpenAI, Anthropi
|
|
|
329
329
|
|
|
330
330
|
Real scan output:
|
|
331
331
|
```
|
|
332
|
-
[DETECT] Google AI connection from Mail (PID: 776) → generativelanguage.googleapis.com:993
|
|
333
332
|
[DETECT] OpenAI connection from Microsoft Edge Helper (PID: 4172) → api.openai.com:443
|
|
334
333
|
[DETECT] Anthropic connection from Cursor Helper (PID: 61436) → api.anthropic.com:443
|
|
335
334
|
[DETECT] OpenAI connection from OneDrive (PID: 96089) → api.openai.com:443
|
|
336
335
|
```
|
|
337
336
|
|
|
337
|
+
**Forward-DNS correlation — reliable Bedrock detection without a CIDR list.**
|
|
338
|
+
AWS Bedrock Runtime endpoints rotate across hundreds of generic EC2 IPs (e.g. `ec2-52-94-238-10.compute-1.amazonaws.com`). Reverse-DNS alone cannot recover the service name. At startup, Layer 2 proactively resolves all known LLM hostnames — including all regional Bedrock runtime and agent-runtime variants — and caches the resulting `IP → hostname` mappings with a short TTL. When psutil reports a connection to `52.94.238.10`, the forward-DNS cache immediately recovers `bedrock-runtime.us-east-1.amazonaws.com` and classifies the connection correctly, even as the IP rotates.
|
|
339
|
+
|
|
340
|
+
**Port filter — eliminates false positives from shared IP space.**
|
|
341
|
+
External LLM APIs use HTTPS exclusively. Connections to provider-adjacent IPs on non-HTTPS ports (e.g. port 993 IMAP on a Google IP) are dropped before classification. Private IPs (Ollama, local inference servers, internal proxies) are exempt from this filter.
|
|
342
|
+
|
|
343
|
+
**Process introspection — framework detection at runtime.**
|
|
344
|
+
When Layer 2 observes a live connection, it resolves the process cmdline to its Python entry script and runs the same L1 detection rules (DAI001–DAI007) against that file in-process. If a framework is identified (LangChain, CrewAI, AutoGen, etc.), the agent is immediately promoted to **CONFIRMED** — no prior static code scan and no CloudTrail required. Each finding carries a `framework_confidence` of `"high"` (signal in the entry script directly) or `"medium"` (signal in a sibling project file), and the resolved `entry_script` path for identity reconciliation with any parallel L1 scan.
|
|
345
|
+
|
|
338
346
|
### Layer 5 — Cloud Audit (v2.7.0+)
|
|
339
347
|
|
|
340
348
|
**Why Layer 2 misses AWS Bedrock.** AWS Bedrock Runtime endpoints rotate across hundreds of generic EC2 IPs with no published CIDR ranges and no stable reverse-DNS pattern. Passive socket monitoring (psutil) can observe the TCP connection but cannot reliably identify it as Bedrock without a complete, continuously-updated IP allowlist — which does not exist publicly. On VPC endpoints, traffic stays inside the AWS network and never appears on the host's socket table at all.
|
|
@@ -506,7 +514,16 @@ Scans developer machines, CI/CD runners, and workstations via osquery. Finds ins
|
|
|
506
514
|
|
|
507
515
|
After all layers run, the correlator builds a unified agent identity. An agent seen in code (Layer 1), confirmed running in K8s (Layer 3), and observed making network calls (Layer 2) is a single correlated identity — not three separate findings.
|
|
508
516
|
|
|
509
|
-
|
|
517
|
+
**CONFIRMED paths** (any of the following is sufficient):
|
|
518
|
+
- Layer 1 (code) + Layer 2 (live network connection)
|
|
519
|
+
- Layer 1 (code) + Layer 3 (K8s / eBPF runtime event)
|
|
520
|
+
- Layer 1 (code) + Layer 4 (osquery endpoint) + Layer 2 (network)
|
|
521
|
+
- Layer 1 (code) + Layer 5 (CloudTrail Bedrock invocation or SSE proxy event)
|
|
522
|
+
- **Layer 2 alone** — when process introspection identifies a framework from the running process entry script, a live connection is sufficient for CONFIRMED without a prior static scan
|
|
523
|
+
|
|
524
|
+
Agents present at runtime with **no Layer 1 match** and no framework identified by process introspection become GHOST agents.
|
|
525
|
+
|
|
526
|
+
The `entry_script` field carries the resolved Python entry-script path from Layer 2 process introspection. When both a static Layer 1 finding and a Layer 2 runtime finding exist for the same file, the platform merges them into a single agent record rather than treating them as two separate orphans.
|
|
510
527
|
|
|
511
528
|
### SaaS blast radius detection (v2.3.0+)
|
|
512
529
|
|
|
@@ -670,13 +687,17 @@ When connected to the platform, each scan triggers the **correlation engine** wh
|
|
|
670
687
|
- **Cross-machine intelligence** — agent seen on 3 machines and crossed from dev into prod? Automatic risk escalation.
|
|
671
688
|
- **SaaS blast radius** — platform aggregates confirmed SaaS connections across all scans and computes blast radius score.
|
|
672
689
|
|
|
673
|
-
**What the scanner sends (v2.
|
|
690
|
+
**What the scanner sends (v2.9.0+):**
|
|
674
691
|
|
|
675
|
-
The upload payload
|
|
692
|
+
The upload payload includes full Layer 2 and Layer 5 telemetry, not just Layer 1 code findings:
|
|
676
693
|
|
|
677
694
|
| Field | Source | Contents |
|
|
678
695
|
|---|---|---|
|
|
679
696
|
| `agents[]` | All layers | Per-agent metadata including classification, risk, SaaS connections |
|
|
697
|
+
| `agents[].detected_hosts` | Layer 2 ForwardDNSCache | DNS-correlated LLM/provider hostnames (e.g. `bedrock-runtime.us-east-1.amazonaws.com`) recovered before psutil classification — reliable even as provider IPs rotate |
|
|
698
|
+
| `agents[].evidence` | Correlator | Ordered confirmation-source tags: `layer1_code_scan`, `layer2_network`, `layer2_process_introspection`, `layer2_dns_host`, `layer3_k8s_runtime`, `layer4_endpoint`, `layer5_cloudtrail`, `layer5_sse_proxy` |
|
|
699
|
+
| `agents[].entry_script` | Layer 2 process introspection | Absolute path to the Python entry script resolved from the OS process cmdline — used to reconcile L1 `code_file` identity with the L2 runtime finding so the same agent is not reported as two orphans |
|
|
700
|
+
| `agents[].framework_confidence` | Layer 2 process introspection | `"high"` = framework signal found in the entry script directly · `"medium"` = found in a sibling project file · `null` = not detected |
|
|
680
701
|
| `agents[].metadata.bedrock_invocations` | Layer 5 CloudTrail | Number of Bedrock API calls attributed to this agent |
|
|
681
702
|
| `agents[].metadata.models_called` | Layer 5 CloudTrail | Model IDs invoked (e.g. `anthropic.claude-3-5-sonnet-20241022-v2:0`) |
|
|
682
703
|
| `agents[].metadata.iam_users` | Layer 5 CloudTrail | IAM principals observed making calls |
|
|
@@ -904,12 +925,12 @@ Full Kubernetes setup: `install.sh` handles Helm, runtime monitoring setup, and
|
|
|
904
925
|
|
|
905
926
|
## DefendAI platform
|
|
906
927
|
|
|
907
|
-
|
|
928
|
+
agentscanner is the **discovery layer** of the DefendAI platform.
|
|
908
929
|
|
|
909
930
|
| Component | Status | Description |
|
|
910
931
|
| ------------------------- | -------------- | --------------------------------------------------------------------- |
|
|
911
|
-
| **
|
|
912
|
-
| **defendai-
|
|
932
|
+
| **agentscanner** | ✅ Open Source (v2.9.0) | Discover and classify AI agents across your environment |
|
|
933
|
+
| **defendai-sensor** | 🧪 Beta | MITM proxy for real-time AI traffic inspection and policy enforcement |
|
|
913
934
|
| **Correlation Engine** | ✅ Available | Cross-machine identity resolution and behavioral drift detection |
|
|
914
935
|
| **Policy Engine** | 🚧 Coming Soon | Define and enforce agent behavior rules |
|
|
915
936
|
| **DefendAI Platform** | 💼 Enterprise | Full lifecycle governance for autonomous AI |
|
|
@@ -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.2"
|
|
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"
|
|
@@ -123,13 +123,18 @@ def load_platform_known_apps(
|
|
|
123
123
|
except Exception:
|
|
124
124
|
pass
|
|
125
125
|
else:
|
|
126
|
-
# Urllib fallback when SDK not installed
|
|
126
|
+
# Urllib fallback when SDK not installed.
|
|
127
|
+
# Use the same auth scheme as /scanner/ingest: Authorization: Bearer {api_key}
|
|
128
|
+
# X-DefendAI-Tenant-Token is included only when a tenant token is available.
|
|
127
129
|
try:
|
|
128
130
|
import urllib.request
|
|
129
131
|
|
|
132
|
+
_headers: dict = {"Authorization": f"Bearer {api_key}"}
|
|
133
|
+
if tenant_token:
|
|
134
|
+
_headers["X-DefendAI-Tenant-Token"] = tenant_token
|
|
130
135
|
req = urllib.request.Request(
|
|
131
136
|
f"{wawsdb_url.rstrip('/')}/scanner/config/known-apps",
|
|
132
|
-
headers=
|
|
137
|
+
headers=_headers,
|
|
133
138
|
)
|
|
134
139
|
with urllib.request.urlopen(req, timeout=5) as resp:
|
|
135
140
|
data = json.loads(resp.read().decode())
|
|
@@ -215,14 +215,17 @@ def load_credentials() -> Optional[Dict[str, str]]:
|
|
|
215
215
|
tenant_token = data.get("tenant_token") or data.get("tenant-token")
|
|
216
216
|
wawsdb_url = data.get("wawsdb_url") or data.get("wawsdb-url")
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
# Only api_key is required; tenant_token and wawsdb_url are optional.
|
|
219
|
+
# A config with just api_key (and optionally wawsdb_url) is valid.
|
|
220
|
+
if not api_key:
|
|
219
221
|
return None
|
|
220
222
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
"tenant_token"
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
result: dict[str, str] = {"api_key": str(api_key)}
|
|
224
|
+
if tenant_token:
|
|
225
|
+
result["tenant_token"] = str(tenant_token)
|
|
226
|
+
if wawsdb_url:
|
|
227
|
+
result["wawsdb_url"] = str(wawsdb_url)
|
|
228
|
+
return result
|
|
226
229
|
except Exception:
|
|
227
230
|
# Never propagate errors from credential loading
|
|
228
231
|
return None
|
|
@@ -485,28 +488,39 @@ def upload_scan_results(
|
|
|
485
488
|
print(f"⚠️ Platform upload failed: {reason} (scan still succeeded)")
|
|
486
489
|
return False
|
|
487
490
|
|
|
488
|
-
# Resolve credentials
|
|
491
|
+
# Resolve credentials.
|
|
492
|
+
#
|
|
493
|
+
# tenant_token is OPTIONAL — the API key alone is sufficient to authenticate.
|
|
494
|
+
# When a tenant_token is also provided it is forwarded as X-DefendAI-Tenant-Token
|
|
495
|
+
# for backwards-compatibility with multi-tenant deployments, but it is never
|
|
496
|
+
# required. This means free-tier users can pass just --api-key and have the
|
|
497
|
+
# upload work without a second credential.
|
|
498
|
+
#
|
|
499
|
+
# Resolution order:
|
|
500
|
+
# 1. CLI arguments (api_key / tenant_token)
|
|
501
|
+
# 2. ~/.defendai/config
|
|
502
|
+
# 3. Built-in sandbox defaults (only when no api_key is found at all)
|
|
489
503
|
source = "sandbox"
|
|
490
504
|
resolved_api_key = api_key
|
|
491
505
|
resolved_tenant_token = tenant_token
|
|
492
506
|
resolved_wawsdb_url = wawsdb_url or _DEFAULT_WAWSDB_URL
|
|
493
507
|
|
|
494
|
-
if resolved_api_key
|
|
508
|
+
if resolved_api_key:
|
|
509
|
+
# api_key alone is enough; tenant_token is supplemental
|
|
495
510
|
source = "cli"
|
|
496
511
|
else:
|
|
497
512
|
cfg = load_credentials()
|
|
498
513
|
if cfg:
|
|
499
|
-
# Only override from config when CLI values are missing
|
|
500
514
|
if not resolved_api_key:
|
|
501
515
|
resolved_api_key = cfg.get("api_key")
|
|
502
516
|
if not resolved_tenant_token:
|
|
503
517
|
resolved_tenant_token = cfg.get("tenant_token")
|
|
504
518
|
resolved_wawsdb_url = cfg.get("wawsdb_url") or resolved_wawsdb_url
|
|
505
|
-
if resolved_api_key
|
|
519
|
+
if resolved_api_key:
|
|
506
520
|
source = "config"
|
|
507
521
|
|
|
508
|
-
if not resolved_api_key
|
|
509
|
-
#
|
|
522
|
+
if not resolved_api_key:
|
|
523
|
+
# No api_key found anywhere — fall back to sandbox for preview
|
|
510
524
|
resolved_api_key = _SANDBOX_API_KEY
|
|
511
525
|
resolved_tenant_token = _SANDBOX_TENANT_TOKEN
|
|
512
526
|
resolved_wawsdb_url = resolved_wawsdb_url or _DEFAULT_WAWSDB_URL
|
|
@@ -518,7 +532,7 @@ def upload_scan_results(
|
|
|
518
532
|
elif source == "config":
|
|
519
533
|
print("✓ Uploading to DefendAI platform (from ~/.defendai/config)...")
|
|
520
534
|
else:
|
|
521
|
-
print("💡 No
|
|
535
|
+
print("💡 No API key provided — uploading to DefendAI sandbox for preview")
|
|
522
536
|
|
|
523
537
|
if mcp_result is None:
|
|
524
538
|
try:
|
|
@@ -622,10 +636,13 @@ def upload_scan_results(
|
|
|
622
636
|
else:
|
|
623
637
|
print("⚠️ Large scan result truncated for upload")
|
|
624
638
|
|
|
625
|
-
|
|
626
|
-
|
|
639
|
+
# X-DefendAI-Tenant-Token is included only when a tenant token is available.
|
|
640
|
+
# The Authorization: Bearer header carries the api_key and is always present.
|
|
641
|
+
headers: dict[str, str] = {
|
|
627
642
|
"Authorization": f"Bearer {resolved_api_key}",
|
|
628
643
|
}
|
|
644
|
+
if resolved_tenant_token:
|
|
645
|
+
headers["X-DefendAI-Tenant-Token"] = resolved_tenant_token
|
|
629
646
|
|
|
630
647
|
try:
|
|
631
648
|
with httpx.Client(timeout=30.0, verify=certifi.where()) as client: # type: ignore[call-arg]
|
|
@@ -7,6 +7,10 @@ Tests for platform.py enrichment functions added in v2.8.0 / v2.9.0:
|
|
|
7
7
|
- format_agents_for_upload (L5 enrichment + credential_files_found + L2 enrichment)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import tempfile
|
|
13
|
+
|
|
10
14
|
import pytest
|
|
11
15
|
|
|
12
16
|
from agent_discover_scanner.platform import (
|
|
@@ -15,6 +19,7 @@ from agent_discover_scanner.platform import (
|
|
|
15
19
|
build_cloud_audit_summary,
|
|
16
20
|
build_network_summary,
|
|
17
21
|
format_agents_for_upload,
|
|
22
|
+
load_credentials,
|
|
18
23
|
)
|
|
19
24
|
|
|
20
25
|
|
|
@@ -558,3 +563,102 @@ class TestFormatAgentsL2Enrichment:
|
|
|
558
563
|
agent = self._fmt(report)[0]
|
|
559
564
|
assert "evidence" in agent
|
|
560
565
|
assert isinstance(agent["evidence"], list)
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
# ── TestLoadCredentials ──────────────────────────────────────────────────────
|
|
569
|
+
|
|
570
|
+
class TestLoadCredentials:
|
|
571
|
+
"""
|
|
572
|
+
load_credentials() unit tests.
|
|
573
|
+
|
|
574
|
+
Verifies that:
|
|
575
|
+
- api_key alone (without tenant_token) is sufficient to return credentials
|
|
576
|
+
- tenant_token and wawsdb_url are optional fields
|
|
577
|
+
- Missing api_key always returns None
|
|
578
|
+
- Malformed / missing config file returns None cleanly
|
|
579
|
+
"""
|
|
580
|
+
|
|
581
|
+
def _write_config(self, tmpdir: str, data: dict) -> str:
|
|
582
|
+
path = os.path.join(tmpdir, ".defendai", "config")
|
|
583
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
584
|
+
with open(path, "w") as f:
|
|
585
|
+
json.dump(data, f)
|
|
586
|
+
return path
|
|
587
|
+
|
|
588
|
+
def _load(self, config_path: str):
|
|
589
|
+
"""Patch the expanduser path and call load_credentials()."""
|
|
590
|
+
import agent_discover_scanner.platform as plat
|
|
591
|
+
original = plat.os.path.expanduser
|
|
592
|
+
plat.os.path.expanduser = lambda p: config_path if "config" in p else original(p)
|
|
593
|
+
try:
|
|
594
|
+
return load_credentials()
|
|
595
|
+
finally:
|
|
596
|
+
plat.os.path.expanduser = original
|
|
597
|
+
|
|
598
|
+
def test_api_key_and_all_fields_returns_full_dict(self):
|
|
599
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
600
|
+
cfg = self._write_config(tmp, {
|
|
601
|
+
"api_key": "key-abc",
|
|
602
|
+
"tenant_token": "tok-xyz",
|
|
603
|
+
"wawsdb_url": "https://custom.example.com",
|
|
604
|
+
})
|
|
605
|
+
result = self._load(cfg)
|
|
606
|
+
assert result is not None
|
|
607
|
+
assert result["api_key"] == "key-abc"
|
|
608
|
+
assert result["tenant_token"] == "tok-xyz"
|
|
609
|
+
assert result["wawsdb_url"] == "https://custom.example.com"
|
|
610
|
+
|
|
611
|
+
def test_api_key_only_returns_credentials(self):
|
|
612
|
+
"""tenant_token absence must NOT prevent credential loading."""
|
|
613
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
614
|
+
cfg = self._write_config(tmp, {"api_key": "key-abc"})
|
|
615
|
+
result = self._load(cfg)
|
|
616
|
+
assert result is not None
|
|
617
|
+
assert result["api_key"] == "key-abc"
|
|
618
|
+
assert "tenant_token" not in result
|
|
619
|
+
|
|
620
|
+
def test_api_key_and_url_no_tenant_token(self):
|
|
621
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
622
|
+
cfg = self._write_config(tmp, {
|
|
623
|
+
"api_key": "key-abc",
|
|
624
|
+
"wawsdb_url": "https://custom.example.com",
|
|
625
|
+
})
|
|
626
|
+
result = self._load(cfg)
|
|
627
|
+
assert result is not None
|
|
628
|
+
assert result["api_key"] == "key-abc"
|
|
629
|
+
assert result["wawsdb_url"] == "https://custom.example.com"
|
|
630
|
+
assert "tenant_token" not in result
|
|
631
|
+
|
|
632
|
+
def test_missing_api_key_returns_none(self):
|
|
633
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
634
|
+
cfg = self._write_config(tmp, {
|
|
635
|
+
"tenant_token": "tok-xyz",
|
|
636
|
+
"wawsdb_url": "https://custom.example.com",
|
|
637
|
+
})
|
|
638
|
+
result = self._load(cfg)
|
|
639
|
+
assert result is None
|
|
640
|
+
|
|
641
|
+
def test_empty_config_returns_none(self):
|
|
642
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
643
|
+
cfg = self._write_config(tmp, {})
|
|
644
|
+
result = self._load(cfg)
|
|
645
|
+
assert result is None
|
|
646
|
+
|
|
647
|
+
def test_api_key_hyphen_variant_accepted(self):
|
|
648
|
+
"""Both api_key and api-key spellings should work."""
|
|
649
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
650
|
+
cfg = self._write_config(tmp, {"api-key": "key-hyphen"})
|
|
651
|
+
result = self._load(cfg)
|
|
652
|
+
assert result is not None
|
|
653
|
+
assert result["api_key"] == "key-hyphen"
|
|
654
|
+
|
|
655
|
+
def test_missing_config_file_returns_none(self):
|
|
656
|
+
"""A path that doesn't exist must return None without raising."""
|
|
657
|
+
import agent_discover_scanner.platform as plat
|
|
658
|
+
original = plat.os.path.expanduser
|
|
659
|
+
plat.os.path.expanduser = lambda p: "/nonexistent/path/config"
|
|
660
|
+
try:
|
|
661
|
+
result = load_credentials()
|
|
662
|
+
finally:
|
|
663
|
+
plat.os.path.expanduser = original
|
|
664
|
+
assert result is None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/deployment/systemd/agent-discover-scanner.service
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/behavioral_patterns.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/cloud_audit/base.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/cloudtrail.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/detectors/sse_proxy/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/exporters/__init__.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/exporters/mcpfw_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/sse/__init__.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/sse/netskope.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/sse/umbrella.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/interceptors/sse/zscaler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/layer4/osquery_executor.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/layer4/osquery_queries.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/layer4/result_parser.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/models/endpoint_discovery.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/json_output.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/k8s_monitor.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/tetragon_events.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/tetragon_monitor.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/monitors/vendor_mapping.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/process_introspection.py
RENAMED
|
File without changes
|
{agentdiscover-2.9.0 → agentdiscover-2.9.2}/src/agent_discover_scanner/reports/layer4_report.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|