# What Dangerous Code Patterns Does the Behavioral AST Analyzer Detect?

> Discover dangerous code patterns like exec eval and process spawning detected by the behavioral AST analyzer in NVIDIA SkillSpector. Improve your code security today.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: deep-dive
- Published: 2026-06-18

---

**The Behavioral AST analyzer in NVIDIA SkillSpector detects eight dangerous code patterns ranging from dynamic code execution via `exec()` and `eval()` to process spawning and dangerous execution chains, each tagged with specific severity levels and confidence scores.**

The **Behavioral AST analyzer** ([`behavioral_ast.py`](https://github.com/NVIDIA/SkillSpector/blob/main/behavioral_ast.py)) is a static analysis component of the NVIDIA SkillSpector repository that parses Python source files using the built-in `ast` module. It identifies constructs that enable **dynamic code execution**, **privilege escalation**, or **runtime code generation** by walking the abstract syntax tree and matching call nodes against known dangerous patterns.

## How the Behavioral AST Analyzer Detects Threats

The analyzer processes each file through a multi-stage pipeline defined in [`src/skillspector/nodes/analyzers/behavioral_ast.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/behavioral_ast.py). First, it parses the source into an AST using `ast.parse()`. Then it walks the tree with `ast.walk()` to inspect every `ast.Call` node.

For each call, the `resolve_call_name` helper (imported from [`common.py`](https://github.com/NVIDIA/SkillSpector/blob/main/common.py)) resolves the dotted name (e.g., `subprocess.run`). The analyzer compares these names against internal sets: `_DANGEROUS_BUILTINS`, `_SUBPROCESS_CALLS`, and `_OS_EXEC_CALLS`. When matched, the `_emit` function creates an `AnalyzerFinding` with a specific rule ID, message, severity, confidence score, and the tag **"Dangerous Code Execution"**.

## The 8 Dangerous Code Patterns Detected

The analyzer assigns a unique rule ID (AST1 through AST8) to each pattern, with severities ranging from **LOW** to **CRITICAL**.

### AST1: Direct Use of `exec()`

**Severity:** HIGH | **Confidence:** 0.85

Detected in lines 72-78 of [`behavioral_ast.py`](https://github.com/NVIDIA/SkillSpector/blob/main/behavioral_ast.py), this rule triggers on any direct call to the `exec()` builtin, which enables arbitrary Python code execution from string inputs.

```python
code = "print('hello')"
exec(code)  # Triggers AST1

```

### AST2: Direct Use of `eval()`

**Severity:** HIGH | **Confidence:** 0.85

Lines 78-85 flag calls to `eval()`, which evaluates string expressions as Python code, presenting similar risks to `exec()`.

```python
expr = "2 + 2"
result = eval(expr)  # Triggers AST2

```

### AST3: Dynamic Import via `__import__()`

**Severity:** MEDIUM | **Confidence:** 0.75

Lines 86-88 detect runtime imports using `__import__()`, which can bypass static analysis and load untrusted modules.

```python
module_name = "os"
mod = __import__(module_name)  # Triggers AST3

```

### AST4: Subprocess Module Calls

**Severity:** MEDIUM | **Confidence:** 0.70

Lines 92-96 identify any call prefixed with `subprocess.` (e.g., `subprocess.run`, `subprocess.Popen`, `subprocess.call`), which spawn external processes and may enable shell injection.

```python
import subprocess
subprocess.run(["ls", "-l"])  # Triggers AST4

```

### AST5: OS Exec-Family Calls

**Severity:** HIGH | **Confidence:** 0.85

Lines 97-101 detect calls to `os.system()` or any `os.` exec-family function (`os.execv`, `os.spawn*`, etc.), providing direct shell access.

```python
import os
os.system("rm -rf /tmp/evil")  # Triggers AST5

```

### AST6: Runtime Code Compilation via `compile()`

**Severity:** MEDIUM | **Confidence:** 0.65

Lines 89-92 flag the `compile()` builtin when used to create code objects at runtime, which often precedes dynamic execution.

```python
source = "a = 1"
code_obj = compile(source, "<string>", "exec")  # Triggers AST6

```

### AST7: Dynamic Attribute Access with `getattr()`

**Severity:** LOW | **Confidence:** 0.50

Lines 102-105 detect `getattr()` calls where the attribute name is not a constant string, enabling dynamic method resolution that can bypass access controls.

```python
obj = {}
attr_name = "dynamic_attr"
getattr(obj, attr_name)  # Triggers AST7

```

### AST8: Dangerous Execution Chains

**Severity:** CRITICAL | **Confidence:** 0.95

The most severe detection (lines 71-78) triggers when `exec()` or `eval()` wraps another dangerous source, such as a subprocess call, dynamic import, or network-loaded code. The `_contains_dangerous_source()` helper recursively checks the argument subtree for nested risks.

```python
import subprocess
payload = "import os; os.system('whoami')"
exec(payload)  # Triggers AST8 (execution chain)

```

## Detection Configuration and Metadata

The rule definitions reside at the top of [`behavioral_ast.py`](https://github.com/NVIDIA/SkillSpector/blob/main/behavioral_ast.py):

- **Messages:** Lines 71-80 define human-readable descriptions like "exec() call detected"
- **Severities:** Lines 82-91 map rule IDs to HIGH, MEDIUM, LOW, or CRITICAL
- **Confidences:** Lines 93-101 assign confidence scores (0.50 to 0.95) based on detection reliability

All findings receive the tag **"Dangerous Code Execution"** (lines 104-108), enabling filtered reporting across the SkillSpector pipeline.

## Summary

- The **Behavioral AST analyzer** in [`src/skillspector/nodes/analyzers/behavioral_ast.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/behavioral_ast.py) detects **8 specific dangerous patterns** using Python's `ast` module.
- **Critical execution chains (AST8)** receive the highest severity when `exec()` or `eval()` wrap other dangerous operations.
- **Direct code execution** via `exec()`, `eval()`, and `compile()` are flagged as HIGH severity with 0.85 confidence.
- **Process spawning** via `subprocess.*` and `os.*` exec calls are flagged as MEDIUM to HIGH severity.
- **Dynamic imports** (`__import__`) and **reflection** (`getattr` with variables) are detected with lower confidence but still tagged for review.

## Frequently Asked Questions

### What is the difference between AST1 and AST8 detections?

**AST1** triggers on any direct `exec()` call regardless of its arguments, while **AST8** specifically detects when `exec()` or `eval()` wraps another dangerous source (like a subprocess call or dynamic import). AST8 is rated **CRITICAL** with 0.95 confidence because execution chains represent compounded security risks.

### How does the analyzer resolve complex call names like `subprocess.run`?

The analyzer uses the `resolve_call_name` helper function defined in [`src/skillspector/nodes/analyzers/common.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/common.py) to transform an `ast.Call` node into its fully qualified dotted name. This allows the detection logic to match against prefixes like `subprocess.` and `os.` regardless of how the module was imported.

### Why does the `getattr()` detection (AST7) have lower confidence?

AST7 carries a **LOW** severity and **0.50** confidence because dynamic attribute access via `getattr()` is a common, legitimate Python pattern. The analyzer lowers the confidence to reduce false positives while still flagging the construct for security review when the attribute name is not a string constant.

### Can the analyzer detect dangerous code in imported modules?

No, the Behavioral AST analyzer only inspects the immediate file being analyzed. It does not recursively follow imports or analyze dependencies. It focuses on the AST of the current file, checking for calls to dangerous builtins and modules as defined in the detection sets.