admina-framework 0.13.0__tar.gz → 0.13.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.
- {admina_framework-0.13.0/admina_framework.egg-info → admina_framework-0.13.2}/PKG-INFO +58 -13
- {admina_framework-0.13.0 → admina_framework-0.13.2}/README.md +56 -11
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/__init__.py +1 -1
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/main.py +33 -5
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/templates/admina.yaml.j2 +2 -1
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/templates/docker-compose.yml.j2 +46 -10
- admina_framework-0.13.2/admina/cli/templates/env.j2 +7 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/config_schema.py +24 -5
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/dashboard/static/index.html +1 -1
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/egress.py +8 -7
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/firewall.py +2 -7
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/forensic.py +35 -1
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/oisg.py +47 -17
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/governance.py +25 -1
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/engines/__init__.py +4 -5
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/_engines.py +17 -2
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/transports/mcp.py +64 -2
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/api/dashboard.py +25 -10
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/forensic_backend.py +11 -2
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/main.py +14 -6
- {admina_framework-0.13.0 → admina_framework-0.13.2/admina_framework.egg-info}/PKG-INFO +58 -13
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina_framework.egg-info/SOURCES.txt +2 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina_framework.egg-info/requires.txt +1 -1
- {admina_framework-0.13.0 → admina_framework-0.13.2}/pyproject.toml +2 -2
- admina_framework-0.13.2/tests/test_admina_alias.py +56 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_check_versions.py +15 -2
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_config_schema_validation.py +2 -1
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_egress_analyze.py +38 -11
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_egress_pipeline.py +36 -8
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_enabled_surfaces.py +44 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_firewall_heuristic_config.py +57 -1
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_fail_mode.py +99 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_iban_it.py +3 -0
- admina_framework-0.13.2/tests/test_mcp_block_reason.py +148 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_metrics_gateway.py +24 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_pipeline_executor.py +15 -5
- {admina_framework-0.13.0 → admina_framework-0.13.2}/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.2}/LICENSE +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/NOTICE +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/commands/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/forensic.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/redteam.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/templates/main.py.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/templates/plugin.py.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/templates/plugin_pyproject.toml.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/templates/plugin_readme.md.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/cli/templates/plugin_test.py.j2 +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/event_bus.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/exception_log.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/jcs.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/offline.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/secretfile.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/secrets.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/trace_context.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/core/types.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/dashboard/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/dashboard/static/heimdall.png +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/dashboard/static/vendor/alpinejs.min.js +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/coordination.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/fingerprint.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/loop_breaker.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/pattern_packs.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/pattern_timing.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/ruleset.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/agent_security/scan_policy.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/ai_infra/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/ai_infra/llm_engine.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/ai_infra/rag.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/ai_infra/webui.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/ai_act_terms.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/cross_regulation.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/eu_ai_act.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/forensic_files.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/forensic_integrity.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/gdpr.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/nis2.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/oisg_evidence.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/otel.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/compliance/schemas/oisg-evidence.schema.json +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/data_sovereignty/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/data_sovereignty/classification.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/data_sovereignty/email_matching.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/data_sovereignty/iban.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/data_sovereignty/masking.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/data_sovereignty/pii.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/domains/data_sovereignty/residency.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/engines/pii_plugins.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/engines/presidio.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/cheshirecat/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/cheshirecat/admina-plugin/admina_governance.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/crewai/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/crewai/callbacks.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/langchain/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/langchain/callbacks.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/n8n/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/integrations/openclaw/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/base.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/adapters/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/adapters/_streaming.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/adapters/anthropic.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/adapters/bedrock.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/adapters/gemini.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/adapters/mistral.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/adapters/ollama.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/adapters/openai.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/adapters/vllm.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/alerts/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/alerts/log.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/alerts/webhook.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/auth/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/auth/apikey.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/compliance/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/compliance/eu_ai_act.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/connectors/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/connectors/chromadb.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/connectors/filesystem.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/forensic/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/forensic/filesystem.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/guards/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/guards/guardrailsai_guard.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/pii/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/pii/spacy_regex.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/transports/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/builtin/transports/http_rest.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/plugins/registry.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/api/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/api/gateway.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/api/integration.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/body_limit.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/dashboard_session.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/decisions.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/engine_bridge.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/engine_report.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/env_check.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/forensic_probe.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/gateway_body.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/gateway_correlation.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/gateway_outcome.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/gateway_response_scan.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/gateway_scan.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/gateway_transport.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/gateway_upstreams.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/governance.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/log_format.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/loop_lag.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/multi_upstream.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/pipeline_executor.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/request_metrics.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/state.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/proxy/surfaces.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/py.typed +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/baselines/baseline.json +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/corpora/SHA256SUMS +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/corpora/coordination.jsonl +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/corpora/injection.jsonl +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/corpora/loop.jsonl +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/corpora/pii.jsonl +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/corpora.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/detectors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/gate.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/metrics.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/report.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/redteam/runner.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/sdk/__init__.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/sdk/_compat.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/sdk/compliance_kit.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/sdk/errors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/sdk/governed_agent.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/sdk/governed_data.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/sdk/governed_model.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/sdk/retry.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina/sdk/streaming.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina_framework.egg-info/dependency_links.txt +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina_framework.egg-info/entry_points.txt +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/admina_framework.egg-info/top_level.txt +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/setup.cfg +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_admina_config_path.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_ai_act_terms.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_audit_api_source.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_auth_middleware_errors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_banner_engine_info.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_benchmark_14us.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_bus_event_metadata.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_canary_no_content.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_cli_forensic_export.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_cli_redteam.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_coordination.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_coordination_cli.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_coordination_corpus.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_coordination_wiring.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_core_config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_dashboard_container.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_data_classifier_restricted.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_domains.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_domains_governance.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_egress_cli.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_egress_policy.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_egress_surfaces.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_engine_effective.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_engine_startup_errors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_engines.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_entrypoint_script.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_error_disclosure.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_event_loop_lag_metric.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_fingerprint.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_firewall_it_baseline.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_firewall_it_benign.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_firewall_parity.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_firewall_pattern_ids.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_firewall_pattern_packs.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_firewall_pattern_timing.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_atomic_write.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_blackbox.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_config_coherence.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_gateway_records.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_incremental_verify.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_integrity.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_rebuilt_and_duplicates.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_record_signature.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_restart.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_s3_reads.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_forensic_volume.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_block_status.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_correlation.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_endpoints.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_errors.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_forward_body.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_governed_stream.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_guard_fail_mode.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_helpers.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_latency_bench.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_mount.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_outcome_headers.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_passthrough.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_pii_contract.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_ruleset_endpoint.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_scan_coverage.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_upstream_auth.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_gateway_upstream_routes.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_governance_pipeline.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_guard_fail_mode.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_health_fields.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_health_forensic_probe.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_jcs.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_lazy_imports.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_log_format.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_mcp_transport.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_metrics_docs_auth.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_model_allowlist_post.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_offline_guarantee.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_oisg_evidence.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_otel_enabled_flag.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_pattern_probe.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_pii_email_matching.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_pii_mask_style.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_pii_plugin_engines.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_presidio_engine.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_proxy_dashboard_session.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_proxy_forensic_would_action.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_proxy_governance_mode_config.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_proxy_guard_fail_mode.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_proxy_mcp_response_pii.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_proxy_metrics_endpoint.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_proxy_minimal_extra.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_proxy_plugins.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_proxy_security.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_query_api_key_deprecation.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_record_decision.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_redaction_values_only.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_redteam_efficacy.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_redteam_external_corpora.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_redteam_lib.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_release_workflows.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_request_sha256.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_response_scan_option.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_ruleset_parity.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_ruleset_sha256.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_scan_depth.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_scan_policy.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_secret_files.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/tests/test_unknown_admina_env.py +0 -0
- {admina_framework-0.13.0 → admina_framework-0.13.2}/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.2
|
|
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>
|
|
@@ -69,7 +69,7 @@ Requires-Dist: ollama<1,>=0.3; extra == "ollama"
|
|
|
69
69
|
Provides-Extra: anthropic
|
|
70
70
|
Requires-Dist: anthropic<2,>=0.39; extra == "anthropic"
|
|
71
71
|
Provides-Extra: mistral
|
|
72
|
-
Requires-Dist: mistralai<
|
|
72
|
+
Requires-Dist: mistralai<4,>=1.0; extra == "mistral"
|
|
73
73
|
Provides-Extra: gemini
|
|
74
74
|
Requires-Dist: google-genai<3,>=1.0; extra == "gemini"
|
|
75
75
|
Provides-Extra: bedrock
|
|
@@ -258,6 +258,11 @@ pip install admina-framework
|
|
|
258
258
|
> matters more than that extra coverage. See
|
|
259
259
|
> [Performance](#performance--hybrid-python--rust-engine) for the trade-off.
|
|
260
260
|
|
|
261
|
+
The PyPI package `admina` is an alias: `pip install admina` installs
|
|
262
|
+
`admina-framework` at the same version, and `admina[X]` installs
|
|
263
|
+
`admina-framework[X]` for every extra above. It has no code of its own, so
|
|
264
|
+
both names install the same `admina` module.
|
|
265
|
+
|
|
261
266
|
### Or install from source
|
|
262
267
|
|
|
263
268
|
```bash
|
|
@@ -439,6 +444,17 @@ dependency: one uvicorn serves the proxy API and the dashboard SPA on the
|
|
|
439
444
|
same port. Use `--stack` for the production-like Docker compose, or
|
|
440
445
|
`--with-llm` to also boot local LLM services.
|
|
441
446
|
|
|
447
|
+
Local mode uses the forensic backend of `FORENSIC_BACKEND` or of
|
|
448
|
+
`admina.yaml`, and memory when neither sets one. In the compose file that
|
|
449
|
+
`admina init` writes, the dashboard (`127.0.0.1:3000`), OTEL and the
|
|
450
|
+
`--with-llm` services listen on the loopback interface only; the dashboard
|
|
451
|
+
asks for `ADMINA_DASHBOARD_PASSWORD` and the API key (both shown by
|
|
452
|
+
`admina password show`). The proxy reaches the MCP server at
|
|
453
|
+
`UPSTREAM_MCP_URL` (default `http://host.docker.internal:9000`, an MCP
|
|
454
|
+
server on the host). `CLICKHOUSE_PASSWORD` and `GRAFANA_ADMIN_PASSWORD`
|
|
455
|
+
come from the `.env` that `admina dev` writes; a plain `docker compose up`
|
|
456
|
+
stops when they are not set.
|
|
457
|
+
|
|
442
458
|
## Dashboard
|
|
443
459
|
|
|
444
460
|
Real-time governance dashboard on port 3000:
|
|
@@ -858,6 +874,12 @@ one trailing newline removed. A missing, unreadable or empty file, or a key
|
|
|
858
874
|
set both directly and as a file, stops the proxy; the error names the
|
|
859
875
|
setting and the path, never the key. `ADMINA_FORENSIC_STATE_KEY_FILE` inside
|
|
860
876
|
the forensic directory stops it too: the key must be kept outside the store.
|
|
877
|
+
The built-in `apikey` auth provider reads `ADMINA_API_KEY` from the
|
|
878
|
+
environment only, and is loaded only when it is set there. A key given only
|
|
879
|
+
through `ADMINA_API_KEY_FILE` or `.env` is checked by the authentication
|
|
880
|
+
middleware itself (the same headers, the same constant-time comparison), and
|
|
881
|
+
`POST /api/v1/audit` then stamps `submitted_by: "api_key"` instead of
|
|
882
|
+
`"user:api_key_user"`.
|
|
861
883
|
|
|
862
884
|
**Forensic store.** `FORENSIC_BACKEND` (`memory`, `filesystem`, `s3`) and
|
|
863
885
|
`FORENSIC_BASE_DIR` choose where the forensic records go. When they are not
|
|
@@ -899,7 +921,12 @@ directory (UTC hour of the write, sequence number on eight digits), with
|
|
|
899
921
|
`admina-forensic/1 record signature` under the chain-state key
|
|
900
922
|
(`ADMINA_FORENSIC_STATE_KEY` or `ADMINA_FORENSIC_STATE_KEY_FILE`, kept
|
|
901
923
|
outside the forensic directory); `record_sig_alg`: `hmac-sha256`. Without
|
|
902
|
-
a key a record has `record_sig_alg: "none"` and no `record_sig`.
|
|
924
|
+
a key a record has `record_sig_alg: "none"` and no `record_sig`. Since
|
|
925
|
+
0.13.1, when a store that holds signed records is opened without its
|
|
926
|
+
key, the proxy does not start in `closed` fail mode
|
|
927
|
+
(`ADMINA_FORENSIC_FAIL_MODE`) and logs a warning in `open` mode, where
|
|
928
|
+
the records it then writes are unsigned (a later verification with the
|
|
929
|
+
key reports `unsigned`).
|
|
903
930
|
|
|
904
931
|
The chain state (`_chain_state.json`, with its HMAC-SHA256 in
|
|
905
932
|
`_chain_state.json.sig` when a key is set) holds `record_count`,
|
|
@@ -974,7 +1001,10 @@ introduced.
|
|
|
974
1001
|
**Audit records.** `POST /api/v1/audit` records the event it receives with
|
|
975
1002
|
`source: "api_v1_audit"` (a `source` in the request is kept as
|
|
976
1003
|
`client_source`) and `submitted_by`: the credential the request was admitted
|
|
977
|
-
with (`api_key`, `append_key`, `user:<id>` or `unauthenticated`).
|
|
1004
|
+
with (`api_key`, `append_key`, `user:<id>` or `unauthenticated`). With
|
|
1005
|
+
`ADMINA_API_KEY` in the environment the built-in `apikey` auth provider
|
|
1006
|
+
admits API-key requests, stamped `user:api_key_user`; `api_key` is the stamp
|
|
1007
|
+
when the key comes only from `ADMINA_API_KEY_FILE` or `.env`. An
|
|
978
1008
|
`event_type` of the records the proxy writes itself (`mcp_request`,
|
|
979
1009
|
`mcp_response`, `gateway_request`, `gateway_response`, `gateway_response_scan`,
|
|
980
1010
|
`policy_violation`, `chain_state_rebuilt`) is refused with `400`.
|
|
@@ -1423,18 +1453,18 @@ fields are always scanned.
|
|
|
1423
1453
|
|
|
1424
1454
|
The scan follows the body 32 levels deep: the body is level 0, its fields
|
|
1425
1455
|
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":
|
|
1456
|
+
With the firewall on, a request with a string nested deeper is blocked in
|
|
1457
|
+
`enforce` mode (`X-Admina-Would-Action: BLOCK` in `observe` and `dry-run`),
|
|
1458
|
+
and its record has `checks.scan_depth = {"action": "BLOCK", "reason":
|
|
1429
1459
|
"depth_limit_exceeded"}`. Tool call arguments nested deeper than the JSON
|
|
1430
1460
|
parser reads are scanned as they are, and the request is blocked the same
|
|
1431
1461
|
way.
|
|
1432
1462
|
|
|
1433
|
-
`/mcp`
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1463
|
+
`/mcp` scans and redacts to the same depth. With the firewall or PII
|
|
1464
|
+
redaction on, a request holding a string, or a non-empty object or array,
|
|
1465
|
+
past level 32 is blocked the same way, since that text would be neither
|
|
1466
|
+
scanned nor redacted. `content` of `/api/v1/validate` must be a string, so
|
|
1467
|
+
that block does not apply there.
|
|
1438
1468
|
|
|
1439
1469
|
#### Scan scope
|
|
1440
1470
|
|
|
@@ -1479,6 +1509,10 @@ case-sensitive. The caller must keep these tags out of text written by
|
|
|
1479
1509
|
untrusted parties, because the gateway cannot tell such text from its own
|
|
1480
1510
|
blocks.
|
|
1481
1511
|
|
|
1512
|
+
`ruleset_sha256()` hashes the Admina version too (and the `admina-core`
|
|
1513
|
+
version with the Rust engine), so a ruleset hash changes with every release:
|
|
1514
|
+
recompute the hashes in `gateway.prescan_rulesets` after each upgrade.
|
|
1515
|
+
|
|
1482
1516
|
Trust model: with scan policies on, the gateway takes the caller's word for
|
|
1483
1517
|
what it has scanned. Any caller that holds the API key can send the header,
|
|
1484
1518
|
and the ruleset it must declare is not a secret (it is on every response and
|
|
@@ -1719,7 +1753,10 @@ cd integrations/cheshirecat/admina-plugin
|
|
|
1719
1753
|
|
|
1720
1754
|
<br>
|
|
1721
1755
|
|
|
1722
|
-
Governs every LLM call and tool invocation in-process
|
|
1756
|
+
Governs every LLM call and tool invocation in-process. Install
|
|
1757
|
+
`admina-framework[proxy,nlp]`: the loop detection that the callbacks turn
|
|
1758
|
+
on by default needs scikit-learn from `[proxy]` (or pass
|
|
1759
|
+
`loop_detection=False`).
|
|
1723
1760
|
|
|
1724
1761
|
```python
|
|
1725
1762
|
from admina.integrations.langchain.callbacks import AdminaCallbackHandler
|
|
@@ -1967,6 +2004,14 @@ curl -X POST http://localhost:8080/api/compliance/classify \
|
|
|
1967
2004
|
curl http://localhost:8080/api/dashboard/score
|
|
1968
2005
|
```
|
|
1969
2006
|
|
|
2007
|
+
A blocked `/mcp` call is answered with a JSON-RPC error (`-32600`, "Request
|
|
2008
|
+
blocked by Admina governance") whose `error.data` holds the `event_id` of
|
|
2009
|
+
the forensic record and a `reason`: `injection_detected` (firewall),
|
|
2010
|
+
`scan_depth_exceeded` (text nested deeper than the scan depth),
|
|
2011
|
+
`egress_refused` (egress policy), `guard_blocked` (a request guard, also a
|
|
2012
|
+
guard error under `ADMINA_GUARD_FAIL_MODE=closed`) or `response_blocked`
|
|
2013
|
+
(a response guard). Before 0.13.1 every block reported `injection_detected`.
|
|
2014
|
+
|
|
1970
2015
|
</details>
|
|
1971
2016
|
|
|
1972
2017
|
## Project documents
|
|
@@ -174,6 +174,11 @@ pip install admina-framework
|
|
|
174
174
|
> matters more than that extra coverage. See
|
|
175
175
|
> [Performance](#performance--hybrid-python--rust-engine) for the trade-off.
|
|
176
176
|
|
|
177
|
+
The PyPI package `admina` is an alias: `pip install admina` installs
|
|
178
|
+
`admina-framework` at the same version, and `admina[X]` installs
|
|
179
|
+
`admina-framework[X]` for every extra above. It has no code of its own, so
|
|
180
|
+
both names install the same `admina` module.
|
|
181
|
+
|
|
177
182
|
### Or install from source
|
|
178
183
|
|
|
179
184
|
```bash
|
|
@@ -355,6 +360,17 @@ dependency: one uvicorn serves the proxy API and the dashboard SPA on the
|
|
|
355
360
|
same port. Use `--stack` for the production-like Docker compose, or
|
|
356
361
|
`--with-llm` to also boot local LLM services.
|
|
357
362
|
|
|
363
|
+
Local mode uses the forensic backend of `FORENSIC_BACKEND` or of
|
|
364
|
+
`admina.yaml`, and memory when neither sets one. In the compose file that
|
|
365
|
+
`admina init` writes, the dashboard (`127.0.0.1:3000`), OTEL and the
|
|
366
|
+
`--with-llm` services listen on the loopback interface only; the dashboard
|
|
367
|
+
asks for `ADMINA_DASHBOARD_PASSWORD` and the API key (both shown by
|
|
368
|
+
`admina password show`). The proxy reaches the MCP server at
|
|
369
|
+
`UPSTREAM_MCP_URL` (default `http://host.docker.internal:9000`, an MCP
|
|
370
|
+
server on the host). `CLICKHOUSE_PASSWORD` and `GRAFANA_ADMIN_PASSWORD`
|
|
371
|
+
come from the `.env` that `admina dev` writes; a plain `docker compose up`
|
|
372
|
+
stops when they are not set.
|
|
373
|
+
|
|
358
374
|
## Dashboard
|
|
359
375
|
|
|
360
376
|
Real-time governance dashboard on port 3000:
|
|
@@ -774,6 +790,12 @@ one trailing newline removed. A missing, unreadable or empty file, or a key
|
|
|
774
790
|
set both directly and as a file, stops the proxy; the error names the
|
|
775
791
|
setting and the path, never the key. `ADMINA_FORENSIC_STATE_KEY_FILE` inside
|
|
776
792
|
the forensic directory stops it too: the key must be kept outside the store.
|
|
793
|
+
The built-in `apikey` auth provider reads `ADMINA_API_KEY` from the
|
|
794
|
+
environment only, and is loaded only when it is set there. A key given only
|
|
795
|
+
through `ADMINA_API_KEY_FILE` or `.env` is checked by the authentication
|
|
796
|
+
middleware itself (the same headers, the same constant-time comparison), and
|
|
797
|
+
`POST /api/v1/audit` then stamps `submitted_by: "api_key"` instead of
|
|
798
|
+
`"user:api_key_user"`.
|
|
777
799
|
|
|
778
800
|
**Forensic store.** `FORENSIC_BACKEND` (`memory`, `filesystem`, `s3`) and
|
|
779
801
|
`FORENSIC_BASE_DIR` choose where the forensic records go. When they are not
|
|
@@ -815,7 +837,12 @@ directory (UTC hour of the write, sequence number on eight digits), with
|
|
|
815
837
|
`admina-forensic/1 record signature` under the chain-state key
|
|
816
838
|
(`ADMINA_FORENSIC_STATE_KEY` or `ADMINA_FORENSIC_STATE_KEY_FILE`, kept
|
|
817
839
|
outside the forensic directory); `record_sig_alg`: `hmac-sha256`. Without
|
|
818
|
-
a key a record has `record_sig_alg: "none"` and no `record_sig`.
|
|
840
|
+
a key a record has `record_sig_alg: "none"` and no `record_sig`. Since
|
|
841
|
+
0.13.1, when a store that holds signed records is opened without its
|
|
842
|
+
key, the proxy does not start in `closed` fail mode
|
|
843
|
+
(`ADMINA_FORENSIC_FAIL_MODE`) and logs a warning in `open` mode, where
|
|
844
|
+
the records it then writes are unsigned (a later verification with the
|
|
845
|
+
key reports `unsigned`).
|
|
819
846
|
|
|
820
847
|
The chain state (`_chain_state.json`, with its HMAC-SHA256 in
|
|
821
848
|
`_chain_state.json.sig` when a key is set) holds `record_count`,
|
|
@@ -890,7 +917,10 @@ introduced.
|
|
|
890
917
|
**Audit records.** `POST /api/v1/audit` records the event it receives with
|
|
891
918
|
`source: "api_v1_audit"` (a `source` in the request is kept as
|
|
892
919
|
`client_source`) and `submitted_by`: the credential the request was admitted
|
|
893
|
-
with (`api_key`, `append_key`, `user:<id>` or `unauthenticated`).
|
|
920
|
+
with (`api_key`, `append_key`, `user:<id>` or `unauthenticated`). With
|
|
921
|
+
`ADMINA_API_KEY` in the environment the built-in `apikey` auth provider
|
|
922
|
+
admits API-key requests, stamped `user:api_key_user`; `api_key` is the stamp
|
|
923
|
+
when the key comes only from `ADMINA_API_KEY_FILE` or `.env`. An
|
|
894
924
|
`event_type` of the records the proxy writes itself (`mcp_request`,
|
|
895
925
|
`mcp_response`, `gateway_request`, `gateway_response`, `gateway_response_scan`,
|
|
896
926
|
`policy_violation`, `chain_state_rebuilt`) is refused with `400`.
|
|
@@ -1339,18 +1369,18 @@ fields are always scanned.
|
|
|
1339
1369
|
|
|
1340
1370
|
The scan follows the body 32 levels deep: the body is level 0, its fields
|
|
1341
1371
|
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":
|
|
1372
|
+
With the firewall on, a request with a string nested deeper is blocked in
|
|
1373
|
+
`enforce` mode (`X-Admina-Would-Action: BLOCK` in `observe` and `dry-run`),
|
|
1374
|
+
and its record has `checks.scan_depth = {"action": "BLOCK", "reason":
|
|
1345
1375
|
"depth_limit_exceeded"}`. Tool call arguments nested deeper than the JSON
|
|
1346
1376
|
parser reads are scanned as they are, and the request is blocked the same
|
|
1347
1377
|
way.
|
|
1348
1378
|
|
|
1349
|
-
`/mcp`
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1379
|
+
`/mcp` scans and redacts to the same depth. With the firewall or PII
|
|
1380
|
+
redaction on, a request holding a string, or a non-empty object or array,
|
|
1381
|
+
past level 32 is blocked the same way, since that text would be neither
|
|
1382
|
+
scanned nor redacted. `content` of `/api/v1/validate` must be a string, so
|
|
1383
|
+
that block does not apply there.
|
|
1354
1384
|
|
|
1355
1385
|
#### Scan scope
|
|
1356
1386
|
|
|
@@ -1395,6 +1425,10 @@ case-sensitive. The caller must keep these tags out of text written by
|
|
|
1395
1425
|
untrusted parties, because the gateway cannot tell such text from its own
|
|
1396
1426
|
blocks.
|
|
1397
1427
|
|
|
1428
|
+
`ruleset_sha256()` hashes the Admina version too (and the `admina-core`
|
|
1429
|
+
version with the Rust engine), so a ruleset hash changes with every release:
|
|
1430
|
+
recompute the hashes in `gateway.prescan_rulesets` after each upgrade.
|
|
1431
|
+
|
|
1398
1432
|
Trust model: with scan policies on, the gateway takes the caller's word for
|
|
1399
1433
|
what it has scanned. Any caller that holds the API key can send the header,
|
|
1400
1434
|
and the ruleset it must declare is not a secret (it is on every response and
|
|
@@ -1635,7 +1669,10 @@ cd integrations/cheshirecat/admina-plugin
|
|
|
1635
1669
|
|
|
1636
1670
|
<br>
|
|
1637
1671
|
|
|
1638
|
-
Governs every LLM call and tool invocation in-process
|
|
1672
|
+
Governs every LLM call and tool invocation in-process. Install
|
|
1673
|
+
`admina-framework[proxy,nlp]`: the loop detection that the callbacks turn
|
|
1674
|
+
on by default needs scikit-learn from `[proxy]` (or pass
|
|
1675
|
+
`loop_detection=False`).
|
|
1639
1676
|
|
|
1640
1677
|
```python
|
|
1641
1678
|
from admina.integrations.langchain.callbacks import AdminaCallbackHandler
|
|
@@ -1883,6 +1920,14 @@ curl -X POST http://localhost:8080/api/compliance/classify \
|
|
|
1883
1920
|
curl http://localhost:8080/api/dashboard/score
|
|
1884
1921
|
```
|
|
1885
1922
|
|
|
1923
|
+
A blocked `/mcp` call is answered with a JSON-RPC error (`-32600`, "Request
|
|
1924
|
+
blocked by Admina governance") whose `error.data` holds the `event_id` of
|
|
1925
|
+
the forensic record and a `reason`: `injection_detected` (firewall),
|
|
1926
|
+
`scan_depth_exceeded` (text nested deeper than the scan depth),
|
|
1927
|
+
`egress_refused` (egress policy), `guard_blocked` (a request guard, also a
|
|
1928
|
+
guard error under `ADMINA_GUARD_FAIL_MODE=closed`) or `response_blocked`
|
|
1929
|
+
(a response guard). Before 0.13.1 every block reported `injection_detected`.
|
|
1930
|
+
|
|
1886
1931
|
</details>
|
|
1887
1932
|
|
|
1888
1933
|
## 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.2}/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():
|