Skip to main content
Security environment variables control opt-in access to potentially dangerous operations, ensuring secure defaults for RCE and session hijacking prevention.
The user opts in with PRAISONAI_ALLOW_LOCAL_TOOLS; without it, dangerous local tools stay blocked by default.

Quick Start

1

Simple Usage

Enable local tools.py loading before starting an agent:
2

With Configuration

Enable job workflows and remote browser access when required:

How It Works


Environment Variables

PRAISONAI_TOOL_SAFETY

Controls whether dangerous built-in tools (shell exec, file delete/move/copy, code execution) are gated by the approval system. Default: unset → default preset active (blocks destructive ops in CI, asks on TTY)
--dangerously-skip-approval on praisonai code automatically exports PRAISONAI_TOOL_SAFETY=off so that child processes inherit the bypass.
Usage Example:

PRAISONAI_ALLOW_LOCAL_TOOLS

Controls automatic loading of tools.py files from the current working directory. Security Risk: Remote Code Execution (RCE) via malicious tools.py files
Affected Components (verified against PR #1658 head 83b8b14c):
  • praisonai wrapper agent generator (generate_crew_and_kickoff and _run_praisonai), now delegating to ToolResolver.get_local_callables() / ToolResolver.get_local_tool_classes()
  • praisonai.tool_resolver.ToolResolver._load_local_tools (single source of truth; the env-var gate itself is enforced by praisonai._safe_loader.load_user_module)
  • praisonai run YAML workflows (recipe tools.py under _run_yaml_workflow)
  • praisonai research --tools <file.py>
  • praisonai chat --rewrite-tools <file.py> and --expand-tools <file.py>
  • Generic CLI _load_tools(tools_path)
  • HTTP API: praisonai.api.call.import_tools_from_file (raises ValueError if disabled)
  • Path-traversal guard: files outside the current working directory are refused even when PRAISONAI_ALLOW_LOCAL_TOOLS=true
  • praisonaiagents.workflows.workflows.AgentFlow (SDK-level: skips tools.py next to the workflow class when unset)
Even when PRAISONAI_ALLOW_LOCAL_TOOLS=true, the loader refuses any path outside the current working directory. This is a deliberate defence-in-depth layer for HTTP-API callers (praisonai.api.call.import_tools_from_file) where the path can come from network input. Move the tools.py you want to load into your CWD if you hit Refusing to exec ... outside working directory. in the logs.
PRAISONAI_ALLOW_LOCAL_TOOLS accepts only true (case-insensitive) in the wrapper (praisonai). Values like 1, yes, or on are not truthy for the wrapper (unlike PRAISONAI_ALLOW_TEMPLATE_TOOLS).
Truthy-value inconsistency across surfaces: The wrapper (praisonai) accepts only true; the SDK’s AgentFlow (praisonaiagents.workflows.workflows) accepts true, 1, or yes; the SDK’s recipe path in workflows.py accepts true, 1, yes, or on. If you rely on PRAISONAI_ALLOW_LOCAL_TOOLS=1, the wrapper will reject it even though the SDK’s AgentFlow will accept it. This is the SDK’s actual behavior — set true to be safe across all surfaces.
CLI equivalent: praisonai run --allow-local-tools sets this env var for the duration of the run and restores the prior value in a finally. Prefer the flag for a per-invocation grant — the env var persists for the shell.
Usage Example:
Error & Warning Messages
Reading the “No tools loaded from …” warning. The dual-cause wording is intentional (PR #2935) — an empty result now means either PRAISONAI_ALLOW_LOCAL_TOOLS is unset or the target module has no public functions after filtering. If the env var is already set, open the file and check that it exports def functions with names that don’t start with _.

PRAISONAI_ALLOW_TEMPLATE_TOOLS

Controls implicit tools.py autoload by the template tool-override system, both from the current working directory and from a recipe’s template directory. Security Risk: Remote Code Execution (RCE) when loading recipes/templates from untrusted sources (e.g. recipes fetched from a remote registry)
Default: unset → disabled
Accepted truthy values: 1, true, yes, on (case-insensitive, whitespace-stripped)
Affected Components:
  • praisonai.templates.tool_override.create_tool_registry_with_overrides
  • praisonai.templates.tool_override.resolve_tools
Note: Explicit override_files, override_dirs, and tools_sources continue to work without this opt-in and are the recommended way to load custom tools.
PRAISONAI_ALLOW_TEMPLATE_TOOLS and PRAISONAI_ALLOW_LOCAL_TOOLS are intentionally distinct gates — the wrapper keeps its own template/CWD tools.py autoload (with PRAISONAI_ALLOW_TEMPLATE_TOOLS) even though the canonical resolver now owns praisonai-tools discovery (PraisonAI#3122). Reason: the wrapper gate is skip-on-error and allows an explicit template directory outside the CWD, which the canonical PRAISONAI_ALLOW_LOCAL_TOOLS + CWD-boundary gate does not. Set the one that matches your load path — set both if a recipe ships a tools.py in a template directory and you also depend on the canonical loader.
Usage Example:

PRAISONAI_AUTH_CONTENT

Loads the entire credential store from a JSON environment variable, bypassing disk I/O entirely (zero-disk mode). Intended for ephemeral containers/CI where OAuth tokens and API keys must not be persisted. Security value: secret hygiene — not an RCE gate. It reduces on-disk secret sprawl. The env var itself must be treated as a secret; inject it only from a secrets manager, never commit it, and prefer a CI runtime that scrubs env vars from build logs.
Default: unset → on-disk store at ~/.praisonai/credentials.json is used (unchanged behaviour). Value shape: a JSON object mapping provider → credential object (not 1/true). Each credential mirrors ProviderCredentialapi_key + auth_method for API keys, plus access_token, refresh_token, expires_at for OAuth. Affected Components (verified against PR #3775 head 52f33362):
  • praisonai_code.cli.configuration.credentials.CredentialStore__init__ (env-blob parsing), _read_credentials (returns the in-memory blob), _write_credentials (no-ops disk I/O; updates the in-memory store only), and the is_in_memory property
  • praisonai_code.cli.commands.auth.auth_list / auth_status — label the Source column env (in-memory) when the store is in memory
Error paths: the store fails fast rather than silently re-enabling disk persistence. Usage Example:
Precedence is env blob > disk file. Per-provider API-key env vars (OPENAI_API_KEY, …) still win for that specific provider. See Auth → Zero-disk mode for the full reference.

PRAISONAI_CONFIG_CONTENT

Supplies the entire user-config layer as an inline JSON/YAML blob parsed in memory. When set, PraisonAI never reads ~/.praisonai/config.yaml or a walk-up praisonai.yaml project file — the direct sibling of PRAISONAI_AUTH_CONTENT for a fully zero-disk run in ephemeral containers/CI. Security value: secret hygiene / stateless-run control — not an RCE gate. It keeps config.yaml (MCP server blocks, permissions arrays, model allowlists, hooks) off disk. Treat the env var itself as a secret: inject it from a secrets manager, never commit it, and only use it in CI runtimes that scrub env vars from build logs.
Default: unset → global + project file discovery (unchanged behaviour). Value shape: any valid YAML mapping — JSON works because JSON is a strict subset of YAML. Same schema as an on-disk config.yaml. The value must be a mapping, not a list or scalar. Interpolation: ${VAR}, {env:VAR}, and {file:...} directives still resolve — the blob composes with the same conventions as an on-disk file. Precedence: occupies the same slot as discovered global/project files. Inline content wins over PRAISONAI_CONFIG (path). Per-key env vars (PRAISONAI_MODEL, …) and CLI flags still override it; managed policy is still enforced on top. Validation / error paths (verified against PR #3983 head 382c6fd6): Usage Example (auth + config in one env set):
Higher-precedence layers still win: per-key env vars (PRAISONAI_MODEL, …) and CLI flags (--model) override any scalar the blob sets. Managed policy still layers below user config. See PRAISONAI_AUTH_CONTENT for the credential sibling, Env → Related Environment Variables, and Env Config Injection for the full reference and decision diagram.

PRAISONAI_CONFIG

Names an explicit config file path to load as the user-config layer, replacing the discovered global + project files. Lower precedence than PRAISONAI_CONFIG_CONTENT (inline wins). Useful when the config is materialised via a mounted secret (Docker/Kubernetes secret volume or Kubernetes ConfigMap) but you don’t want it discoverable via file walk-up.
Default: unset → global + project file discovery (unchanged behaviour). Value shape: an absolute or tilde-expanded path to any file the resolver accepts (YAML/JSON). Discovered global + project files are ignored while this path is used. Behaviour on missing / unreadable file: does not hard-fail. Emits UserWarning: "PRAISONAI_CONFIG points at '<path>', which could not be read as a config file; falling back to file discovery." and falls back to discovery. A non-mapping file body is skipped the same way.
When both PRAISONAI_CONFIG_CONTENT and PRAISONAI_CONFIG are set, the inline content wins and this path is ignored (no warning, by design).
The provenance report (praisonai config provenance) reports a layer of env-config for keys supplied this way — source is env:PRAISONAI_CONFIG_CONTENT for inline content, or the absolute file path for explicit-path mode.

PRAISONAI_ALLOW_PLUGIN_DISCOVERY

Controls automatic discovery and loading of plugins from .praisonai/plugins/ (project-level) and ~/.praisonai/plugins/ (user-level) directories. Security Risk: Remote Code Execution (RCE) via malicious third-party plugins discovered on sys.path.
Default: unset → discovery skipped silently (debug log only)
Accepted truthy values: true, 1, yes (case-insensitive, whitespace-stripped). on is not accepted.
Affected Component: praisonaiagents.plugins.manager.PluginManager.discover_and_load_plugins Default behavior when unset: discover_and_load_plugins() is a no-op and returns 0. A logger.debug message fires: Plugin auto-discovery disabled; set PRAISONAI_ALLOW_PLUGIN_DISCOVERY=true. Usage Example:
Workaround when off: Register plugins explicitly without enabling discovery:

PRAISONAI_NO_PLUGINS

Suppresses external-plugin discovery for the current process — the inverse of PRAISONAI_ALLOW_PLUGIN_DISCOVERY. Use for clean, deterministic baselines while debugging or in CI. Security value: reproduces a known-clean run when triaging whether a hook-injecting plugin is influencing behaviour. Does not change persisted .praisonai/config.yaml state.
Default: unset → plugins load normally
Accepted truthy values: true, 1, yes (case-insensitive, whitespace-stripped). on is not accepted.
Affected Component: praisonaiagents.plugins.manager.PluginManager.discover_entry_points and .auto_discover_plugins — both short-circuit to 0 when the var is truthy or the constructor was called with disabled=True. Precedence: PluginManager(disabled=True|False) constructor param > PRAISONAI_NO_PLUGINS env var > default (load plugins).
CLI equivalent: praisonai run --pure / chat --pure / code --pure (long alias --no-plugins) sets the env var for the duration of the run and restores the prior value in a finally. Prefer the flag for a per-invocation grant — the env var persists for the shell. See Pure Mode.
Usage Example:

PRAISONAI_PROJECT_ROOT

Sets the allowed root directory for agent output file writes. Writes outside this root are silently blocked. Security Risk: Path-traversal write outside the project tree (e.g. output_file="../../etc/passwd").
Default: unset → os.getcwd() at save time
Affected Component: praisonaiagents.agent.memory_mixin.MemoryMixin._save_output_to_file
Silent-failure semantics: When the output path resolves outside the project root, the save returns False, logs Output file %r is outside project root %r; skipping save at logging.warning, and prints ⚠️ Output path outside project root: <path> to stdout. No exception is raised. Usage Example:

ALLOW_LOCAL_CRAWL

Bypasses SSRF protection for loopback, private, link-local, multicast, and unspecified IP addresses in web crawl tools. Security Risk: Server-Side Request Forgery (SSRF) — agents fetching http://169.254.169.254/... (cloud metadata), http://localhost:6379 (Redis), etc.
Default: unset → loopback/private/link-local/multicast/unspecified IPs blocked; returns {"error": "URL blocked by SSRF policy"} per blocked URL
Accepted truthy values: true only — exact, case-sensitive match. This is stricter than other env vars on this page.
Affected Components:
  • praisonaiagents.tools.web_crawl_tools_is_safe_crawl_url calls is_safe_http_url(url) with no allowlist, so the crawler goes through the strict path.
  • praisonaiagents.tools.url_safety.is_safe_http_url — accepts an explicit allowlist keyword; only the web crawler uses the no-allowlist path. SearXNG passes its own loopback allowlist (see SEARXNG_URL_ALLOWLIST).
web_crawl blocks loopback (127.0.0.1, localhost, ::1) by default — set ALLOW_LOCAL_CRAWL=true to reach a local dev server. SearXNG reaches loopback by design and does not need this flag. That is why localhost:32768 works for SearXNG search while 127.0.0.1 stays blocked for crawling.
One predicate (is_safe_http_url) serves both callers; the SearXNG path opts into a loopback allowlist, the crawler passes none. Usage Example:
ALLOW_LOCAL_CRAWL uses exact "true" matching (case-sensitive). "True", "TRUE", "1", and "yes" are all rejected. This differs from the PRAISONAI_* variables on this page.

SEARXNG_URL_ALLOWLIST

Adds extra hostnames the SearXNG search call sites may reach, on top of the built-in loopback set (localhost, 127.0.0.1, ::1). For self-hosting SearXNG on a custom host that resolves to loopback. Security Risk: Server-Side Request Forgery (SSRF) — a misconfigured allowlist that reached private ranges could expose internal services. This env var is designed so it cannot: entries can only exempt the loopback class.
Scope: SearXNG only. The web crawler (web_crawl / _is_safe_crawl_url) does not read this list.
Format: comma-separated hostnames — whitespace is trimmed and each host is lowercased. Empty or unset → only the built-in loopback set applies.
Default: unset → SearXNG reaches only localhost, 127.0.0.1, ::1.
Affected Components:
  • praisonaiagents.tools.url_safety._env_allowlist — reads SEARXNG_URL_ALLOWLIST
  • praisonaiagents.tools.url_safety.validate_searxng_url — merges the env list with the built-in loopback set and passes it as the allowlist to is_safe_http_url
  • praisonaiagents.tools.searxng_tools.searxng_search and praisonaiagents.tools.web_search (SearXNG provider)
Usage Example:
An allowlist entry can only exempt the loopback address class. Any host that resolves to a private, link-local, multicast, or unspecified address stays blocked — even when allowlisted. So SEARXNG_URL_ALLOWLIST=169.254.169.254 can not reach cloud metadata, and 10.x / 192.168.x ranges remain blocked. The IP-class checks run before the allowlist is consulted.

PRAISONAI_ALLOW_JOB_WORKFLOWS

Controls execution of job and hybrid workflow types that can run shell commands and scripts. Security Risk: Remote Code Execution (RCE) via malicious YAML workflows
Workflow Types Affected:
  • Job workflows: Direct shell, Python, and script execution
  • Hybrid workflows: Combined agent + job execution
Usage Example:

PRAISONAI_BROWSER_ALLOW_REMOTE

Controls browser server binding to non-loopback interfaces (0.0.0.0, remote IPs). Security Risk: WebSocket session hijacking and unauthorized browser access
Default Behavior:
  • Binds to 127.0.0.1 (localhost only)
  • Blocks attempts to bind to 0.0.0.0 or remote interfaces
Usage Example:

PRAISONAI_RUN_SYNC_TIMEOUT

Default maximum seconds the wrapper’s sync-to-async bridge will wait for a coroutine to complete. Default: 300 (5 minutes)
Applies to:
  • Every praisonai CLI entry and wrapper-based server (gateway, a2u, mcp_server, scheduler) via praisonai._async_bridge.run_sync.
  • Every ACP/LSP agent-centric tool call in praison "…" and praisonai tui launch via the sibling praisonai_code.cli.features.agent_tools._run_sync bridge (added in PR #3361).
The SDK (praisonaiagents) uses its own separate bridge — see Async Bridge for the full map.

Common Patterns


Migration Guide

Upgrading from Vulnerable Versions

1

Identify Usage

Check if you use any of these features:
  • Local tools.py files
  • Recipes / templates that ship a tools.py and rely on it being implicitly loaded
  • Job or hybrid workflows with shell/script execution
  • Browser server binding to 0.0.0.0
  • HTTP API callers that pass a file_path to praisonai.api.call.import_tools_from_file — these now raise ValueError until you opt in
2

Add Environment Variables

3

Test Functionality

Verify your existing workflows still work:
4

Review Security

Evaluate if you really need each dangerous feature:
  • Can you avoid local tools.py files?
  • Can you use agent workflows instead of job workflows?
  • Can you use localhost-only browser access?

Best Practices

Only enable environment variables for features you actively use. Each variable increases your attack surface.
Never enable dangerous variables in production unless absolutely necessary. Use staging environments for testing.
When PRAISONAI_ALLOW_LOCAL_TOOLS=true or PRAISONAI_ALLOW_TEMPLATE_TOOLS=1 is set, ensure your working directory doesn’t contain untrusted tools.py files. This is especially risky for recipes fetched from remote registries.
When PRAISONAI_BROWSER_ALLOW_REMOTE=true, use firewalls and authentication to protect browser endpoints.

Security Advisories

These environment variables address the following security vulnerabilities: CVE IDs: Pending assignment by GitHub Security Advisory system Fixed Versions:
  • praisonai: >=0.0.57
  • praisonaiagents: >=0.0.23
PR #1583 (2026-04-30) extended PRAISONAI_ALLOW_LOCAL_TOOLS enforcement to research/rewrite/expand/recipe tool-loading paths and the HTTP API, and added a CWD-only path constraint as defence-in-depth. No new advisory was filed; the threat model is unchanged from GHSA-g985-wjh9-qxxc.

Guardrails

Content filtering and safety controls

Permissions

Agent permission management system