SkillSpector Dangerous Function Calls: AST Analyzer Detection Patterns
SkillSpector’s behavioral AST analyzer identifies dangerous function calls by scanning Python source files for built-in execution APIs, subprocess and OS process spawners, dynamic attribute access, and dangerous execution chains that wrap high-risk operations.
NVIDIA’s SkillSpector employs a static analysis engine to detect unsafe code execution patterns in AI-generated skills. The analyzer evaluates Python source files against a curated list of dangerous function signatures defined in src/skillspector/nodes/analyzers/behavioral_ast.py, generating SARIF findings with rule IDs AST1 through AST8.
Built-in Execution APIs (AST1, AST2, AST6)
The analyzer flags calls to Python’s built-in functions that can execute arbitrary code. According to lines 32-33 of behavioral_ast.py, this category includes exec(), eval(), compile(), and __import__().
These functions allow dynamic code execution or module loading, making them primary targets for injection attacks. When the analyzer encounters these calls via the _analyze_python function, it emits findings with high severity and confidence scores.
# AST1 - exec()
exec("print('dangerous')")
# AST2 - eval()
result = eval("2 + 2")
# AST6 - compile()
code_obj = compile("x = 1", "<string>", "exec")
Subprocess Module Calls (AST4)
Any function from the subprocess module that spawns external processes triggers rule AST4. Lines 34-44 of behavioral_ast.py define this set as subprocess.call, subprocess.run, subprocess.Popen, subprocess.check_output, subprocess.check_call, subprocess.getoutput, and subprocess.getstatusoutput.
These calls create command injection risks when user input reaches the shell. The analyzer uses resolve_call_name from src/skillspector/nodes/analyzers/common.py to resolve imported names and match them against this dangerous set.
import subprocess
subprocess.run(["rm", "-rf", "/"]) # AST4
subprocess.Popen("ls -la", shell=True) # AST4
subprocess.check_output("whoami", shell=True) # AST4
OS Exec-Family Calls (AST5)
The analyzer detects 21 distinct functions from the os module that execute or spawn system processes. Lines 46-68 of behavioral_ast.py enumerate the complete list including os.system, os.popen, all os.exec* variants (execl, execle, execlp, execlpe, execv, execve, execvp, execvpe), and all os.spawn* variants (spawnl, spawnle, spawnlp, spawnlpe, spawnv, spawnve, spawnvp, spawnvpe), plus os.posix_spawn and os.posix_spawnp.
These low-level system calls bypass Python’s safety mechanisms, warranting immediate flagging with critical severity.
import os
os.system("ls /tmp") # AST5
os.execv("/bin/ls", ["ls", "/tmp"]) # AST5
Dynamic Attribute Access (AST7)
SkillSpector flags getattr() calls where the attribute name is not a constant literal. Lines 78-84 of behavioral_ast.py implement this check to detect reflection-based attacks where user-controlled input determines which method or attribute to access.
Dynamic attribute retrieval enables attackers to invoke private methods or bypass access controls when the second argument derives from external input.
obj = SomeClass()
attr_name = input("attribute?") # user-controlled
value = getattr(obj, attr_name) # AST7 - flagged because attr_name is not constant
Dangerous Execution Chains (AST8)
The most severe pattern occurs when exec(), eval(), or compile() wraps any dangerous source including subprocess calls, OS exec functions, __import__(), or network-related helpers. Lines 71-80 of behavioral_ast.py implement this chain detection, reporting it as rule AST8 with a 0.95 confidence score.
This pattern indicates an attempt to obfuscate malicious code execution by nesting dangerous calls within evaluation contexts.
import subprocess
exec("subprocess.run(['rm', '-rf', '/'])") # AST8 - dangerous chain detected
How the Detection Engine Works
The core detection logic resides in the _analyze_python function within src/skillspector/nodes/analyzers/behavioral_ast.py. The analyzer walks the AST and uses resolve_call_name from src/skillspector/nodes/analyzers/common.py to fully qualify function references, handling imports and aliases.
When a match occurs against the dangerous call sets defined in lines 32-68, the analyzer constructs an AnalyzerFinding object (defined in src/skillspector/models.py) containing the rule ID, file location, code snippet, severity level, and remediation guidance. The meta_analyzer.py orchestrator aggregates these findings into the final SARIF report.
Summary
- SkillSpector detects five categories of dangerous function calls through its behavioral AST analyzer: built-in execution APIs, subprocess spawners, OS exec-family functions, dynamic attribute access, and dangerous execution chains.
- Rule IDs AST1 through AST8 map to specific dangerous patterns with severity ratings ranging from high to critical and confidence scores up to 0.95.
- The detection logic is implemented in
src/skillspector/nodes/analyzers/behavioral_ast.pywith helper functions incommon.pyand data models inmodels.py. - Code examples demonstrate how
exec(),subprocess.run,os.system,getattr()with variables, and nested execution chains trigger analyzer findings.
Frequently Asked Questions
What confidence score does SkillSpector assign to dangerous execution chains?
SkillSpector assigns AST8 findings a confidence score of 0.95, indicating high certainty that the pattern represents a dangerous execution chain where exec(), eval(), or compile() wraps other high-risk operations.
How does SkillSpector distinguish safe from dangerous getattr calls?
The analyzer inspects the AST node for the second argument to getattr() as implemented in lines 78-84 of behavioral_ast.py. If the attribute name is not a constant literal (e.g., a string variable or concatenated value), it triggers rule AST7; constant literals like getattr(obj, "method") are not flagged.
Which file contains the core detection logic for dangerous function calls?
The core detection logic resides in src/skillspector/nodes/analyzers/behavioral_ast.py, specifically within the _analyze_python function and the dangerous call sets defined at lines 32-68. Helper functions for name resolution are located in src/skillspector/nodes/analyzers/common.py.
What severity levels does SkillSpector use for dangerous function calls?
SkillSpector assigns severity levels ranging from high to critical depending on the specific dangerous pattern. Built-in execution APIs and subprocess calls typically receive high severity, while OS exec-family calls and dangerous execution chains (AST8) receive critical severity ratings.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →