How Static Analysis Works in SkillSpector's Detection Process
Static analysis in SkillSpector serves as the first line of defense, providing rapid, syntax-agnostic detection of known insecure patterns by scanning source code and configuration files through regex-based analyzer nodes integrated into a LangGraph workflow.
SkillSpector, NVIDIA's security assessment framework, employs static analysis to identify security-relevant issues before invoking heavier semantic or LLM-based evaluations. This initial stage operates by wiring a collection of analyzer nodes into a directed graph workflow defined in src/skillspector/graph.py (lines 33-53), allowing the tool to catch high-confidence vulnerabilities through pattern matching rather than runtime execution.
Integration with the LangGraph Workflow
Static analysis nodes are registered in the ANALYZER_NODES collection within src/skillspector/nodes/analyzers/__init__.py. During a scan, the LangGraph workflow executes in distinct phases: first building context by collecting file contents, then streaming that context to every registered static-analysis node, and finally passing their aggregated findings to the meta_analyzer node (lines 38-51 of src/skillspector/graph.py). This architecture allows static patterns to run in parallel against the same file content before the system performs more resource-intensive analysis.
The static-analysis stage complements later semantic and LLM-based analyses by catching straightforward, high-confidence problems directly from text, configuration files, and documentation. Results flow through analyzer_finding_to_finding conversion functions and eventually reach the report node, which formats the final SARIF output.
The Static Analysis Execution Pipeline
The run_static_patterns function in src/skillspector/nodes/analyzers/static_runner.py orchestrates the static detection process through four distinct phases:
File Filtering and Type Detection
Before analysis begins, the system filters the input file list to skip oversized files and evaluation-dataset prose. The runner infers a file type from the file extension (lines 63-68), ensuring that appropriate pattern sets are applied only to relevant content types. This preprocessing step prevents wasted computation on binary files or non-security-relevant documentation.
Regex Pattern Matching
Each static-pattern module implements an analyze(content, file_path, file_type) function that walks the file's text with compiled regular-expression patterns. For example, the prompt-injection analyzer in src/skillspector/nodes/analyzers/static_patterns_prompt_injection.py (lines 35-86) searches for specific vulnerability signatures:
- P1 patterns: Instruction-override strings such as "ignore all previous instructions"
- P2 patterns: Hidden-instruction markers
- P3 patterns: Exfiltration commands
- P4 patterns: Behavior-manipulation language
The analyzer uses re.finditer with flags like re.IGNORECASE | re.MULTILINE to locate matches while preserving line number information for accurate reporting.
Structured Finding Generation
When a pattern matches, the analyzer constructs an AnalyzerFinding object (lines 20-33) containing:
- rule_id: Identifier for the specific pattern (e.g., "P1")
- severity: Risk level (e.g.,
Severity.HIGH) - location: File path and start line number
- confidence: Reliability score of the match
- tags: Classification category (e.g.,
PatternCategory.PROMPT_INJECTION) - context: Snippet of surrounding text for review
- matched_text: The exact substring triggering the alert
Conversion to Graph State
The run_static_patterns function transforms each AnalyzerFinding into a generic Finding object using analyzer_finding_to_finding (lines 101-130). This conversion enriches the data with remediation text, category names, and explanations derived from the pattern-defaults registry, ensuring consistent formatting across all analyzer types before the findings enter the shared graph state.
Static Pattern Analyzer Categories
SkillSpector includes specialized static analyzers for distinct vulnerability classes:
- Prompt Injection Detection: Identifies instruction overrides and hidden commands in AI-related code (implemented in
static_patterns_prompt_injection.py) - Privilege Escalation: Detects excessive permission requests and credential-access patterns (implemented in
static_patterns_privilege_escalation.py) - Data Exfiltration: Scans for patterns indicating unauthorized data transfer or leakage attempts
Each analyzer follows the same interface contract—accepting content and file metadata, returning a list of AnalyzerFinding objects—allowing the framework to treat all static detection uniformly.
Implementation Examples
The following example demonstrates how to invoke static pattern analysis programmatically:
# Example: run static patterns on a set of components
from skillspector.nodes.analyzers import static_patterns_prompt_injection as pi_mod
from skillspector.nodes.analyzers import static_runner
state = {
"components": ["example.py", "README.md"],
"file_cache": {
"example.py": open("example.py").read(),
"README.md": open("README.md").read(),
},
}
findings = static_runner.run_static_patterns(state, [pi_mod])
for f in findings:
print(f.rule_id, f.message, f.severity, f.location.file, f.start_line)
The internal implementation of a static-pattern analyzer follows this structure:
# Inside a static-pattern module (prompt injection)
def analyze(content: str, file_path: str, file_type: str) -> list[AnalyzerFinding]:
findings = []
for pattern, confidence in P1_PATTERNS:
for m in re.finditer(pattern, content, re.IGNORECASE | re.MULTILINE):
findings.append(
AnalyzerFinding(
rule_id="P1",
message="Instruction Override",
severity=Severity.HIGH,
location=Location(file=file_path, start_line=get_line_number(content, m.start())),
confidence=confidence,
tags=[PatternCategory.PROMPT_INJECTION.value],
context=get_context(content, m.start()),
matched_text=m.group(0)[:200],
)
)
return findings
Summary
- Static analysis acts as the initial filter in SkillSpector's detection pipeline, running before semantic and LLM-based evaluations to catch obvious vulnerabilities quickly.
- Pattern matching uses compiled regular expressions across multiple vulnerability categories (prompt injection, privilege escalation, data exfiltration) implemented in dedicated analyzer modules.
- Findings are richly structured through the
AnalyzerFindingclass, capturing severity, confidence, location, and context before conversion to the standardFindingformat. - Integration occurs through LangGraph, with analyzer nodes registered in
ANALYZER_NODESand executed during the context-building phase of the workflow defined insrc/skillspector/graph.py.
Frequently Asked Questions
How does static analysis differ from semantic analysis in SkillSpector?
Static analysis operates on surface-level text patterns using regular expressions to detect known insecure signatures without understanding code semantics. Semantic analysis, which occurs later in the workflow, uses LLM-based reasoning to understand context and intent. Static analysis provides immediate, high-confidence results while semantic analysis handles complex, context-dependent vulnerabilities.
What file types does SkillSpector's static analysis support?
The system infers file types from extensions within run_static_patterns (lines 63-68 of static_runner.py), applying different pattern sets based on the detected type. While the framework can scan any text-based file, specific modules like static_patterns_prompt_injection.py target source code, configuration files, and documentation containing security-relevant strings.
How are static analysis findings prioritized?
Each AnalyzerFinding includes explicit severity and confidence fields set during pattern matching. High-severity findings (such as P1 instruction overrides marked as Severity.HIGH) surface immediately. After initial detection, the meta_analyzer node aggregates results from all static analyzers and ranks them before final SARIF report generation.
Can custom static patterns be added to SkillSpector?
Yes. The modular architecture in src/skillspector/nodes/analyzers/__init__.py registers analyzers through the ANALYZER_NODES collection. Developers can create new modules implementing the standard analyze(content, file_path, file_type) interface, returning AnalyzerFinding objects with appropriate rule IDs and PatternCategory tags to integrate seamlessly into the existing LangGraph workflow.
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 →