Sandboxing Agentic Workflows with WASM

Joe Lucas

ShmooCon XX (Final) · Day 2 · Build It

Overview

In his ShmooCon 2025 talk, Joe Lucas tackles a critical and often overlooked security challenge emerging from the rapid adoption of agentic AI workflows: the inherent danger of executing untrusted, Large Language Model (LLM)-generated code. As AI applications increasingly move beyond simple text generation to autonomous code execution and iteration, the risk of security vulnerabilities, data breaches, and system compromise escalates dramatically. Lucas, drawing from his experience bridging the gap between security and developer communities, highlights that many developers, particularly in the scientific and data science fields, are inadvertently introducing significant risks by naively running LLM output directly on application servers.

Watch on YouTube

Visual summary for Sandboxing Agentic Workflows with WASM by Joe Lucas
Visual summary for Sandboxing Agentic Workflows with WASM by Joe Lucas

Key moments

  1. 0:40 Introduction to Joe Lucas and talk topic
  2. 2:00 The problem: AI libraries using exec for plots
  3. 4:00 Defining agentic workflows and their inherent risks
  4. 4:20 Open source security challenges and 'human layer' issues
  5. 6:00 Demonstrating dangerous exec use with Plotly code
  6. 6:50 Visualizing the typical, risky agentic workflow
  7. 7:20 Real-world 'smart data frame' example

Sandboxing Agentic Workflows with WASM

Speakers: Joe Lucas

Conference: ShmooCon

YouTube: https://www.youtube.com/watch?v=-_jUZBMeU5w

Overview

In his ShmooCon 2025 talk, Joe Lucas tackles a critical and often overlooked security challenge emerging from the rapid adoption of agentic AI workflows: the inherent danger of executing untrusted, Large Language Model (LLM)-generated code. As AI applications increasingly move beyond simple text generation to autonomous code execution and iteration, the risk of security vulnerabilities, data breaches, and system compromise escalates dramatically. Lucas, drawing from his experience bridging the gap between security and developer communities, highlights that many developers, particularly in the scientific and data science fields, are inadvertently introducing significant risks by naively running LLM output directly on application servers.

Lucas advocates for a robust, client-side sandboxing approach utilizing WebAssembly (WASM) and Pyodide to mitigate these threats. This strategy shifts the execution of potentially malicious or erroneous code from the application server to the user's browser, leveraging the browser's established security model to provide isolation and control. By demonstrating how Python-based agentic tasks, such as generating data visualizations with Plotly, can be securely executed within a browser environment, Lucas provides a practical and actionable framework for developers and security professionals to build more resilient and trustworthy AI systems. His talk serves as a stark reminder that while AI may seem like a new frontier, many of its security challenges are rooted in long-standing application security principles that must not be forgotten.

Background

▶ Watch: Introduction to Joe Lucas and talk topic (0:40)

The recent explosion in AI capabilities has led to the development of agentic workflows, where AI models like LLMs are not just generating text, but actively producing and executing code to achieve complex goals. Initially, users would manually copy LLM-generated code, run it, and then feed back errors or refinements to the model. However, the allure of full automation quickly led to the concept of the AI application running the code itself, iterating on errors, and only returning the desired output. While powerful, this "agency" introduces a profound security dilemma: executing arbitrary, LLM-generated code directly within the context of the calling application or server is a "terrible idea" if done naively.

Lucas points out that many contemporary AI libraries designed to facilitate agentic flows often fall into this trap, employing insufficient security measures. He illustrates this with examples from popular open-source libraries. One such example shows an attempt to sanitize dangerous Python code using regular expressions – a method Lucas quickly dismisses as easily evadable. This particular regex, ironically, was added by maintainers based on vulnerability reports from Lucas's colleagues, yet it fell short of providing effective remediation. Another snippet reveals a common pattern: taking supposedly Plotly code and directly inserting it into Python's exec() function to generate an image. The core problem, Lucas emphasizes, is the inability to guarantee that LLM-generated code will execute exactly as intended, without any security-relevant side effects. This challenge only escalates with the increasing range of inputs and outputs an agent is expected to handle.

Beyond the technical vulnerabilities, Lucas highlights a more "depressing light bulb moment" regarding open-source security. While the auditable nature of open-source code is a security advantage, it's often undermined by a "human layer" of neglect. Solo or small developer teams frequently lack the necessary security expertise or dedicated focus. Compounding this, a "cultural aversion" to security contributions persists, often prioritizing features over security, leading to degraded user experience arguments, and a lack of incentives for maintainers to accept security-focused pull requests. The canonical open-source answer of "just fork it" is impractical for security professionals who don't want to maintain diverse libraries. Thus, while open-source software retains a better security story than closed-source, the public availability of code does not inherently guarantee its security.

The typical agentic workflow Lucas describes involves a user interacting via natural language, which an LLM translates into a task (e.g., "generate Python"). Another LLM then generates the code, which is executed on the application server, and the user receives the output (e.g., an image). He uses an example of a "smart data frame" that wraps a Pandas data frame, allowing natural language queries like "return the top five countries by GDP." While convenient, these simplistic workflows "fail to adequately account for failure modes in the LLM generated content." These failures can stem from mistakes, malicious users, bad information retrieval, or even inherently "bad" (ineffective or incompetent) models. This "badness" isn't always immediately obvious, and models can suffer from concept drift, where performance degrades over time. Lucas's crucial admonition is to "treat LLM generated code as untrusted," especially when users influence its generation. This often clashes with developers' hopes that AI would reduce work, underscoring the need for significant "scaffold engineering" to make these systems effective, reliable, and secure.

Key Findings

▶ Watch: Defining agentic workflows and their inherent risks (4:00)

Lucas posits that securing agentic AI workflows doesn't require entirely new security paradigms; rather, it demands the diligent application of conventional code sandboxing techniques. The primary finding is that the browser, leveraging WebAssembly (WASM), offers a potent and underutilized sandboxing environment for untrusted, LLM-generated code, particularly for Python-based tasks.

His key findings can be summarized as:

  1. Untrusted Code Requires Robust Isolation: LLM-generated code must be treated as untrusted. For server-side execution, hypervisors like Firecracker or serverless options like AWS Lambda (built on Firecracker) provide the strongest isolation guarantees. Containers, while improved, generally share the host kernel and are not considered a security boundary for untrusted code.
  2. Browser as a Security Boundary: Browsers, despite historical "browser escapes," offer a compelling security contract. They are designed to isolate untrusted code from the host operating system. Critically, moving code execution to the client-side provides "great, dare I say perfect isolation guarantees" between the executing user, the server, and other users. This approach, while a form of "risk transference," is deemed acceptable given users' regular browser updates.
  3. Pyodide Enables Python in the Browser: To execute Python functionality within a browser environment, Lucas identifies Pyodide as the most accessible and effective solution. Pyodide is not WebAssembly itself but acts as a bridge, allowing CPython-like functionality to run in the browser using WASM. It provides a standard runtime, supports the rich scientific Python ecosystem (e.g., Plotly), and simplifies JavaScript/browser integrations, allowing developers to focus on their Python code.
  4. Configurable Browser Security Controls: The browser context, when properly configured, offers a suite of security controls for the WASM sandbox:
  • Browser Context Isolation: Strict permissions, disabling JavaScript outside WASM, preventing Content Security Policy (CSP) bypasses, and restricting access to browser APIs.
  • Network Controls: Request interception to prevent unauthorized network access and allow-listing specific domains.
  • Resource Limitation: Memory limits and timeouts to prevent resource exhaustion attacks.
  • Input/Output Validation: Conventional validation, particularly for LLM-generated content, with hooks for retries.
  • Error Handling and Monitoring: Visibility into sandbox activity for both security and development teams.
  1. Efficiency and Privacy Benefits: Client-side execution leverages the user's compute resources, reducing server load and costs. Furthermore, it enables the use of user data directly on their device, offering a significant privacy advantage by eliminating the need to transmit sensitive information to the server.

In essence, Lucas's central finding is that by embracing WASM and Pyodide, developers can transform the browser into a powerful, secure, and efficient sandbox for agentic AI workflows, effectively addressing the inherent risks of executing untrusted code.

Technical Deep Dive

▶ Watch: Open source security challenges and 'human layer' issues (4:20)

The core of Joe Lucas's proposed solution lies in leveraging WebAssembly (WASM) and Pyodide to execute untrusted, LLM-generated Python code directly within the user's browser. This technical architecture fundamentally shifts the security boundary from the application server to the client.

WebAssembly (WASM) is a binary instruction format for stack-based virtual machines. It serves as a compilation target for various high-level languages, originally C and C++, but now widely supporting Rust, Go, and others. WASM is designed for high-performance execution in web browsers, enabling near-native speed for complex computations. Its sandboxed nature within the browser environment is crucial for security, as it provides a clear isolation layer from the host operating system.

Pyodide is presented as the "easy button" for bringing Python functionality to the browser via WASM. It's important to clarify that Pyodide is not WASM itself, but rather a CPython distribution compiled to WebAssembly. This allows developers to run standard Python code, including popular scientific libraries, directly in the browser. Key technical advantages of Pyodide include:

  • Standard CPython Runtime: Pyodide provides a familiar Python environment, minimizing the need for prompt engineers to generate "weird WebAssembly instructions." LLMs are generally proficient at generating standard CPython, making integration smoother.
  • Scientific Python Ecosystem: Built for the scientific community, Pyodide includes many essential data science libraries baked in, with the capability to bring in additional dependencies via Python wheels.
  • Browser Integration: It handles the complex interactions with JavaScript and browser APIs, abstracting away the difficulties of running Python in a web context.

The implementation involves several steps within the web application:

  1. Pyodide Initialization: The first step is to initialize Pyodide in the client-side JavaScript. This involves loading the Pyodide runtime and potentially installing necessary Python dependencies (e.g., Plotly) from wheels over the wire. Lucas noted that this initialization can incur a latency hit (e.g., 8 seconds in his demo), but subsequent code execution is very fast.
  1. Code Templating and Manipulation: The LLM-generated code might require minor adjustments before execution. This could involve adding necessary import statements or performing string manipulation to ensure the code is ready for the Pyodide environment. The goal is to prepare the code for execution and define what object should be returned.
  1. Execution and Output: The prepared Python code is then executed within the Pyodide instance. The result, such as an SVG string for a Plotly graph, is returned to the JavaScript context for display in the web page.

Configurable Security Controls within the Browser Sandbox:

The beauty of this approach lies in the inherent security model of the browser, which can be further hardened:

  • Browser Context Isolation: The browser enforces strict isolation. Developers can configure permissions to disable JavaScript execution outside of WASM, restrict access to sensitive browser APIs (e.g., localStorage, navigator), and implement a robust Content Security Policy (CSP) to prevent bypasses and unauthorized resource loading.
  • Network Controls: Client-side network requests originating from WASM can be intercepted. This allows for implementing allow-lists for specific domains, preventing the LLM-generated code from making unauthorized external connections or exfiltrating data.
  • Resource Limitation: The browser environment can be configured with memory limits and execution timeouts for the WASM module. This prevents resource exhaustion attacks or infinite loops from crashing the user's browser.
  • Input/Output Validation: Even though execution is client-side, conventional input and output validation are still crucial. Filters can be applied to the LLM-generated code before execution and to its output, providing a hook for kicking off retries if validation fails.
  • Error Handling and Monitoring: Implementing robust client-side error handling and logging provides visibility into what's happening within the sandbox, which is valuable for both security incident response and development debugging.

While Pyodide is convenient, Lucas notes that for "really paranoid" use cases, a much more restrictive, custom WASM runtime could be shipped, albeit with increased complexity. The key is that many of these controls are "free" with the browser sandbox model, requiring only appropriate configuration.

Demo / Proof of Concept

▶ Watch: Visualizing the typical, risky agentic workflow (6:50)

Joe Lucas illustrated his proposed sandboxing mechanism with a live demonstration, showcasing the practical application of Pyodide and WebAssembly in securing agentic workflows. The demo focused on the common use case of generating data visualizations (e.g., Plotly graphs) from LLM-generated Python code.

The demonstration began with a web page featuring a blue box displaying metrics. This box indicated that Pyodide initialization took approximately 8 seconds. This initial loading time is the primary latency hit for the client-side execution model, as it involves fetching the Pyodide runtime and any necessary Python wheels (like Plotly) over the network.

Below this metrics box, there was a text area mocking the output of an LLM. Lucas explicitly stated he was "mocking LLMs because I like mocking LLMs and I didn't trust them with my live demo." This text area contained a Python code snippet designed to generate a Plotly graph. The code would include data definitions (e.g., lists of countries, GDPs, happiness indexes) and Plotly commands to create a specific visualization.

An "Execute" button was the central interactive element. When clicked, this button triggered the execution of the Python code from the text area within the Pyodide WebAssembly sandbox running in the user's browser. The crucial aspect of the demo was the speed of subsequent execution time, which was "really, really good" once Pyodide was initialized. This highlighted that while the initial setup has overhead, the actual computation within the WASM sandbox is highly efficient.

The output of the execution was a dynamically generated plot (an SVG image) displayed directly on the web page. If the LLM (or the mocked LLM in the demo) were to generate different code, clicking "Execute" again would run the new code and update the plot, all without involving the server in the execution process.

Lucas then dropped into developer mode to provide a deeper look at what was happening under the hood. He showed:

  • The HTML script responsible for initializing Pyodide and running the Python code, as conceptually outlined in the technical deep dive.
  • The network traffic, revealing that Python wheels (pre-compiled packages) were being sent "over the wire" to the browser.
  • The underlying WebAssembly (WASM) binary, which represented the compiled Python environment and code. He briefly mentioned that a decompiler exists for WASM for "the brave among you," but he hadn't found it necessary. This visual confirmation underscored that the code was indeed running in a low-level, sandboxed binary format.

The demo effectively showcased the key benefits of this approach:

  1. Isolation: The application server remained isolated from the untrusted code.
  2. Multi-tenancy: Users were protected from each other, as their code executed in their respective browser sandboxes.
  3. User Compute: The user's device was leveraged for computation, offloading resources from the server.
  4. Privacy: The potential for user data to remain on their device, without being shipped to the server, was highlighted as a significant privacy win.

While acknowledging some downsides, such as the initial loading latency and challenges with server-side agentic loops, the demo provided compelling proof of concept for the viability and advantages of client-side WASM sandboxing for AI-generated code.

Defensive Implications

▶ Watch: Real-world 'smart data frame' example (7:20)

Joe Lucas's talk provides critical defensive implications for organizations grappling with the security of agentic AI workflows. The overarching message for defenders is to treat all LLM-generated code as untrusted, regardless of its source or apparent simplicity. This fundamental shift in mindset is crucial for building secure AI systems.

Here are the key defensive actions and considerations:

  1. Adopt Client-Side Sandboxing with WASM/Pyodide: Where feasible, migrate the execution of LLM-generated code from server-side to client-side using WebAssembly (WASM) and Pyodide. This strategy immediately transfers the risk of arbitrary code execution from your controlled server environment to the user's browser, leveraging the browser's robust, albeit not perfect, security model. This is especially potent for tasks like data visualization or client-side data analysis where the output is directly for the user.
  2. Rigorously Configure Browser Security Controls: The effectiveness of client-side sandboxing hinges on proper browser configuration. Defenders should ensure:
  • Strict Permissions: Disable JavaScript execution outside of the WASM module, restrict access to sensitive browser APIs (e.g., fetch, localStorage, geolocation), and minimize the attack surface.
  • Content Security Policy (CSP): Implement a tight CSP to prevent unwanted resource loading, script injection, and data exfiltration attempts from the client-side.
  • Network Controls: Utilize browser features or proxy mechanisms to intercept and control network requests originating from the WASM sandbox. Implement an allow-list for specific domains that the code is permitted to interact with, preventing unauthorized external communication.
  1. Implement Resource Limitations: Configure memory limits and execution timeouts for WASM modules. This prevents malicious or buggy LLM-generated code from performing resource exhaustion attacks that could degrade the user's browser performance or crash their system.
  2. Enforce Robust Input and Output Validation: Even with client-side execution, validation remains essential.
  • Pre-execution Validation: Apply filters and validation rules to the LLM-generated code before it enters the WASM sandbox. While regex is insufficient for full security, basic checks can catch obvious malformations or prompt injection attempts.
  • Post-execution Validation: Validate the output of the WASM execution (e.g., ensuring an SVG is well-formed). This can provide a hook for triggering retries if the output is unexpected or malicious.
  1. Monitor and Log Client-Side Activity: Implement mechanisms for logging errors, warnings, and unusual behavior from the WASM sandbox. This provides crucial visibility into potential security incidents or performance issues, aiding both security teams and developers.
  2. Consider Headless Browsers for Server-Side Agentic Loops: For agentic workflows that require server-side iteration or interaction with other models (e.g., a vision model analyzing a generated graph), headless browsers running WASM can offer a compromise. This still allows for sandboxed execution of the LLM-generated code while enabling server-side orchestration, though it reintroduces server-side compute costs.
  3. Address Data Management Challenges: If the data required for LLM-generated code execution does not reside client-side, it must be embedded directly into the LLM's output (e.g., massive arrays in the code). This increases the size of the LLM's response and provides more opportunities for hallucinations or data manipulation within the generated code. Defenders should evaluate the privacy and integrity implications of this data transfer.
  4. Educate Developers and Advocate for Security-First Design: Lucas emphasized the "ugly human layer" in open-source security and developers' tendency to prioritize features over security. Defenders must proactively engage with development teams, educating them on the inherent risks of LLM-generated code and advocating for security to be integrated early in the design and development of AI systems. Highlighting the benefits of client-side execution (privacy, cost savings) can help sway adoption.
  5. Keep Dependency Stacks Trim: For Pyodide-based solutions, a large number of dependencies (wheels) can significantly increase initial loading latency. Defenders should advise development teams to minimize the dependency stack to improve performance and reduce the attack surface.

By implementing these defensive strategies, organizations can significantly reduce their exposure to the novel and evolving threats posed by the integration of untrusted, LLM-generated code into their applications.

Key Takeaways

  • Treat LLM-Generated Code as Inherently Untrusted: All code produced by Large Language Models, especially when user-influenced, must be considered untrusted and executed within robust isolation boundaries to prevent security vulnerabilities and system compromise.
  • Leverage Conventional Security Principles for AI: Many "new" AI security challenges are re-iterations of classic application security problems. Developers and security professionals should apply well-established sandboxing, validation, and isolation techniques to AI workflows rather than seeking entirely novel solutions.
  • WebAssembly (WASM) and Pyodide Enable Secure Client-Side Execution: WASM, specifically through Pyodide for Python, offers a powerful mechanism to run complex, untrusted code directly in the user's browser. This shifts execution risk from application servers, leverages user compute, and enhances data privacy.
  • Browser Sandbox Provides Robust Isolation: The browser's inherent security model, when properly configured with strict permissions, network controls (e.g., request interception, allow-lists), and resource limitations, provides a strong and cost-effective sandbox for LLM-generated code.
  • Security is a Cultural and Developmental Challenge: Beyond technical solutions, addressing the "ugly human layer" of open-source security—including developer education, cultural aversion to security, and lack of incentives—is crucial for widespread adoption of secure AI development practices.
  • Balance Security, Performance, and User Experience: While client-side execution offers significant security benefits, initial loading latency (e.g., Pyodide initialization) and complex dependency management require careful consideration to maintain a positive user experience.

About the Speaker(s)

Joe Lucas is a security professional who became acutely aware of the power and potential of WebAssembly (WASM) during JupiterCon two years prior to his ShmooCon talk. Initially attending JupiterCon to educate data scientists on how to protect their research from integrity modifications or secret theft, he inadvertently discovered the broader capabilities of WASM, particularly through a demonstration of JupyterLite, a Jupyter distribution that runs in the browser.

Lucas is not an expert in WASM but approaches the topic from a practical security perspective, having tinkered with JupyterLite deployments himself. His experience at JupiterCon, where his security talk was reportedly the first and only one ever presented, highlighted a significant gap in security awareness within the scientific and developer communities. He observed attendees' surprise at basic security concepts, underscoring his belief that "for the most part for our target audience for people who will really move the needle on cyber security they're not here [at hacker conferences]... they're at PyCon or cppcon or whatever." Lucas advocates for security researchers to submit talks to developer conferences to influence those who are building the software and may lack access to dedicated security teams. His background reflects a commitment to bridging the knowledge divide between security practitioners and the broader developer ecosystem.

Reviews

Dr. Zero (Offensive Security Researcher) — STRONG ACCEPT

This session provides a brutally honest and technically sound approach to securing agentic AI workflows, a problem rife with naive implementations and marketing fluff. The speaker correctly identifies that many 'AI security' issues are just rehashed application security problems, and proposes a practical, client-side sandboxing solution using WebAssembly and Pyodide. While the core sandboxing concepts aren't novel, their application to the emerging challenge of executing untrusted LLM-generated code is both timely and highly impactful, offering concrete architectural guidance for developers grappling with this issue.

Heather Calloway (CISO) — STRONG ACCEPT

This talk by Joe Lucas powerfully addresses the burgeoning risk of executing untrusted, LLM-generated code in agentic AI workflows. He correctly identifies that many developers are making a "terrible idea" a reality by naively running this code server-side. His proposed solution, leveraging WebAssembly (WASM) and Pyodide for client-side sandboxing, is a pragmatic and actionable approach that shifts execution risk, enhances privacy, and provides a clear path for securing these emerging systems. It’s a vital reminder that conventional security principles remain paramount even in new technological frontiers, demanding clear risk ownership and a cultural shift in development practices.

→ Top-rated talks at ShmooCon XX (Final)

All talks from ShmooCon XX (Final)