Skip to main content

subprocess Wrapper

FieldValue
Document IDASCEND-SDK-014
Version2026.04
Last UpdatedApril 2026
AuthorAscend Engineering Team
PublisherOW-KAI Technologies Inc.
ClassificationEnterprise Client Documentation
ComplianceSOC 2 CC6.1/CC6.2, PCI-DSS 7.1/8.3, HIPAA 164.312, NIST 800-53 AC-2/SI-4

Governed subprocess execution for AI agents.

Overview

The ASCEND subprocess wrapper provides a drop-in replacement for Python's subprocess module that automatically governs shell command execution through the ASCEND platform.

danger

The subprocess wrapper governs shell command execution. Never use Python's native subprocess module in AI agent code -- always use the ASCEND wrapper to ensure all commands are evaluated against governance policies.

Installation

from ascend.wrappers import subprocess

Features

  • Drop-in replacement - Same API as stdlib subprocess
  • Shell=True blocked by default - Prevents shell injection attacks
  • Local risk classification - Critical/high/medium/low patterns
  • Governance integration - Commands submitted to ASCEND for approval
  • Auto-approve safe commands - Low-risk commands approved locally
  • Configurable fail modes - Choose fail-closed or fail-open behavior
  • Decorator support - Add governance to existing functions

Usage

Basic Usage

from ascend.wrappers import subprocess

# Same API as stdlib - but governed!
result = subprocess.run(['ls', '-la'], capture_output=True)

# Shell commands require explicit approval
# This will raise ShellNotAllowedError by default:
subprocess.run('echo hello', shell=True) # BLOCKED

With Governance Client

from ascend import AscendClient
from ascend.wrappers import subprocess
from ascend.wrappers.subprocess import configure

# Configure with ASCEND client
client = AscendClient(api_key="your_key")
configure(client=client, fail_mode='closed')

# Now all risky commands go through ASCEND approval
result = subprocess.run(['rm', '-rf', '/tmp/cache']) # Requires approval

Decorator Pattern

from ascend.wrappers.subprocess import govern_subprocess

@govern_subprocess(risk_level="high")
def deploy():
subprocess.run(["./deploy.sh"])

Configuration

from ascend.wrappers.subprocess import configure

configure(
client=ascend_client, # AscendClient instance
fail_mode='closed', # 'closed' (default) or 'open'
allow_shell=False, # Block shell=True by default
auto_approve_safe=True, # Auto-approve low-risk commands
blocked_commands=['rm'], # Additional commands to block locally
timeout=5 # Governance check timeout in seconds
)

Risk Classification

Commands are classified into four risk levels based on pattern matching:

Critical Risk

PatternDescription
rm -rf /Recursive delete from root
dd if=...of=/dev/Direct disk write
mkfs.Filesystem format
curl | bashPipe to shell (RCE)
wget | shPipe to shell (RCE)
chmod 777 /Wide open permissions from root
Fork bomb patternResource exhaustion

High Risk

PatternDescription
sudoPrivilege escalation
su -Switch user
chmod [0-7]*7[0-7]*World-writable permissions
chown on root pathsOwnership change from root
iptablesFirewall modification
systemctl stop/disableService disruption
kill -9Force kill
nc -e/-lNetcat listener

Medium Risk

PatternDescription
chmodPermission change
chownOwnership change
mount/umountFilesystem mount
killProcess kill
crontabCron modification
ssh/scp/rsyncRemote operations

Low Risk (Auto-approved)

PatternDescription
lsList files
catView file
grepSearch
echoEcho
pwdCurrent directory
whoamiCurrent user
dateDate
unameSystem info

API Reference

Functions

# Drop-in replacements (same API as stdlib)
run(args, *, stdin=None, input=None, stdout=None, ...)
call(args, *, stdin=None, stdout=None, ...)
check_call(args, *, stdin=None, stdout=None, ...)
check_output(args, *, stdin=None, ...)
Popen(args, **kwargs) # Class

# Governance utilities
configure(client=None, fail_mode=None, ...)
governed_run(args, **kwargs)
classify_command_risk(command) -> str # Returns: critical/high/medium/low

Exceptions

from ascend.wrappers.subprocess import (
GovernanceError, # Base exception for governance failures
ShellNotAllowedError # Raised when shell=True is used but not allowed
)

Error Handling

from ascend.wrappers.subprocess import GovernanceError, ShellNotAllowedError

try:
result = subprocess.run(['rm', '-rf', '/'])
except GovernanceError as e:
print(f"Command denied: {e}")
except ShellNotAllowedError as e:
print(f"Shell not allowed: {e}")

Environment Variables

VariableDefaultDescription
ASCEND_SUBPROCESS_FAIL_MODEclosedFail mode when API unavailable
ASCEND_SUBPROCESS_ALLOW_SHELLfalseAllow shell=True commands
ASCEND_SUBPROCESS_AUTO_APPROVE_LOWtrueAuto-approve low-risk commands

Governance Flow

subprocess.run(['rm', '-rf', '/tmp/cache'])


┌─────────────────────────┐
│ 1. Check shell=True │ → ShellNotAllowedError if shell and not allowed
└─────────────────────────┘


┌─────────────────────────┐
│ 2. Classify risk │ → critical/high/medium/low
└─────────────────────────┘


┌─────────────────────────┐
│ 3. Check local blocklist│ → GovernanceError if blocked
└─────────────────────────┘


┌─────────────────────────┐
│ 4. Low risk + auto? │ → Auto-approve, skip backend
└─────────────────────────┘
│ No

┌─────────────────────────┐
│ 5. Submit to ASCEND │ → action_type="system.subprocess"
└─────────────────────────┘


Approved? ─── No ──→ GovernanceError

Yes


Execute & Return

Re-exported Constants

The wrapper re-exports stdlib subprocess constants:

from ascend.wrappers import subprocess

subprocess.PIPE # Pipe constant
subprocess.STDOUT # Stdout constant
subprocess.DEVNULL # Devnull constant
subprocess.CalledProcessError
subprocess.TimeoutExpired
subprocess.CompletedProcess

Compliance

  • CWE-78: OS Command Injection
  • CWE-77: Command Injection
  • MITRE ATT&CK: T1059.004 (Unix Shell)
  • NIST 800-53: SI-10 (Information Input Validation)

Example: Safe File Operations

from ascend.wrappers import subprocess

# Safe: List directory contents (auto-approved)
result = subprocess.run(['ls', '-la', '/tmp'], capture_output=True)
print(result.stdout)

# Safe: Copy file (low risk, auto-approved)
subprocess.run(['cp', 'source.txt', 'dest.txt'])

# Governed: Delete file (requires ASCEND approval)
subprocess.run(['rm', 'important_file.txt'])

# Blocked: Recursive delete (critical risk)
try:
subprocess.run(['rm', '-rf', '/']) # GovernanceError
except subprocess.GovernanceError as e:
print(f"Blocked: {e}")

Integration with Existing Code

Replace your imports to enable governance:

# Before
import subprocess

# After
from ascend.wrappers import subprocess

# All existing code works unchanged!
# Low-risk commands auto-approved, high-risk go through governance