Security Considerations#

Scope: WayFlow Agents, Flows, Steps, or Tools in any environment (development, staging, production).

Why it matters: WayFlow integrates LLMs, Python, tools, and APIs. Its attack surface spans all Steps, Agents, Flows, Tools, and Context Providers. A single untrusted component (e.g., an unsafe tool or poorly-scoped ContextProvider) can compromise the entire runtime.

Considerations regarding tools#

WayFlow allows LLMs to interact with applications via Tools (ClientTool, ServerTool, or RemoteTool), granting access to state or operations. Since tool inputs often come from LLMs or users, rigorous input sanitization and validation are crucial.

Key Principles for Tool Security:

  • Mandatory Input Validation: Always validate tool inputs.

    • For ClientTool and ServerTool, use input_descriptors to define/enforce schemas (types and descriptions) as a primary defense. Note that input_descriptors do not support constraints like ranges or max lengths, so you must implement additional validation in your tool’s code.

    • For RemoteTool, which constructs HTTP requests, validation is critical for all parameters that can be templated (e.g., url, method, json_body, data, params, headers, cookies). While input_descriptors can define the schema for arguments passed to the RemoteTool, the core security lies in controlling how these arguments are used to form the request and in features like url_allow_list.

  • Output Scrutiny: Define expected outputs with output_descriptors. For ClientTool, clients post results; for ServerTool, the server func generates them. Calling Flows/Agents must treat incoming ToolResult content as untrusted until validated/sanitized.

  • Least Privilege: Grant tools only permissions essential for their function.

Tool Security Specifics#

Important

Rigorously sanitize all tool inputs. Tools, bridging LLM understanding with system operations, are prime targets. Validate types, lengths, and semantic correctness before core logic execution.

`ServerTool` Considerations:

  • Callable Security (`func`): The func callable in ServerTool is the primary security concern. Harden this server-executed code against vulnerabilities.

  • Isolation for High-Risk Tools: Run high-risk ServerTool instances (e.g., with elevated permissions, network/filesystem access) in sandboxed environments (containers/pods) with minimal IAM roles. Deny network/filesystem writes unless essential.

  • Tools from `Flows` or `Steps`:

    • When creating a ServerTool via Flow.from_flow(), or Flow.from_step(), its security is inherited. Ensure source Flow or Step tools are secure.

`ClientTool` Considerations:

  • Client-Side Execution: ClientTool execution occurs on the client. Client environment security, though outside WayFlow’s control, impacts overall application security.

  • Untrusted `ToolRequest`: Clients receive a ToolRequest. Though WayFlow generates name and tool_request_id, client code must parse args with a strict schema, avoiding direct use in shell commands or sensitive OS functions.

  • Untrusted Client `ToolResult`: Server-side WayFlow components must treat ToolResult from clients as untrusted. Validate its content before processing.

`RemoteTool` Considerations:

  • Templated Request Arguments: RemoteTool allows various parts of the HTTP request (URL, method, body, headers, etc.) to be templated using Jinja. This is powerful but introduces risks if the inputs to these templates are not strictly controlled. Maliciously crafted inputs could lead to information leakage (e.g., exposing sensitive data in URLs or headers) or enable attacks like SSRF (Server-Side Request Forgery) or automated DDoS.

  • URL Allow List (`url_allow_list`): This is a critical security feature. Always define a url_allow_list to restrict the tool to a predefined set of allowed URLs or URL patterns. This significantly mitigates the risk of the tool being used to make requests to unintended or malicious endpoints. Refer to the API documentation for detailed matching rules.

  • Secure Connections (`allow_insecure_http`): By default, RemoteTool disallows non-HTTPS URLs (allow_insecure_http=False). Maintain this default unless there’s an explicit, well-justified reason to allow insecure HTTP, and ensure the risks are understood.

  • Credential Handling (`allow_credentials`): By default (allow_credentials=True), URLs can contain credentials (e.g., https://user:pass@example.com). If your use case does not require this, set allow_credentials=False to prevent accidental leakage or misuse of credentials in URLs.

  • URL Fragments (`allow_fragments`): Control whether URL fragments (e.g., #section) are permitted in requested URLs and allow list entries. Default is True. Set to False if fragments are not needed and could introduce ambiguity or bypass attempts.

  • Output Parsing (`output_jq_query`): If using output_jq_query to parse JSON responses, be aware that complex queries on very large or maliciously structured JSON could consume significant resources. While primarily a performance concern, extreme cases might have denial-of-service implications.

Caution

As highlighted in the RemoteTool API, since the Agent can generate arguments (url, method, json_body, data, params, headers, cookies) or parts of these arguments in the respective Jinja templates, this can impose a security risk of information leakage and enable specific attack vectors like automated DDOS attacks. Please use RemoteTool responsibly and ensure that only valid URLs can be given as arguments or that no sensitive information is used for any of these arguments by the agent. Utilize url_allow_list, allow_credentials, and allow_fragments to control URL validity.

Harden All Tools (ServerTool, ClientTool and RemoteTool)#

Tool Hardening Guidelines#

Risk

Guidance

Unvalidated arguments (leading to injection, DoS, etc.)

Primary Defense:

  • For ClientTool & ServerTool: Use input_descriptors for basic type checking. In ServerTool’s func, add comprehensive validation (string length limits, numeric ranges, format constraints). Cap string lengths and numeric ranges in tool implementation code.

  • For RemoteTool:

    • Rigorously validate and sanitize any inputs used in templated arguments (url, method, json_body, data, params, headers, cookies).

    • Crucially, always configure `url_allow_list` to restrict outbound requests to known, trusted endpoints.

    • Leverage allow_insecure_http=False (default), and consider setting allow_credentials=False if not needed.

Excessive privileges (for ServerTool)

Run in least-privilege containers/pods.

Separate network namespaces for sensitive data/external system access.

Explicitly deny unnecessary filesystem/network access.

Stateful tools

Prefer stateless tools. If stateful, use hardened datastores (e.g., Oracle Database) over in-memory objects where feasible.

Implement optimistic locking and rigorous input sanitization for state-modifying operations.

ClientTool misuse (client-side vulnerabilities)

Client apps handling ToolRequest must treat args as untrusted.

Validate/sanitize client-side args before local execution (esp. OS commands, sensitive API calls).

Insecure underlying components (for tools from Flows, Steps)

Ensure source Flows or Steps tools for WayFlow ServerTool are vetted; the tool inherits their security.

Data leakage via ToolResult

Define output_descriptors clearly.

Ensure ServerTool func and ClientTool client code return only necessary data.

Consuming Agent/Flow must validate/sanitize ToolResult content.

Considerations regarding network communication#

WayFlow components use the network for LLM communications, ApiCallStep tool operations, and third-party integrations. Implement robust network security.

Important

Adopt a defense-in-depth network security strategy for WayFlow, including supply-chain security for models/containers, network segmentation, encrypted communications, and strict egress controls.

Supply-Chain Security for WayFlow Assets#

  • Model Integrity: For third-party models used by WayFlow (fine-tuned, embeddings):
    • Download via HTTPS, pin SHA-256 digests and verify integrity before loading into WayFlow.

  • Container Security: For containerized WayFlow:
    • Use minimal, regularly updated base images. Scan images (e.g., Trivy, Grype) for CVE. Sign (e.g., before pushing to a package index) and verify (e.g., when downloading) images.

Network Segmentation for WayFlow#

Isolate WayFlow components to limit blast radius:

  • Subnet Isolation:
    • LLM hosts (OCI Gen AI, vLLM) in dedicated subnets.

    • Tool backends and ApiCallStep targets in isolated subnets.

    • Keep the WayFlow control-flow logic (e.g., Agents, Flows, BranchingSteps) separate from data processing (e.g., database mutations, data analytics)

  • TLS:
    • Use mTLS for WayFlow service-to-service communications such as connecting to MCP servers, Monitoring/Telemetry services, File Storage Services, Databases, and so on.

Egress Controls for WayFlow Components#

  • Strict Egress Rules: Default-deny outbound traffic:
  • Centralized Allow-Lists for WayFlow:
    • LLM provider endpoints and Telemetry/logging destinations.

    • Approved Tool API endpoints for tools making external calls, such as ApiCallStep and RemoteTool (enforced via its url_allow_list parameter).

    • Make sure to set up alert on policy violations.

Network Connection Requirements#

WayFlow Network Security: Key Implementations#

Area

WayFlow-Specific Implementation

External LLM Traffic Encryption

  • OCI Gen AI: HTTPS by default.

  • vLLM: Front with TLS terminator (Nginx/Caddy) or use mTLS ingress.

  • Verify TLS certs; pin where feasible.

WayFlow Component Egress Rules

  • Container network policies (iptables, Calico, Cilium) allowing only approved LLM endpoints, Tool API destinations, telemetry sinks, and DNS.

External API Call Security (e.g., ApiCallStep, RemoteTool)

  • Enforce HTTPS: Use allow_insecure_http=False (default for both).

  • URL Allow Lists: For RemoteTool, always configure its url_allow_list parameter. For ApiCallStep, ensure broader network-level allow-lists are in place.

  • Validate/sanitize templated URLs and other request parameters (headers, body) derived from potentially untrusted inputs.

  • Consider allow_credentials=False and allow_fragments=False for RemoteTool if those features are not strictly necessary.

  • Use connection timeouts/rate limiting (tool-specific or via infrastructure).

  • Log outbound requests (destination URLs, sanitized headers/bodies) for audit.

Data Residency

  • Ensure LLM providers meet data residency needs.

  • Use regional LLM endpoints for data locality with WayFlow.

Network Monitoring & Alerting

  • Maintain allow-list of approved hostnames and IPs for WayFlow traffic.

  • Alert on policy violations/unauthorized connection attempts from WayFlow components.

Considerations regarding API keys and secrets management#

Secure API key/secret management is critical for WayFlow deployments (LLM providers, external services, internal auth) to prevent unauthorized access and data breaches.

Important

Never embed API keys, passwords, or other secrets directly in code, configuration files, or serialized JSON/YAML. Always use secure injection mechanisms and follow the principle of least privilege for credential access.

Control Secret Sprawl#

Prevent hardcoded secrets in WayFlow:

  • Runtime injection for WayFlow required secrets (e.g., LLM API key or database credentials):

    • Avoid using Kubernetes/Docker secrets to deploy environment variables.

    • Use secrets managers (e.g., OCI Vault).

  • Avoid Baked Secrets in WayFlow:

    • Never put API keys in WayFlow’s serialized JSON/YAML.

    • Exclude secrets from Python source shown to LLMs during WayFlow development.

    • Use placeholders in version-controlled WayFlow configs.

    • Use pre-commit hooks to prevent committing secrets to source control.

  • Per-Flow Service Accounts & Rotation:

    • Use dedicated service accounts per WayFlow Flow.

    • Rotate API keys regularly.

    • Automate key rotation if possible.

    • Monitor/audit credential use.

LLM Provider Authentication Security#

OpenAI Models (`OpenAIModel`):

  • Injecting a Secret as an environment variable makes the value available to everything inside the Pod.

  • We recommend exploring alternatives to sourcing the OPENAI_API_KEY env variable at runtime.

  • Use organization-specific API keys with usage limits.

  • Monitor TokenUsage for anomaly detection.

  • Secure proxy credentials separately for VPN proxies.

OCI GenAI Models (`OCIGenAIModel`):

vLLM Models (`VllmModel`):

  • Use internal DNS names and private networking

  • Implement authentication at reverse proxy level

Credential Rotation and Monitoring#

  • Rotation: Rotate WayFlow credentials regularly.

  • Monitoring: Track WayFlow’s TokenUsage for anomalies.

  • Incident Response: Have procedures to revoke compromised WayFlow credentials.

Considerations regarding Resource-exhaustion vectors#

Resource-exhaustion events may stem from hostile over-use or from innocent implementation mistakes (e.g., unbounded loops, forgotten awaits), so interrupts should be viewed as guardrails against both abuse and developer error. WayFlow provides two soft execution-interrupts, but they don’t cover all vectors. This section details their use, limitations, and additional production hardening techniques.

Built-in execution interrupts#

Class

Purpose / behaviour

SoftTimeoutExecutionInterrupt

Stops the assistant after a configurable wall-clock duration (default 10 min).

  • Soft → it waits until the current Step or FlowExecutionStep returns; it cannot pre-empt a long-running Tool call or LLM generation.

SoftTokenLimitExecutionInterrupt

Aborts execution once an LLM (or group of LLMs) has emitted a specified number of tokens.

  • Soft → the check happens between steps; generation already in flight finishes first.

Usage example#

from wayflowcore.agent import Agent
from wayflowcore.executors.interrupts.timeoutexecutioninterrupt import SoftTimeoutExecutionInterrupt
from wayflowcore.executors.interrupts.tokenlimitexecutioninterrupt import SoftTokenLimitExecutionInterrupt

timeout_int   = SoftTimeoutExecutionInterrupt(timeout=30)          # 30 s max
token_int     = SoftTokenLimitExecutionInterrupt(total_tokens=500) # 500 tokens
status = conversation.execute(execution_interrupts=[timeout_int, token_int])

Gaps in coverage (what is not provided)#

  • Memory limits – no built-in interrupt for resident-set or GPU VRAM.

  • CPU / thread quotas – Python loops can still hog a core until the Step returns.

  • Hard timeouts inside a Tool – a Tool that calls a REST API may block indefinitely.

  • Concurrent-request ceilingsMapStep(parallel_execution=True) fans-out one sub-flow (or Tool call) per element in the input list. Because there is no internal throttle, nothing stops 1000 parallel MapStep forks (possibly leading to CPU thrashing or OOM-kill) without your own semaphore.

  • LLM generation cancellation – once a prompt is sent, the soft timeout waits for the model to answer.

Considerations regarding input validation and sanitation#

WayFlow has no built-in input validation/sanitation. Developers are responsible for securing user inputs via validation, sanitation, and external guardrails.

Important

All user-provided inputs—whether from chat interfaces, API payloads, or Tool arguments—should be treated as potentially malicious and require rigorous validation and sanitation before processing.

Core Input Security Requirements#

Validation and Bounds Checking:

  • Validate length/structure of all inputs to WayFlow (chat, API, Tool arguments).

  • Enforce max length limits on user inputs to WayFlow.

  • Validate formats/types before passing to WayFlow components.

  • Check inputs are within reasonable bounds for the WayFlow application.

  • Use output_descriptors on PromptExecutionStep for JSON schemas to reduce prompt-injection leakage.

External Guardrails Integration:

WayFlow applications should leverage external security services to provide comprehensive input protection:

  • OCI Gen AI Inference Protection: Built-in guardrails (toxicity, prompt-injection, jailbreak scanning) per Responsible AI.

  • Custom filtering for WayFlow: Use regex/policy filters with open-source models in WayFlow.

  • LLM DevSecOps: Apply LLM DevSecOps guide practices to WayFlow development.

Architectural Patterns for Input Security:

  • Early filtering: Insert a BranchingStep at Flow start to route “unsafe” content to a refuse/retry branch.

  • Defense in depth: Layer multiple validation/sanitation for WayFlow inputs.

  • Fail-safe defaults: Reject ambiguous/malformed inputs to WayFlow.

Considerations regarding logging#

Error handling & exception hygiene#

Robust error handling is key for WayFlow stability and security.

  • Custom WayFlow Steps: Implement comprehensive error handling. Map exceptions to sanitized messages, avoiding internal detail leakage.

  • `CatchExceptionStep`: Configure to prevent stack trace/sensitive debug info leakage in user errors.

  • Private Debug Logging: Log full exception traces for WayFlow errors privately for debugging, while sanitizing user-facing messages.

Audit & observability#

Comprehensive auditing/observability is vital for WayFlow.

  • Structured Logs for WayFlow Events: Log key WayFlow operations at INFO level:

    • conversation_id: Trace interaction lifecycle.

    • step_name: Executed WayFlow Step.

    • input_hash: Hash of Step/Tool input (avoid raw sensitive input).

    • output_hash: Hash of Step/Tool output.

    • tool_name: Called Tool.

  • Tamper-Evident Log Storage for WayFlow: Use secure, append-only storage for WayFlow logs.

  • PII Scrubbing in WayFlow Logs: Mask/redact/tokenize PII in WayFlow logs/traces before persistence for privacy compliance (GDPR, right to be forgotten). It may be beneficial to use frameworks for detecting, redacting, masking, and anonymizing sensitive data (PII) such as Presidio

Considerations regarding Telemetry & observability#

WayFlow can emit rich execution traces—Spans—that describe every tool call, LLM invocation, and Step transition inside an Agent or Flow. These traces are invaluable for debugging and performance tuning, but they may also contain sensitive data.

1. What’s in a Span?#

  • Flow / Agent configuration snapshot

  • Sequence of executed Steps (timestamps, duration)

  • Input / output payloads, including user prompts and Tool arguments

  • Status transitions and exception details

2. Default behaviour#

WayFlow sends no traces by default. To enable exporting you must supply a custom SpanExporter.

Danger

Raw Spans can expose PII, secrets, or proprietary model prompts. PII/secrets must be removed/hashed from logs/metrics.

3. Implementing a SpanExporter#

from wayflowcore.tracing.spanexporter import SpanExporter
from wayflowcore.tracing.span import Span
import asyncio, aiohttp

class AsyncHTTPSExporter(SpanExporter):
    async def _send(self, span: Span) -> None:
        payload = sanitize(span)           # e.g., redact secrets here
        async with aiohttp.ClientSession() as s:
            await s.post("https://trace.local/ingest", json=payload, timeout=3)

    def export(self, span: Span) -> None:
        asyncio.create_task(self._send(span))   # non-blocking

Key points:

  • Async / non-blocking - keep the exporter off the critical path.

  • Robust error handling - never raise from export; drop or queue on failure.

  • Back-pressure - apply rate limits or batch Spans to avoid DoS on the collector.

4. SpanProcessor guidance#

When you add a custom SpanProcessor:

  • Apply sampling early to reduce volume.

  • Use bounded queues to cap memory.

  • Log (INFO) when Spans are dropped due to back-pressure.

Considerations regarding UI rendering#

WayFlow uses Jinja 2 for templates in core components like:

Jinja 2’s automatic HTML-escaping is disabled by design in WayFlow to:

  • deliver prompt text to LLMs without foreign escape sequences, and

  • prevent mangling of deliberately model-generated HTML tags.

This preserves model fidelity/latency but removes default XSS/code-injection safeguards.

Important

Never render the raw output of a WayFlow step directly to a browser. Treat every step.<OUTPUT> or Message.content string as untrusted and sanitise or escape it before display. Failure to do so can lead to HTML/JS injection (stored XSS) where malicious scripts sent in chat can be executed by the browser if auto-escaping is off.

Extended API-specific guidance#

  • `OutputMessageStep`

    This class returns strings and performs no escaping. When its output is propagated to a web front-end (often via DataFlowEdge OutputMessageStep.OUTPUT), apply HTML escaping there.

  • `MessageList` / `ChatHistoryContextProvider`

    Chat histories can contain user HTML. If displaying transcripts, pipe the history through the same sanitiser used for new LLM outputs.

  • `ServerTool` results

    ServerTool instances may return Markdown or HTML. Mark the result type explicitly (e.g., text/plain vs text/markdown) and render with a Markdown library that sanitises HTML by default (e.g., markdown-it-py with linkify=False).

Considerations regarding assistant/flow serialization#

WayFlow components inheriting from SerializableObject can be converted to/from a dictionary representation (typically YAML) using serialize.

Serialized JSON of assistants/flows are computational programs. Executing deserialized objects is like executing code. Ensuring integrity/authenticity before deserialization is key to prevent Arbitrary Code Execution (ACE).

Secure Deserialization Practices#

Deserializing data from untrusted sources is inherently risky. Always treat serialized WayFlow objects from external or unverified origins as potentially malicious.

  1. Verify Data Integrity and Authenticity: Before attempting to deserialize, ensure the JSON/YAML data comes from a trusted source and has not been tampered with. Implement mechanisms such as digital signatures to verify the origin and integrity of the serialized data.

  2. Choose JSON over YAML: When serializing data, JSON if preferred over YAML as YAML presents the risk unsafe deserialization allowing remote code execution.

  3. Use Safe Loading if using YAML: The YAML data itself should be parsed safely before passing it to WayFlow’s deserialization functions. Always use yaml.safe_load() from the PyYAML library instead of yaml.load() to prevent the execution of arbitrary Python code embedded within the YAML structure itself.

  4. Controlled Deserialization Context: While autodeserialize accepts a deserialization_context, ensure a reused context doesn’t carry over state from a prior untrusted deserialization that could be exploited.

Example of a safer deserialization pipeline:

from yaml import safe_load # Crucially, use safe_load, not load
from wayflowcore.serialization.serializer import autodeserialize
# Assume verify_signature_and_integrity is a custom function you've implemented
# to check the signature against the raw_yaml_string and a trusted key.
# This step is conceptual and depends on your specific trust model.

raw_yaml_string, signature = get_potentially_untrusted_yaml_and_signature()
is_trusted_source = verify_signature_and_integrity(raw_yaml_string, signature)

if is_trusted_source:
    # First, safely parse the YAML string to a Python object structure
    parsed_yaml_object = safe_load(raw_yaml_string)
    # Then, deserialize the WayFlow object using autodeserialize
    if isinstance(parsed_yaml_object, dict):
        assistant_or_flow = autodeserialize(parsed_yaml_object)

Remember that autodeserialize expects parsed YAML (a Python dictionary), not the raw YAML string, if using safe_load. Passing a string directly to autodeserialize makes it use yaml.safe_load internally. Explicit safe_load first allows pre-validation of the YAML structure.

Other Security Concerns#

For any other security concerns, please submit a GitHub issue.