admina-framework 0.13.0__tar.gz → 0.13.1__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.
- {admina_framework-0.13.0/admina_framework.egg-info → admina_framework-0.13.1}/PKG-INFO +52 -12
- {admina_framework-0.13.0 → admina_framework-0.13.1}/README.md +51 -11
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/__init__.py +1 -1
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/main.py +33 -5
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/templates/admina.yaml.j2 +2 -1
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/templates/docker-compose.yml.j2 +46 -10
- admina_framework-0.13.1/admina/cli/templates/env.j2 +7 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/config_schema.py +24 -5
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/dashboard/static/index.html +1 -1
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/egress.py +8 -7
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/firewall.py +2 -7
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/forensic.py +35 -1
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/oisg.py +47 -17
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/governance.py +25 -1
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/engines/__init__.py +4 -5
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/_engines.py +17 -2
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/transports/mcp.py +64 -2
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/api/dashboard.py +25 -10
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/forensic_backend.py +11 -2
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/main.py +14 -6
- {admina_framework-0.13.0 → admina_framework-0.13.1/admina_framework.egg-info}/PKG-INFO +52 -12
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina_framework.egg-info/SOURCES.txt +1 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/pyproject.toml +1 -1
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_config_schema_validation.py +2 -1
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_egress_analyze.py +38 -11
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_egress_pipeline.py +36 -8
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_enabled_surfaces.py +44 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_firewall_heuristic_config.py +57 -1
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_fail_mode.py +99 -0
- admina_framework-0.13.1/tests/test_mcp_block_reason.py +148 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_metrics_gateway.py +24 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_pipeline_executor.py +15 -5
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_request_size_cap.py +14 -0
- admina_framework-0.13.0/admina/cli/templates/env.j2 +0 -8
- {admina_framework-0.13.0 → admina_framework-0.13.1}/LICENSE +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/NOTICE +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/commands/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/forensic.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/redteam.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/templates/main.py.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/templates/plugin.py.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/templates/plugin_pyproject.toml.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/templates/plugin_readme.md.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/templates/plugin_test.py.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/event_bus.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/exception_log.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/jcs.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/offline.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/secretfile.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/secrets.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/trace_context.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/core/types.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/dashboard/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/dashboard/static/heimdall.png +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/dashboard/static/vendor/alpinejs.min.js +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/coordination.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/fingerprint.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/loop_breaker.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/pattern_packs.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/pattern_timing.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/ruleset.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/scan_policy.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/ai_infra/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/ai_infra/llm_engine.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/ai_infra/rag.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/ai_infra/webui.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/ai_act_terms.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/cross_regulation.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/eu_ai_act.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/forensic_files.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/forensic_integrity.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/gdpr.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/nis2.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/oisg_evidence.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/otel.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/compliance/schemas/oisg-evidence.schema.json +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/data_sovereignty/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/data_sovereignty/classification.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/data_sovereignty/email_matching.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/data_sovereignty/iban.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/data_sovereignty/masking.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/data_sovereignty/pii.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/data_sovereignty/residency.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/engines/pii_plugins.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/engines/presidio.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/cheshirecat/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/cheshirecat/admina-plugin/admina_governance.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/crewai/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/crewai/callbacks.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/langchain/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/langchain/callbacks.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/n8n/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/integrations/openclaw/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/base.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/adapters/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/adapters/_streaming.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/adapters/anthropic.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/adapters/bedrock.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/adapters/gemini.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/adapters/mistral.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/adapters/ollama.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/adapters/openai.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/adapters/vllm.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/alerts/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/alerts/log.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/alerts/webhook.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/auth/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/auth/apikey.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/compliance/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/compliance/eu_ai_act.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/connectors/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/connectors/chromadb.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/connectors/filesystem.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/forensic/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/forensic/filesystem.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/guards/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/guards/guardrailsai_guard.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/pii/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/pii/spacy_regex.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/transports/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/builtin/transports/http_rest.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/plugins/registry.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/api/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/api/gateway.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/api/integration.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/body_limit.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/dashboard_session.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/decisions.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/engine_bridge.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/engine_report.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/env_check.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/forensic_probe.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/gateway_body.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/gateway_correlation.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/gateway_outcome.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/gateway_response_scan.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/gateway_scan.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/gateway_transport.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/gateway_upstreams.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/governance.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/log_format.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/loop_lag.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/multi_upstream.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/pipeline_executor.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/request_metrics.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/state.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/proxy/surfaces.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/py.typed +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/baselines/baseline.json +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/corpora/SHA256SUMS +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/corpora/coordination.jsonl +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/corpora/injection.jsonl +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/corpora/loop.jsonl +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/corpora/pii.jsonl +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/corpora.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/detectors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/gate.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/metrics.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/report.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/redteam/runner.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/sdk/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/sdk/_compat.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/sdk/compliance_kit.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/sdk/errors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/sdk/governed_agent.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/sdk/governed_data.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/sdk/governed_model.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/sdk/retry.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina/sdk/streaming.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina_framework.egg-info/dependency_links.txt +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina_framework.egg-info/entry_points.txt +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina_framework.egg-info/requires.txt +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/admina_framework.egg-info/top_level.txt +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/setup.cfg +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_admina_config_path.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_ai_act_terms.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_audit_api_source.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_auth_middleware_errors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_banner_engine_info.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_benchmark_14us.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_bus_event_metadata.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_canary_no_content.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_check_versions.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_cli_forensic_export.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_cli_redteam.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_coordination.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_coordination_cli.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_coordination_corpus.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_coordination_wiring.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_core_config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_dashboard_container.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_data_classifier_restricted.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_domains.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_domains_governance.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_egress_cli.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_egress_policy.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_egress_surfaces.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_engine_effective.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_engine_startup_errors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_engines.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_entrypoint_script.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_error_disclosure.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_event_loop_lag_metric.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_fingerprint.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_firewall_it_baseline.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_firewall_it_benign.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_firewall_parity.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_firewall_pattern_ids.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_firewall_pattern_packs.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_firewall_pattern_timing.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_atomic_write.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_blackbox.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_config_coherence.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_gateway_records.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_incremental_verify.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_integrity.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_rebuilt_and_duplicates.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_record_signature.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_restart.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_s3_reads.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_forensic_volume.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_block_status.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_correlation.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_endpoints.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_errors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_forward_body.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_governed_stream.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_guard_fail_mode.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_helpers.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_latency_bench.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_mount.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_outcome_headers.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_passthrough.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_pii_contract.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_ruleset_endpoint.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_scan_coverage.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_upstream_auth.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_gateway_upstream_routes.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_governance_pipeline.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_guard_fail_mode.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_health_fields.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_health_forensic_probe.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_iban_it.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_jcs.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_lazy_imports.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_log_format.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_mcp_transport.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_metrics_docs_auth.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_model_allowlist_post.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_offline_guarantee.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_oisg_evidence.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_otel_enabled_flag.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_pattern_probe.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_pii_email_matching.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_pii_mask_style.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_pii_plugin_engines.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_presidio_engine.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_proxy_dashboard_session.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_proxy_forensic_would_action.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_proxy_governance_mode_config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_proxy_guard_fail_mode.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_proxy_mcp_response_pii.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_proxy_metrics_endpoint.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_proxy_minimal_extra.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_proxy_plugins.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_proxy_security.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_query_api_key_deprecation.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_record_decision.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_redaction_values_only.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_redteam_efficacy.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_redteam_external_corpora.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_redteam_lib.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_release_workflows.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_request_sha256.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_response_scan_option.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_ruleset_parity.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_ruleset_sha256.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_scan_depth.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_scan_policy.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_secret_files.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_unknown_admina_env.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.1}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: admina-framework
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.1
|
|
4
4
|
Summary: Admina — governed AI development framework
|
|
5
5
|
Author-email: Stefano Noferi <info@admina.org>
|
|
6
6
|
Maintainer-email: Stefano Noferi <info@admina.org>
|
|
@@ -439,6 +439,17 @@ dependency: one uvicorn serves the proxy API and the dashboard SPA on the
|
|
|
439
439
|
same port. Use `--stack` for the production-like Docker compose, or
|
|
440
440
|
`--with-llm` to also boot local LLM services.
|
|
441
441
|
|
|
442
|
+
Local mode uses the forensic backend of `FORENSIC_BACKEND` or of
|
|
443
|
+
`admina.yaml`, and memory when neither sets one. In the compose file that
|
|
444
|
+
`admina init` writes, the dashboard (`127.0.0.1:3000`), OTEL and the
|
|
445
|
+
`--with-llm` services listen on the loopback interface only; the dashboard
|
|
446
|
+
asks for `ADMINA_DASHBOARD_PASSWORD` and the API key (both shown by
|
|
447
|
+
`admina password show`). The proxy reaches the MCP server at
|
|
448
|
+
`UPSTREAM_MCP_URL` (default `http://host.docker.internal:9000`, an MCP
|
|
449
|
+
server on the host). `CLICKHOUSE_PASSWORD` and `GRAFANA_ADMIN_PASSWORD`
|
|
450
|
+
come from the `.env` that `admina dev` writes; a plain `docker compose up`
|
|
451
|
+
stops when they are not set.
|
|
452
|
+
|
|
442
453
|
## Dashboard
|
|
443
454
|
|
|
444
455
|
Real-time governance dashboard on port 3000:
|
|
@@ -858,6 +869,12 @@ one trailing newline removed. A missing, unreadable or empty file, or a key
|
|
|
858
869
|
set both directly and as a file, stops the proxy; the error names the
|
|
859
870
|
setting and the path, never the key. `ADMINA_FORENSIC_STATE_KEY_FILE` inside
|
|
860
871
|
the forensic directory stops it too: the key must be kept outside the store.
|
|
872
|
+
The built-in `apikey` auth provider reads `ADMINA_API_KEY` from the
|
|
873
|
+
environment only, and is loaded only when it is set there. A key given only
|
|
874
|
+
through `ADMINA_API_KEY_FILE` or `.env` is checked by the authentication
|
|
875
|
+
middleware itself (the same headers, the same constant-time comparison), and
|
|
876
|
+
`POST /api/v1/audit` then stamps `submitted_by: "api_key"` instead of
|
|
877
|
+
`"user:api_key_user"`.
|
|
861
878
|
|
|
862
879
|
**Forensic store.** `FORENSIC_BACKEND` (`memory`, `filesystem`, `s3`) and
|
|
863
880
|
`FORENSIC_BASE_DIR` choose where the forensic records go. When they are not
|
|
@@ -899,7 +916,12 @@ directory (UTC hour of the write, sequence number on eight digits), with
|
|
|
899
916
|
`admina-forensic/1 record signature` under the chain-state key
|
|
900
917
|
(`ADMINA_FORENSIC_STATE_KEY` or `ADMINA_FORENSIC_STATE_KEY_FILE`, kept
|
|
901
918
|
outside the forensic directory); `record_sig_alg`: `hmac-sha256`. Without
|
|
902
|
-
a key a record has `record_sig_alg: "none"` and no `record_sig`.
|
|
919
|
+
a key a record has `record_sig_alg: "none"` and no `record_sig`. Since
|
|
920
|
+
0.13.1, when a store that holds signed records is opened without its
|
|
921
|
+
key, the proxy does not start in `closed` fail mode
|
|
922
|
+
(`ADMINA_FORENSIC_FAIL_MODE`) and logs a warning in `open` mode, where
|
|
923
|
+
the records it then writes are unsigned (a later verification with the
|
|
924
|
+
key reports `unsigned`).
|
|
903
925
|
|
|
904
926
|
The chain state (`_chain_state.json`, with its HMAC-SHA256 in
|
|
905
927
|
`_chain_state.json.sig` when a key is set) holds `record_count`,
|
|
@@ -974,7 +996,10 @@ introduced.
|
|
|
974
996
|
**Audit records.** `POST /api/v1/audit` records the event it receives with
|
|
975
997
|
`source: "api_v1_audit"` (a `source` in the request is kept as
|
|
976
998
|
`client_source`) and `submitted_by`: the credential the request was admitted
|
|
977
|
-
with (`api_key`, `append_key`, `user:<id>` or `unauthenticated`).
|
|
999
|
+
with (`api_key`, `append_key`, `user:<id>` or `unauthenticated`). With
|
|
1000
|
+
`ADMINA_API_KEY` in the environment the built-in `apikey` auth provider
|
|
1001
|
+
admits API-key requests, stamped `user:api_key_user`; `api_key` is the stamp
|
|
1002
|
+
when the key comes only from `ADMINA_API_KEY_FILE` or `.env`. An
|
|
978
1003
|
`event_type` of the records the proxy writes itself (`mcp_request`,
|
|
979
1004
|
`mcp_response`, `gateway_request`, `gateway_response`, `gateway_response_scan`,
|
|
980
1005
|
`policy_violation`, `chain_state_rebuilt`) is refused with `400`.
|
|
@@ -1423,18 +1448,18 @@ fields are always scanned.
|
|
|
1423
1448
|
|
|
1424
1449
|
The scan follows the body 32 levels deep: the body is level 0, its fields
|
|
1425
1450
|
level 1, and the JSON of tool call arguments is at the level of its string.
|
|
1426
|
-
|
|
1427
|
-
(`X-Admina-Would-Action: BLOCK` in `observe` and `dry-run`),
|
|
1428
|
-
has `checks.scan_depth = {"action": "BLOCK", "reason":
|
|
1451
|
+
With the firewall on, a request with a string nested deeper is blocked in
|
|
1452
|
+
`enforce` mode (`X-Admina-Would-Action: BLOCK` in `observe` and `dry-run`),
|
|
1453
|
+
and its record has `checks.scan_depth = {"action": "BLOCK", "reason":
|
|
1429
1454
|
"depth_limit_exceeded"}`. Tool call arguments nested deeper than the JSON
|
|
1430
1455
|
parser reads are scanned as they are, and the request is blocked the same
|
|
1431
1456
|
way.
|
|
1432
1457
|
|
|
1433
|
-
`/mcp`
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1458
|
+
`/mcp` scans and redacts to the same depth. With the firewall or PII
|
|
1459
|
+
redaction on, a request holding a string, or a non-empty object or array,
|
|
1460
|
+
past level 32 is blocked the same way, since that text would be neither
|
|
1461
|
+
scanned nor redacted. `content` of `/api/v1/validate` must be a string, so
|
|
1462
|
+
that block does not apply there.
|
|
1438
1463
|
|
|
1439
1464
|
#### Scan scope
|
|
1440
1465
|
|
|
@@ -1479,6 +1504,10 @@ case-sensitive. The caller must keep these tags out of text written by
|
|
|
1479
1504
|
untrusted parties, because the gateway cannot tell such text from its own
|
|
1480
1505
|
blocks.
|
|
1481
1506
|
|
|
1507
|
+
`ruleset_sha256()` hashes the Admina version too (and the `admina-core`
|
|
1508
|
+
version with the Rust engine), so a ruleset hash changes with every release:
|
|
1509
|
+
recompute the hashes in `gateway.prescan_rulesets` after each upgrade.
|
|
1510
|
+
|
|
1482
1511
|
Trust model: with scan policies on, the gateway takes the caller's word for
|
|
1483
1512
|
what it has scanned. Any caller that holds the API key can send the header,
|
|
1484
1513
|
and the ruleset it must declare is not a secret (it is on every response and
|
|
@@ -1719,7 +1748,10 @@ cd integrations/cheshirecat/admina-plugin
|
|
|
1719
1748
|
|
|
1720
1749
|
<br>
|
|
1721
1750
|
|
|
1722
|
-
Governs every LLM call and tool invocation in-process
|
|
1751
|
+
Governs every LLM call and tool invocation in-process. Install
|
|
1752
|
+
`admina-framework[proxy,nlp]`: the loop detection that the callbacks turn
|
|
1753
|
+
on by default needs scikit-learn from `[proxy]` (or pass
|
|
1754
|
+
`loop_detection=False`).
|
|
1723
1755
|
|
|
1724
1756
|
```python
|
|
1725
1757
|
from admina.integrations.langchain.callbacks import AdminaCallbackHandler
|
|
@@ -1967,6 +1999,14 @@ curl -X POST http://localhost:8080/api/compliance/classify \
|
|
|
1967
1999
|
curl http://localhost:8080/api/dashboard/score
|
|
1968
2000
|
```
|
|
1969
2001
|
|
|
2002
|
+
A blocked `/mcp` call is answered with a JSON-RPC error (`-32600`, "Request
|
|
2003
|
+
blocked by Admina governance") whose `error.data` holds the `event_id` of
|
|
2004
|
+
the forensic record and a `reason`: `injection_detected` (firewall),
|
|
2005
|
+
`scan_depth_exceeded` (text nested deeper than the scan depth),
|
|
2006
|
+
`egress_refused` (egress policy), `guard_blocked` (a request guard, also a
|
|
2007
|
+
guard error under `ADMINA_GUARD_FAIL_MODE=closed`) or `response_blocked`
|
|
2008
|
+
(a response guard). Before 0.13.1 every block reported `injection_detected`.
|
|
2009
|
+
|
|
1970
2010
|
</details>
|
|
1971
2011
|
|
|
1972
2012
|
## Project documents
|
|
@@ -355,6 +355,17 @@ dependency: one uvicorn serves the proxy API and the dashboard SPA on the
|
|
|
355
355
|
same port. Use `--stack` for the production-like Docker compose, or
|
|
356
356
|
`--with-llm` to also boot local LLM services.
|
|
357
357
|
|
|
358
|
+
Local mode uses the forensic backend of `FORENSIC_BACKEND` or of
|
|
359
|
+
`admina.yaml`, and memory when neither sets one. In the compose file that
|
|
360
|
+
`admina init` writes, the dashboard (`127.0.0.1:3000`), OTEL and the
|
|
361
|
+
`--with-llm` services listen on the loopback interface only; the dashboard
|
|
362
|
+
asks for `ADMINA_DASHBOARD_PASSWORD` and the API key (both shown by
|
|
363
|
+
`admina password show`). The proxy reaches the MCP server at
|
|
364
|
+
`UPSTREAM_MCP_URL` (default `http://host.docker.internal:9000`, an MCP
|
|
365
|
+
server on the host). `CLICKHOUSE_PASSWORD` and `GRAFANA_ADMIN_PASSWORD`
|
|
366
|
+
come from the `.env` that `admina dev` writes; a plain `docker compose up`
|
|
367
|
+
stops when they are not set.
|
|
368
|
+
|
|
358
369
|
## Dashboard
|
|
359
370
|
|
|
360
371
|
Real-time governance dashboard on port 3000:
|
|
@@ -774,6 +785,12 @@ one trailing newline removed. A missing, unreadable or empty file, or a key
|
|
|
774
785
|
set both directly and as a file, stops the proxy; the error names the
|
|
775
786
|
setting and the path, never the key. `ADMINA_FORENSIC_STATE_KEY_FILE` inside
|
|
776
787
|
the forensic directory stops it too: the key must be kept outside the store.
|
|
788
|
+
The built-in `apikey` auth provider reads `ADMINA_API_KEY` from the
|
|
789
|
+
environment only, and is loaded only when it is set there. A key given only
|
|
790
|
+
through `ADMINA_API_KEY_FILE` or `.env` is checked by the authentication
|
|
791
|
+
middleware itself (the same headers, the same constant-time comparison), and
|
|
792
|
+
`POST /api/v1/audit` then stamps `submitted_by: "api_key"` instead of
|
|
793
|
+
`"user:api_key_user"`.
|
|
777
794
|
|
|
778
795
|
**Forensic store.** `FORENSIC_BACKEND` (`memory`, `filesystem`, `s3`) and
|
|
779
796
|
`FORENSIC_BASE_DIR` choose where the forensic records go. When they are not
|
|
@@ -815,7 +832,12 @@ directory (UTC hour of the write, sequence number on eight digits), with
|
|
|
815
832
|
`admina-forensic/1 record signature` under the chain-state key
|
|
816
833
|
(`ADMINA_FORENSIC_STATE_KEY` or `ADMINA_FORENSIC_STATE_KEY_FILE`, kept
|
|
817
834
|
outside the forensic directory); `record_sig_alg`: `hmac-sha256`. Without
|
|
818
|
-
a key a record has `record_sig_alg: "none"` and no `record_sig`.
|
|
835
|
+
a key a record has `record_sig_alg: "none"` and no `record_sig`. Since
|
|
836
|
+
0.13.1, when a store that holds signed records is opened without its
|
|
837
|
+
key, the proxy does not start in `closed` fail mode
|
|
838
|
+
(`ADMINA_FORENSIC_FAIL_MODE`) and logs a warning in `open` mode, where
|
|
839
|
+
the records it then writes are unsigned (a later verification with the
|
|
840
|
+
key reports `unsigned`).
|
|
819
841
|
|
|
820
842
|
The chain state (`_chain_state.json`, with its HMAC-SHA256 in
|
|
821
843
|
`_chain_state.json.sig` when a key is set) holds `record_count`,
|
|
@@ -890,7 +912,10 @@ introduced.
|
|
|
890
912
|
**Audit records.** `POST /api/v1/audit` records the event it receives with
|
|
891
913
|
`source: "api_v1_audit"` (a `source` in the request is kept as
|
|
892
914
|
`client_source`) and `submitted_by`: the credential the request was admitted
|
|
893
|
-
with (`api_key`, `append_key`, `user:<id>` or `unauthenticated`).
|
|
915
|
+
with (`api_key`, `append_key`, `user:<id>` or `unauthenticated`). With
|
|
916
|
+
`ADMINA_API_KEY` in the environment the built-in `apikey` auth provider
|
|
917
|
+
admits API-key requests, stamped `user:api_key_user`; `api_key` is the stamp
|
|
918
|
+
when the key comes only from `ADMINA_API_KEY_FILE` or `.env`. An
|
|
894
919
|
`event_type` of the records the proxy writes itself (`mcp_request`,
|
|
895
920
|
`mcp_response`, `gateway_request`, `gateway_response`, `gateway_response_scan`,
|
|
896
921
|
`policy_violation`, `chain_state_rebuilt`) is refused with `400`.
|
|
@@ -1339,18 +1364,18 @@ fields are always scanned.
|
|
|
1339
1364
|
|
|
1340
1365
|
The scan follows the body 32 levels deep: the body is level 0, its fields
|
|
1341
1366
|
level 1, and the JSON of tool call arguments is at the level of its string.
|
|
1342
|
-
|
|
1343
|
-
(`X-Admina-Would-Action: BLOCK` in `observe` and `dry-run`),
|
|
1344
|
-
has `checks.scan_depth = {"action": "BLOCK", "reason":
|
|
1367
|
+
With the firewall on, a request with a string nested deeper is blocked in
|
|
1368
|
+
`enforce` mode (`X-Admina-Would-Action: BLOCK` in `observe` and `dry-run`),
|
|
1369
|
+
and its record has `checks.scan_depth = {"action": "BLOCK", "reason":
|
|
1345
1370
|
"depth_limit_exceeded"}`. Tool call arguments nested deeper than the JSON
|
|
1346
1371
|
parser reads are scanned as they are, and the request is blocked the same
|
|
1347
1372
|
way.
|
|
1348
1373
|
|
|
1349
|
-
`/mcp`
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1374
|
+
`/mcp` scans and redacts to the same depth. With the firewall or PII
|
|
1375
|
+
redaction on, a request holding a string, or a non-empty object or array,
|
|
1376
|
+
past level 32 is blocked the same way, since that text would be neither
|
|
1377
|
+
scanned nor redacted. `content` of `/api/v1/validate` must be a string, so
|
|
1378
|
+
that block does not apply there.
|
|
1354
1379
|
|
|
1355
1380
|
#### Scan scope
|
|
1356
1381
|
|
|
@@ -1395,6 +1420,10 @@ case-sensitive. The caller must keep these tags out of text written by
|
|
|
1395
1420
|
untrusted parties, because the gateway cannot tell such text from its own
|
|
1396
1421
|
blocks.
|
|
1397
1422
|
|
|
1423
|
+
`ruleset_sha256()` hashes the Admina version too (and the `admina-core`
|
|
1424
|
+
version with the Rust engine), so a ruleset hash changes with every release:
|
|
1425
|
+
recompute the hashes in `gateway.prescan_rulesets` after each upgrade.
|
|
1426
|
+
|
|
1398
1427
|
Trust model: with scan policies on, the gateway takes the caller's word for
|
|
1399
1428
|
what it has scanned. Any caller that holds the API key can send the header,
|
|
1400
1429
|
and the ruleset it must declare is not a secret (it is on every response and
|
|
@@ -1635,7 +1664,10 @@ cd integrations/cheshirecat/admina-plugin
|
|
|
1635
1664
|
|
|
1636
1665
|
<br>
|
|
1637
1666
|
|
|
1638
|
-
Governs every LLM call and tool invocation in-process
|
|
1667
|
+
Governs every LLM call and tool invocation in-process. Install
|
|
1668
|
+
`admina-framework[proxy,nlp]`: the loop detection that the callbacks turn
|
|
1669
|
+
on by default needs scikit-learn from `[proxy]` (or pass
|
|
1670
|
+
`loop_detection=False`).
|
|
1639
1671
|
|
|
1640
1672
|
```python
|
|
1641
1673
|
from admina.integrations.langchain.callbacks import AdminaCallbackHandler
|
|
@@ -1883,6 +1915,14 @@ curl -X POST http://localhost:8080/api/compliance/classify \
|
|
|
1883
1915
|
curl http://localhost:8080/api/dashboard/score
|
|
1884
1916
|
```
|
|
1885
1917
|
|
|
1918
|
+
A blocked `/mcp` call is answered with a JSON-RPC error (`-32600`, "Request
|
|
1919
|
+
blocked by Admina governance") whose `error.data` holds the `event_id` of
|
|
1920
|
+
the forensic record and a `reason`: `injection_detected` (firewall),
|
|
1921
|
+
`scan_depth_exceeded` (text nested deeper than the scan depth),
|
|
1922
|
+
`egress_refused` (egress policy), `guard_blocked` (a request guard, also a
|
|
1923
|
+
guard error under `ADMINA_GUARD_FAIL_MODE=closed`) or `response_blocked`
|
|
1924
|
+
(a response guard). Before 0.13.1 every block reported `injection_detected`.
|
|
1925
|
+
|
|
1886
1926
|
</details>
|
|
1887
1927
|
|
|
1888
1928
|
## Project documents
|
|
@@ -251,8 +251,10 @@ def init(
|
|
|
251
251
|
for f in created:
|
|
252
252
|
click.echo(f" ✓ {f}")
|
|
253
253
|
|
|
254
|
-
# 4. Bootstrap secrets (first-time setup)
|
|
255
|
-
|
|
254
|
+
# 4. Bootstrap secrets (first-time setup). A project whose vault exists
|
|
255
|
+
# gets the vault's secrets in a .env created by this run.
|
|
256
|
+
if _bootstrap_secrets(project_dir) is None and ".env" in created:
|
|
257
|
+
SecretVault(project_dir).write_dotenv(project_dir / ".env")
|
|
256
258
|
|
|
257
259
|
# 5. Docker compose pull (optional)
|
|
258
260
|
if not no_pull and shutil.which("docker"):
|
|
@@ -570,8 +572,28 @@ def _require_proxy_extra_for_local_dev() -> None:
|
|
|
570
572
|
raise SystemExit(1)
|
|
571
573
|
|
|
572
574
|
|
|
575
|
+
def _local_forensic_backend(env: dict[str, str], data: dict[str, object]) -> tuple[str, str]:
|
|
576
|
+
"""The forensic backend the local proxy uses and where it comes from:
|
|
577
|
+
``FORENSIC_BACKEND`` when set and not empty, else
|
|
578
|
+
``domains.compliance.forensic.backend`` (or ``storage``) of admina.yaml,
|
|
579
|
+
else ``memory`` (the proxy's default, see admina.proxy.forensic_backend).
|
|
580
|
+
"""
|
|
581
|
+
value = env.get("FORENSIC_BACKEND", "").strip().lower()
|
|
582
|
+
if value:
|
|
583
|
+
return value, "FORENSIC_BACKEND"
|
|
584
|
+
domains = data.get("domains")
|
|
585
|
+
compliance = domains.get("compliance") if isinstance(domains, dict) else None
|
|
586
|
+
forensic = compliance.get("forensic") if isinstance(compliance, dict) else None
|
|
587
|
+
if isinstance(forensic, dict):
|
|
588
|
+
value = str(forensic.get("backend") or forensic.get("storage") or "").strip().lower()
|
|
589
|
+
if value:
|
|
590
|
+
return ("s3" if value == "minio" else value), "admina.yaml"
|
|
591
|
+
return "memory", ""
|
|
592
|
+
|
|
593
|
+
|
|
573
594
|
def _run_local(
|
|
574
595
|
project_dir: Path,
|
|
596
|
+
data: dict[str, object],
|
|
575
597
|
vault: SecretVault,
|
|
576
598
|
*,
|
|
577
599
|
no_browser: bool,
|
|
@@ -594,8 +616,9 @@ def _run_local(
|
|
|
594
616
|
|
|
595
617
|
env = os.environ.copy()
|
|
596
618
|
env.update(vault.export_env())
|
|
597
|
-
# Local dev defaults — sane for single-user localhost.
|
|
598
|
-
|
|
619
|
+
# Local dev defaults — sane for single-user localhost. The forensic
|
|
620
|
+
# backend is not set here: the proxy reads FORENSIC_BACKEND, else the
|
|
621
|
+
# backend of admina.yaml, else uses memory.
|
|
599
622
|
env.setdefault("OTEL_ENDPOINT", "")
|
|
600
623
|
env.setdefault("REDIS_URL", "")
|
|
601
624
|
env.setdefault("CLICKHOUSE_HOST", "")
|
|
@@ -622,7 +645,11 @@ def _run_local(
|
|
|
622
645
|
|
|
623
646
|
click.echo(f"\n Starting Admina proxy + dashboard on http://{display_host}:{port}")
|
|
624
647
|
click.echo(" Mode: local (no Docker)")
|
|
625
|
-
|
|
648
|
+
forensic_backend, forensic_source = _local_forensic_backend(env, data)
|
|
649
|
+
if forensic_source:
|
|
650
|
+
click.echo(f" Forensic backend: {forensic_backend} ({forensic_source})")
|
|
651
|
+
else:
|
|
652
|
+
click.echo(" Forensic backend: in-memory (events live for the process lifetime)")
|
|
626
653
|
if is_public:
|
|
627
654
|
click.echo(
|
|
628
655
|
f" ⚠ Listening on {host}:{port} — accessible from the LAN. "
|
|
@@ -848,6 +875,7 @@ def dev(
|
|
|
848
875
|
bind_host = "0.0.0.0" if public else host
|
|
849
876
|
_run_local(
|
|
850
877
|
project_dir,
|
|
878
|
+
data,
|
|
851
879
|
vault,
|
|
852
880
|
no_browser=no_browser,
|
|
853
881
|
port=port,
|
|
@@ -58,7 +58,8 @@ domains:
|
|
|
58
58
|
forensic:
|
|
59
59
|
# The proxy's forensic backend and directory. FORENSIC_BACKEND and
|
|
60
60
|
# FORENSIC_BASE_DIR, when set, take precedence (the generated
|
|
61
|
-
# docker-compose.yml sets both
|
|
61
|
+
# docker-compose.yml sets both). Without either, the proxy, `admina dev`
|
|
62
|
+
# included, keeps the records in memory.
|
|
62
63
|
# backend: filesystem
|
|
63
64
|
# base_dir: .admina/forensic
|
|
64
65
|
bucket: forensic-blackbox
|
{admina_framework-0.13.0 → admina_framework-0.13.1}/admina/cli/templates/docker-compose.yml.j2
RENAMED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Docker Compose — {{ project_name }}
|
|
2
2
|
# Generated by: admina init
|
|
3
3
|
# Docs: https://admina.org/docs/docker
|
|
4
|
+
#
|
|
5
|
+
# Secrets come from .env, which `admina init` and `admina dev` write from the
|
|
6
|
+
# project's secrets vault (show them with `admina password show`).
|
|
4
7
|
|
|
5
8
|
services:
|
|
6
9
|
|
|
@@ -17,13 +20,29 @@ services:
|
|
|
17
20
|
- .env
|
|
18
21
|
environment:
|
|
19
22
|
- REDIS_URL=redis://redis:6379/0
|
|
23
|
+
{% if domains.compliance %}
|
|
20
24
|
- CLICKHOUSE_HOST=clickhouse
|
|
21
25
|
- CLICKHOUSE_PORT=8123
|
|
22
26
|
- CLICKHOUSE_DB=admina
|
|
27
|
+
{% else %}
|
|
28
|
+
# No ClickHouse and no OTEL collector without the compliance domain.
|
|
29
|
+
- CLICKHOUSE_HOST=
|
|
30
|
+
{% endif %}
|
|
23
31
|
- FORENSIC_BACKEND=filesystem
|
|
24
32
|
- FORENSIC_BASE_DIR=/app/.admina/forensic
|
|
33
|
+
{% if domains.compliance %}
|
|
25
34
|
- OTEL_ENDPOINT=http://otel-collector:4317
|
|
35
|
+
{% else %}
|
|
36
|
+
- OTEL_ENABLED=false
|
|
37
|
+
{% endif %}
|
|
38
|
+
# The MCP server the proxy forwards /mcp to. The default is port 9000
|
|
39
|
+
# of the Docker host; export UPSTREAM_MCP_URL before `docker compose up`
|
|
40
|
+
# (or `admina dev --stack`) to use another server.
|
|
41
|
+
- UPSTREAM_MCP_URL=${UPSTREAM_MCP_URL:-http://host.docker.internal:9000}
|
|
26
42
|
- LOG_LEVEL=INFO
|
|
43
|
+
extra_hosts:
|
|
44
|
+
# host.docker.internal on Linux too (Docker Desktop defines it).
|
|
45
|
+
- "host.docker.internal:host-gateway"
|
|
27
46
|
volumes:
|
|
28
47
|
# Forensic records and chain state (FORENSIC_BASE_DIR), kept when the
|
|
29
48
|
# container is re-created.
|
|
@@ -31,8 +50,10 @@ services:
|
|
|
31
50
|
depends_on:
|
|
32
51
|
redis:
|
|
33
52
|
condition: service_healthy
|
|
53
|
+
{% if domains.compliance %}
|
|
34
54
|
clickhouse:
|
|
35
55
|
condition: service_healthy
|
|
56
|
+
{% endif %}
|
|
36
57
|
healthcheck:
|
|
37
58
|
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
|
38
59
|
interval: 10s
|
|
@@ -47,13 +68,27 @@ services:
|
|
|
47
68
|
image: ghcr.io/admina-org/admina-dashboard:{{ admina_version }}
|
|
48
69
|
container_name: {{ project_name }}-dashboard
|
|
49
70
|
ports:
|
|
50
|
-
|
|
71
|
+
# Loopback only. To reach the dashboard from other hosts, publish it
|
|
72
|
+
# behind TLS; it asks for ADMINA_DASHBOARD_PASSWORD (HTTP Basic Auth).
|
|
73
|
+
- "127.0.0.1:3000:80"
|
|
74
|
+
environment:
|
|
75
|
+
# With the API key the container needs a password: nginx adds the
|
|
76
|
+
# key only to the dashboard's read-only routes, behind Basic Auth.
|
|
77
|
+
- ADMINA_API_KEY=${ADMINA_API_KEY:-}
|
|
78
|
+
- ADMINA_DASHBOARD_PASSWORD=${ADMINA_DASHBOARD_PASSWORD:-}
|
|
51
79
|
{% if domains.agent_security %}
|
|
52
80
|
depends_on:
|
|
53
81
|
- proxy
|
|
54
82
|
{% endif %}
|
|
55
83
|
healthcheck:
|
|
56
|
-
|
|
84
|
+
# BusyBox wget on nginx:alpine resolves "localhost" to ::1 first and
|
|
85
|
+
# nginx listens on 0.0.0.0; force IPv4 to avoid spurious "Connection
|
|
86
|
+
# refused" failures.
|
|
87
|
+
{% if domains.agent_security %}
|
|
88
|
+
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:80/health"]
|
|
89
|
+
{% else %}
|
|
90
|
+
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:80/"]
|
|
91
|
+
{% endif %}
|
|
57
92
|
interval: 30s
|
|
58
93
|
timeout: 3s
|
|
59
94
|
retries: 3
|
|
@@ -68,12 +103,12 @@ services:
|
|
|
68
103
|
environment:
|
|
69
104
|
- CLICKHOUSE_DB=admina
|
|
70
105
|
- CLICKHOUSE_USER=default
|
|
71
|
-
- CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD
|
|
106
|
+
- CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD must be set (admina dev writes it to .env)}
|
|
72
107
|
- CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1
|
|
73
108
|
volumes:
|
|
74
109
|
- clickhouse-data:/var/lib/clickhouse
|
|
75
110
|
healthcheck:
|
|
76
|
-
test: ["CMD", "clickhouse-client", "--query", "SELECT 1"]
|
|
111
|
+
test: ["CMD", "clickhouse-client", "--user", "default", "--password", "${CLICKHOUSE_PASSWORD}", "--query", "SELECT 1"]
|
|
77
112
|
interval: 5s
|
|
78
113
|
timeout: 3s
|
|
79
114
|
retries: 10
|
|
@@ -87,7 +122,7 @@ services:
|
|
|
87
122
|
image: ollama/ollama:latest
|
|
88
123
|
container_name: {{ project_name }}-ollama
|
|
89
124
|
ports:
|
|
90
|
-
- "11434:11434"
|
|
125
|
+
- "127.0.0.1:11434:11434" # no authentication: localhost only
|
|
91
126
|
volumes:
|
|
92
127
|
- ollama-data:/root/.ollama
|
|
93
128
|
{% if gpu_vendor | default("none") == "nvidia" %}
|
|
@@ -119,7 +154,7 @@ services:
|
|
|
119
154
|
image: chromadb/chroma:0.5.23
|
|
120
155
|
container_name: {{ project_name }}-chromadb
|
|
121
156
|
ports:
|
|
122
|
-
- "8000:8000"
|
|
157
|
+
- "127.0.0.1:8000:8000" # no authentication: localhost only
|
|
123
158
|
volumes:
|
|
124
159
|
- chromadb-data:/chroma/chroma
|
|
125
160
|
environment:
|
|
@@ -142,7 +177,8 @@ services:
|
|
|
142
177
|
image: ghcr.io/open-webui/open-webui:main
|
|
143
178
|
container_name: {{ project_name }}-webui
|
|
144
179
|
ports:
|
|
145
|
-
-
|
|
180
|
+
# Localhost only: sign-up is open, and the first account is the admin.
|
|
181
|
+
- "127.0.0.1:{{ ai_infra_webui_port | default(3080) }}:8080"
|
|
146
182
|
volumes:
|
|
147
183
|
- webui-data:/app/backend/data
|
|
148
184
|
environment:
|
|
@@ -183,8 +219,8 @@ services:
|
|
|
183
219
|
image: otel/opentelemetry-collector-contrib:0.96.0
|
|
184
220
|
container_name: {{ project_name }}-otel
|
|
185
221
|
ports:
|
|
186
|
-
- "4317:4317"
|
|
187
|
-
- "4318:4318"
|
|
222
|
+
- "127.0.0.1:4317:4317" # OTLP gRPC (localhost only)
|
|
223
|
+
- "127.0.0.1:4318:4318" # OTLP HTTP (localhost only)
|
|
188
224
|
depends_on:
|
|
189
225
|
clickhouse:
|
|
190
226
|
condition: service_healthy
|
|
@@ -201,7 +237,7 @@ services:
|
|
|
201
237
|
- "3001:3000"
|
|
202
238
|
environment:
|
|
203
239
|
- GF_SECURITY_ADMIN_USER=admin
|
|
204
|
-
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD
|
|
240
|
+
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:?GRAFANA_ADMIN_PASSWORD must be set (admina dev writes it to .env)}
|
|
205
241
|
- GF_USERS_ALLOW_SIGN_UP=false
|
|
206
242
|
volumes:
|
|
207
243
|
- grafana-data:/var/lib/grafana
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# {{ project_name }} — Environment variables
|
|
2
|
+
# Generated by: admina init
|
|
3
|
+
# The secrets (ADMINA_API_KEY, ADMINA_DASHBOARD_PASSWORD, CLICKHOUSE_PASSWORD,
|
|
4
|
+
# GRAFANA_ADMIN_PASSWORD, WEBUI_SECRET_KEY) are written here from the
|
|
5
|
+
# project's secrets vault by `admina init` and `admina dev`.
|
|
6
|
+
|
|
7
|
+
LOG_LEVEL=INFO
|
|
@@ -19,8 +19,8 @@ paths (``domains.agent_security.firewall.pattern_packs``,
|
|
|
19
19
|
``alert_channels[0].url``):
|
|
20
20
|
|
|
21
21
|
- **errors**: values of the wrong type, each as ``<path>: must be …``
|
|
22
|
-
(true or false, an integer, a number, a
|
|
23
|
-
of mappings, a mapping);
|
|
22
|
+
(true or false, an integer, a number, a finite number greater than 0, a
|
|
23
|
+
string, a list of strings, a list of mappings, a mapping);
|
|
24
24
|
- **unknown**: keys the schema does not know.
|
|
25
25
|
|
|
26
26
|
An empty value (null) is not checked: the reader of the key treats it as
|
|
@@ -35,28 +35,43 @@ malformed entry is skipped with a warning).
|
|
|
35
35
|
|
|
36
36
|
from __future__ import annotations
|
|
37
37
|
|
|
38
|
+
import math
|
|
38
39
|
from collections.abc import Mapping
|
|
39
40
|
from dataclasses import dataclass, field
|
|
40
41
|
from typing import Any
|
|
41
42
|
|
|
42
|
-
__all__ = ["SCHEMA", "SchemaProblems", "find_problems"]
|
|
43
|
+
__all__ = ["SCHEMA", "SchemaProblems", "find_problems", "positive_finite_number"]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def positive_finite_number(value: Any) -> bool:
|
|
47
|
+
"""Whether *value* is a finite number greater than 0 (an int or a float,
|
|
48
|
+
not a bool)."""
|
|
49
|
+
return (
|
|
50
|
+
not isinstance(value, bool)
|
|
51
|
+
and isinstance(value, int | float)
|
|
52
|
+
and math.isfinite(value)
|
|
53
|
+
and value > 0
|
|
54
|
+
)
|
|
43
55
|
|
|
44
56
|
|
|
45
57
|
@dataclass(frozen=True)
|
|
46
58
|
class _Scalar:
|
|
47
|
-
kind: str # bool | int | number | str
|
|
59
|
+
kind: str # bool | int | number | positive | str
|
|
48
60
|
|
|
49
61
|
def describe(self) -> str:
|
|
50
62
|
return {
|
|
51
63
|
"bool": "true or false",
|
|
52
64
|
"int": "an integer",
|
|
53
65
|
"number": "a number",
|
|
66
|
+
"positive": "a finite number greater than 0",
|
|
54
67
|
"str": "a string",
|
|
55
68
|
}[self.kind]
|
|
56
69
|
|
|
57
70
|
def accepts(self, value: Any) -> bool:
|
|
58
71
|
if self.kind == "bool":
|
|
59
72
|
return isinstance(value, bool)
|
|
73
|
+
if self.kind == "positive":
|
|
74
|
+
return positive_finite_number(value)
|
|
60
75
|
if isinstance(value, bool):
|
|
61
76
|
return False
|
|
62
77
|
if self.kind == "int":
|
|
@@ -117,6 +132,7 @@ class _List:
|
|
|
117
132
|
BOOL = _Scalar("bool")
|
|
118
133
|
INT = _Scalar("int")
|
|
119
134
|
NUMBER = _Scalar("number")
|
|
135
|
+
POSITIVE = _Scalar("positive")
|
|
120
136
|
STR = _Scalar("str")
|
|
121
137
|
STRINGS = _List(STR)
|
|
122
138
|
ANY = _Any()
|
|
@@ -181,7 +197,10 @@ SCHEMA = _Section(
|
|
|
181
197
|
{
|
|
182
198
|
"enabled": BOOL,
|
|
183
199
|
"mode": STR,
|
|
184
|
-
|
|
200
|
+
# Read by the Python firewall only, and
|
|
201
|
+
# refused here so that an invalid value
|
|
202
|
+
# stops either engine.
|
|
203
|
+
"heuristic_threshold": POSITIVE,
|
|
185
204
|
"allowed_tags": STRINGS,
|
|
186
205
|
"custom_patterns": _List(ANY),
|
|
187
206
|
"disabled_categories": STRINGS,
|
|
@@ -921,7 +921,7 @@ function dashboard() {
|
|
|
921
921
|
max: 25,
|
|
922
922
|
hint: noAssessment ? 'No gap analysis run yet — see EU AI Act card' : null,
|
|
923
923
|
},
|
|
924
|
-
{ key: 'no_recent_attacks', label: 'No blocked
|
|
924
|
+
{ key: 'no_recent_attacks', label: 'No blocked requests (since start)', value: bd.no_recent_attacks ?? 0, max: 15 },
|
|
925
925
|
{ key: 'forensic_chain_valid', label: 'Forensic chain valid', value: bd.forensic_chain_valid ?? 0, max: 10 },
|
|
926
926
|
];
|
|
927
927
|
},
|
|
@@ -41,6 +41,7 @@ from typing import Any
|
|
|
41
41
|
from urllib.parse import urlsplit
|
|
42
42
|
|
|
43
43
|
from admina.core.types import RiskLevel
|
|
44
|
+
from admina.domains.governance import SCAN_DEPTH
|
|
44
45
|
|
|
45
46
|
__all__ = [
|
|
46
47
|
"EGRESS_SURFACES",
|
|
@@ -60,9 +61,9 @@ logger = logging.getLogger("admina.egress")
|
|
|
60
61
|
#: Surfaces the egress stage can run on (``agent_security.egress.surfaces``).
|
|
61
62
|
EGRESS_SURFACES = ("gateway", "mcp", "integration", "sdk")
|
|
62
63
|
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
|
|
64
|
+
# The walks below stop at the pipeline's SCAN_DEPTH, so egress and the
|
|
65
|
+
# PII/firewall walk agree on how deep a payload is inspected: a call the
|
|
66
|
+
# firewall scans in full is never refused by egress for its depth alone.
|
|
66
67
|
|
|
67
68
|
# Argument names that declare a network destination. Their presence means the
|
|
68
69
|
# call is an egress attempt even when the value cannot be resolved.
|
|
@@ -182,7 +183,7 @@ def _walk(
|
|
|
182
183
|
A truncated walk means part of the arguments was never inspected, so the
|
|
183
184
|
caller cannot claim the call has no destination — see :func:`analyze`.
|
|
184
185
|
"""
|
|
185
|
-
if depth >
|
|
186
|
+
if depth > SCAN_DEPTH:
|
|
186
187
|
# Only a string, dict or list could have hidden a destination; an
|
|
187
188
|
# int, float, bool or None could not, so skipping one loses nothing
|
|
188
189
|
# and must not be reported as an incomplete scan.
|
|
@@ -221,7 +222,7 @@ def _walk(
|
|
|
221
222
|
|
|
222
223
|
def _classify_write_shaped(obj: Any, depth: int) -> str | None:
|
|
223
224
|
"""Return the reason the call is payload-bearing, or None."""
|
|
224
|
-
if depth >
|
|
225
|
+
if depth > SCAN_DEPTH:
|
|
225
226
|
return None
|
|
226
227
|
if isinstance(obj, str):
|
|
227
228
|
# Bare hostnames are recognised here regardless of the key: the
|
|
@@ -275,7 +276,7 @@ def _collect_payload_fields(obj: Any, depth: int, found: list[str]) -> None:
|
|
|
275
276
|
destination and carries a payload, and the payload is what this
|
|
276
277
|
function is for.
|
|
277
278
|
"""
|
|
278
|
-
if depth >
|
|
279
|
+
if depth > SCAN_DEPTH:
|
|
279
280
|
return
|
|
280
281
|
if isinstance(obj, str):
|
|
281
282
|
# A host or URL says where the call goes, which is what the fan-in
|
|
@@ -351,7 +352,7 @@ def payload_fields(params: Any) -> list[str]:
|
|
|
351
352
|
|
|
352
353
|
|
|
353
354
|
def _remote_hint(obj: Any, depth: int = 0) -> bool | None:
|
|
354
|
-
if depth >
|
|
355
|
+
if depth > SCAN_DEPTH:
|
|
355
356
|
return None
|
|
356
357
|
if isinstance(obj, dict):
|
|
357
358
|
for key, value in obj.items():
|
{admina_framework-0.13.0 → admina_framework-0.13.1}/admina/domains/agent_security/firewall.py
RENAMED
|
@@ -19,13 +19,13 @@ Dual-layer defense: regex pattern matching + heuristic analysis.
|
|
|
19
19
|
|
|
20
20
|
import base64
|
|
21
21
|
import logging
|
|
22
|
-
import math
|
|
23
22
|
import re
|
|
24
23
|
import time
|
|
25
24
|
import unicodedata
|
|
26
25
|
from collections.abc import Callable, Iterable
|
|
27
26
|
from typing import TYPE_CHECKING, Any, NamedTuple
|
|
28
27
|
|
|
28
|
+
from admina.core.config_schema import positive_finite_number
|
|
29
29
|
from admina.core.types import RiskLevel
|
|
30
30
|
|
|
31
31
|
if TYPE_CHECKING:
|
|
@@ -809,12 +809,7 @@ def parse_custom_patterns(
|
|
|
809
809
|
|
|
810
810
|
|
|
811
811
|
def _threshold(value: Any) -> float:
|
|
812
|
-
if (
|
|
813
|
-
isinstance(value, bool)
|
|
814
|
-
or not isinstance(value, (int, float))
|
|
815
|
-
or not math.isfinite(value)
|
|
816
|
-
or value <= 0
|
|
817
|
-
):
|
|
812
|
+
if not positive_finite_number(value):
|
|
818
813
|
raise ValueError(
|
|
819
814
|
"agent_security.firewall.heuristic_threshold must be a finite number "
|
|
820
815
|
f"greater than 0 (got {value!r})"
|