DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Building Threat Intelligence Pipelines Using Python, APIs, and Elasticsearch
  • Automating Threat Detection Using Python, Kafka, and Real-Time Log Processing
  • Building a Production-Ready MCP Server in Python
  • Data Privacy and Security: A Developer's Guide to Handling Sensitive Data With DuckDB

Trending

  • When RabbitMQ Accepts a Message but Does Not Route It to a Queue
  • Why We Chose Iceberg Over Delta After Evaluating Both at Scale
  • Delegated Transport, Forked Authorization: Where Chat and AI-Agent Integrations Actually Break
  • Two AI Agents, One Database: Solving Concurrent Updates With Optimistic Locking

Draft This article hasn't been published, so readers can't see it.

In moderation This article is waiting for review, so readers can't see it yet.

Deleted This article has been deleted, so readers can't see it.

  1. DZone
  2. Software Design and Architecture
  3. Security
  4. Tracing Dangerous Python Code Paths With rce-path

Tracing Dangerous Python Code Paths With rce-path

I built rce-path, a Python package for tracing potentially untrusted inputs to code and shell execution, preserving static evidence, and adding optional AI review.

By 
Sai Teja Erukude
·
Nov. 10, 26 · Analysis
Post on X (opens in a new tab) Share on LinkedIn (opens in a new tab)
85 Views

Join the DZone community Save articles, join the discussion, and download Refcards and Trend Reports with a free account.

Join for free Already a member? Log in

Finding an eval() call in a Python repository is easy. Understanding where its argument comes from takes more work.

The same problem appears with shell commands. A scanner can point to subprocess.run(), but a reviewer still needs to trace the command through assignments, string formatting, and branches. Then comes the harder question: can someone outside the intended trust boundary control that input?

I built rce-path to make that investigation more structured. It scans a local Python checkout, identifies modeled execution sinks, and records the input flow it can establish from the source. Each finding includes locations, ordered evidence, and the assumptions that still need review.

The package also supports optional OpenAI and Ollama annotations. Static scanning works independently, so you can inspect the evidence before deciding whether to involve a model.

The Problem I Wanted to Address

A suspicious function call is a starting point for security review. It leaves several questions unanswered:

  • Where did the argument originate?
  • Which assignments or expressions carried it to the execution call?
  • Does the path depend on a branch or an unknown value?
  • Is the source request data, a function parameter, or local input?
  • Which parts of the flow remain unresolved?

These details matter because a function parameter is not necessarily remotely controlled. A request-derived value may pass through authentication or validation outside the scanner's view. A shell invocation using fixed literals needs a different investigation from one assembled from request data.

My goal with rce-path is to give the reviewer a traceable path and make the remaining uncertainty visible.

The Core Idea

The current release, version 0.1.0, uses Python's abstract syntax tree and per-scope control-flow analysis to track modeled inputs toward execution sinks.

A source is an input origin the analyzer recognizes. A sink is an operation that can execute Python code or a shell command. Between them, the analyzer follows supported assignments and expressions, including concatenation and f-strings. It also handles branches, early returns, and bounded loop analysis.

The report preserves the steps connecting these operations. If a call's return value cannot be resolved, the scanner records that uncertainty rather than filling the gap with an assumed data flow.

Targets are read as source data. A normal scan does not import the target package, install its dependencies, invoke its build hooks, or execute its code.

What the First Release Covers

The initial sink rules cover:

  • eval() and exec(), including supported built-in aliases.
  • os.system() and os.popen().
  • Shell execution through subprocess.run(), call(), check_call(), check_output(), and Popen().
  • subprocess.getoutput() and getstatusoutput(), which imply shell execution.

Modeled sources include function parameters and Flask request inputs such as request.args, request.form, request.data, and request.get_data(). Environment variables, command-line input, stdin, and file reads are categorized as local inputs.

The subprocess rules account for shell behavior. An unknown shell value introduces an explicit condition. Cases using shell=False have coverage limitations; they should not be interpreted as a general safety guarantee.

This is a focused first release. It lets researchers and Python developers investigate supported execution paths without requiring an AI backend.

A Small Example

Consider this deliberately unsafe example. Save it as demo.py, but do not run it as an application:

Python
 
from flask import request
import subprocess

def lookup():
    host = request.args.get("host", "")
    command = f"nslookup {host}"
    subprocess.run(command, shell=True)


The security question is whether request data reaches a shell command. Here, the analyzer can model the request source, its assignment to host, its inclusion in the f-string, and the assignment to command before the shell invocation.

There is no route registration in this snippet. It illustrates a data-flow pattern; it does not establish that the function is deployed or reachable over a network.

To try rce-path, use Python 3.11 or newer and install the package from PyPI:

PowerShell
 
pip install rce-path


Save the example as demo.py, then generate a Markdown report:

PowerShell
 
rce-path scan demo.py --format markdown --output report.md


For this example, the report identifies the subprocess.shell rule at line 7, with static_path evidence and a network_request source. Its reachability field is network_source_modeled.

The evidence follows the request read on line 5, assignment to host, f-string construction on line 6, assignment to command, and the execution sink on line 7.

Those fields need careful interpretation. network_source_modeled means the analyzer recognized a network-input pattern. Authentication, deployment exposure, and actual exploit feasibility remain questions for human review.

Using the Python API

The command is named rce-path; the Python import is rce_path. The same scan is available through the public API:

Python
 
from rce_path import ScanConfig, scan

if __name__ == "__main__":
    report = scan(
        "demo.py",
        config=ScanConfig(ai_enabled=False),
    )
    print(report.summary)
    for finding in report.findings:
        print(
            finding.rule_id,
            finding.location.file,
            finding.location.line,
        )
        for step in finding.steps:
            print(
                step.kind,
                step.location.line,
                step.description,
            )


The report and findings are typed Pydantic records. You can export them with report.to_json() or report.to_markdown() and use the structured fields in your own review workflow.

Each finding carries a fingerprint, source category, evidence tier, source locations, and review state. Conditions, assumptions, and unresolved calls are available alongside the evidence. The report also records analyzed and skipped files, source content hashes, and coverage diagnostics.

Fingerprints exclude the absolute checkout root and AI annotations, which helps compare the same finding across equivalent checkouts. They are location-based, so moving code can change a fingerprint.

Where AI Review Fits

An LLM can explain a finding, identify follow-up questions, or assess whether the available evidence supports an interpretation. In rce-path, that review is an optional annotation layer.

Both provider implementations use a common evidence-and-review contract. Responses are validated, including references to evidence IDs. AI annotations do not rewrite the static finding or change its fingerprint.

For a local Ollama review, install the extra:

PowerShell
 
pip install "rce-path[ollama]"


Start your Ollama server and choose a model you already have installed:

PowerShell
 
rce-path scan demo.py --ai ollama --model YOUR_MODEL


The default endpoint is http://127.0.0.1:11434. The package does not automatically download a model or fall back to a cloud provider.

For OpenAI, install the corresponding extra and set OPENAI_API_KEY in your environment:

PowerShell
 
pip install "rce-path[openai]"
rce-path scan demo.py --ai openai --model YOUR_MODEL --allow-remote-code


YOUR_MODEL is a placeholder for an operator-selected model that supports the provider's required structured output.

OpenAI and non-loopback Ollama endpoints require explicit permission to transmit code. The package sends bounded evidence slices and applies best-effort redaction of known secret patterns. Redaction can miss sensitive content, so review what you permit it to transmit. OpenAI requests use store=False, which is not a promise of zero retention.

Review failures stay visible. A refused, unavailable, invalid, or budget-exceeded review is reported as unsuccessful rather than being presented as a completed assessment.

Running It in a Review Pipeline

For machine-readable output, scan a local checkout and save JSON:

PowerShell
 
rce-path scan ./checkout --format json --output findings.json


You can also choose a findings policy:

PowerShell
 
rce-path scan ./checkout --fail-on static-path


This fails the selected policy when the report contains a modeled static path. It does not mean the package has confirmed an exploitable vulnerability.

The CLI uses three exit codes:

  1. the scan completed and the selected policy passed.
  2. the findings policy failed.
  3. an operational error, incomplete scan, or failed requested AI review occurred.

The default policy is --fail-on none. Completion status takes precedence over the findings policy, so an incomplete scan cannot be treated as a successful clean scan.

A repository-level rce-path.toml can configure rules, exclusions, and analysis budgets. Repository configuration cannot enable AI or grant permission to transmit code; those decisions require runtime opt-in.

Current Limits

rce-path uses bounded static analysis. Cross-function and cross-file flow, dynamic dispatch, closures, complex heap mutation, comprehensions, and precise exception handling are outside its current coverage.

Branches are joined conservatively. A reported path can include conditions that are not jointly feasible at runtime. An unknown function is not automatically treated as a sanitizer, and a guard does not by itself prove an argument safe.

For that reason, static_path means the analyzer established a path within its models. A sink_only finding identifies a sink whose input flow remains unresolved. Neither establishes remote code execution, and the package assigns no automatic CVSS score or exploitability probability.

Read the coverage diagnostics alongside the findings. An empty completed report means "No modeled findings," not that the repository has been proved secure.

What Comes Next

The roadmap includes bounded local call summaries, additional framework and deserialization models, SARIF output, and separate human-approved validation workflows. These capabilities are planned for later phases and are not implemented in the current release.

For now, I want rce-path to make a specific part of vulnerability review easier to inspect: how an input reaches an execution operation, what evidence supports that path, and what remains unknown.

Start with a small file whose behavior you understand. Read the evidence steps, check the assumptions, and then apply the same workflow to a local repository. That is the most useful way to judge whether the package fits your research or code-review process.

Project Links

  • PyPI
  • GitHub
  • Usage and limitations
Python (language) security

Opinions expressed by DZone contributors are their own.

Related

  • Building Threat Intelligence Pipelines Using Python, APIs, and Elasticsearch
  • Automating Threat Detection Using Python, Kafka, and Real-Time Log Processing
  • Building a Production-Ready MCP Server in Python
  • Data Privacy and Security: A Developer's Guide to Handling Sensitive Data With DuckDB

Partner Resources

×

Comments

Loading comments

The comments didn't load

Please try again in a moment.

Likes

Loading

The next likes didn't load. Please try again.

The likes didn't load

Please try again in a moment.

See who liked this

Log in or join DZone for free to see who liked this article.

Log in Join for free
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook