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 AnalyzerFinding class, capturing severity, confidence, location, and context before conversion to the standard Finding format.
  • Integration occurs through LangGraph, with analyzer nodes registered in ANALYZER_NODES and executed during the context-building phase of the workflow defined in src/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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →