Back to skill

Security audit

dive-into-langgraph

Security checks for vulnerabilities and agentic risk

Overview

This is a LangGraph tutorial skill, but several examples teach under-scoped handling of local files, user data, and external model/search providers.

Install only if you want Chinese LangGraph tutorial material and review the examples before reuse. Run demos in an isolated environment, pin dependencies, keep .env secrets out of source control, do not use the local-file context example with untrusted paths, add real authorization checks instead of prompt-only restrictions, and require consent/redaction before sending user content or files to external providers.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/6.context.md:349
Finding

Caller-controlled file paths can disclose arbitrary local files to an external model provider

Content
View full analysis

Vulnerability Details

File Location: references/6.context.md, lines 349–418
Vulnerability Type: Arbitrary local file read followed by external transmission
Risk Level: High

Vulnerable Code

python
@dataclass
class FileContext:
    uploaded_files: list[dict]

@wrap_model_call
def inject_file_context(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
    """Inject context about files user has uploaded this session."""
    uploaded_files = request.runtime.context.uploaded_files

    try:
        base_dir = os.path.dirname(os.path.abspath(__file__))
    except Exception as e:
        import ipynbname
        import os
        notebook_path = ipynbname.path()
        base_dir = os.path.dirname(notebook_path)

    file_sections = []
    for file in uploaded_files:
        name, ftype = "", ""
        path = file.get("path")
        if path:
            base_filename = os.path.basename(path)
            stem, ext = os.path.splitext(base_filename)
            name = stem or base_filename
            ftype = (ext.lstrip(".") if ext else None)

            content_list = [f"Name: {name}"]
            if ftype:
                content_list.append(f"Type: {ftype}")

            abs_path = path if os.path.isabs(path) else os.path.join(base_dir, path)

            content_block = ""
            if abs_path and os.path.exists(abs_path):
                try:
                    with open(abs_path, "r", encoding="utf-8") as f:
                        content_block = f.read()
                except Exception as e:
                    content_block = f"[File read error '{abs_path}': {e}]"
            else:
                content_block = "[File path missing or not found]"

            section = (
                f"---\n"
                f"{chr(10).join(content_list)}\n\n"
                f"{content_block}\n"
  
...[truncated 3081 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace caller-provided filesystem paths with opaque, server-issued upload identifiers.

  2. Store uploads beneath a dedicated root directory with restrictive permissions.

  3. Resolve and validate each requested path before opening it:

    python
    from pathlib import Path
    
    UPLOAD_ROOT = Path("/srv/app/uploads").resolve()
    
    def resolve_uploaded_file(file_id: str) -> Path:
        candidate = (UPLOAD_ROOT / file_id).resolve(strict=True)
    
        if candidate == UPLOAD_ROOT or UPLOAD_ROOT not in candidate.parents:
            raise PermissionError("File is outside the upload directory")
    
        if candidate.is_symlink() or not candidate.is_file():
            raise PermissionError("Invalid uploaded file")
    
        return candidate
    
  4. Reject absolute paths, .. traversal components, device files, and symbolic links.

  5. Allowlist expected text formats and enforce strict per-file and aggregate size limits.

  6. Read files incrementally rather than loading unlimited content into memory.

  7. Detect and redact likely credentials before constructing a remote prompt.

  8. Clearly notify users and obtain consent before sending uploaded content to an external model provider.

  9. Use a locally hosted model or an approved no-retention endpoint when processing sensitive documents.

  10. Avoid returning raw local paths and exception details to downstream model messages.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
references/6.context.md:230
Finding

Prompt-only role restriction does not prevent unauthorized tool execution

Content
View full analysis

Vulnerability Details

File Location: references/6.context.md, lines 230–264
Vulnerability Type: Missing deterministic authorization enforcement
Risk Level: Medium

Vulnerable Code

python
@dataclass
class Context:
    user_role: str
    deployment_env: str

@dynamic_prompt
def context_aware_prompt(request: ModelRequest) -> str:
    user_role = request.runtime.context.user_role
    env = request.runtime.context.deployment_env

    base = "You are a helpful assistant."

    if user_role == "admin":
        base += "\nYou can use the get_weather tool."
    else:
        base += "\nYou are prohibited from using the get_weather tool."

    if env == "production":
        base += "\nBe extra careful with any data modifications."

    return base

@tool
def get_weather(city: str) -> str:
    """Get weather for a given city."""
    return f"It's always sunny in {city}!"

agent = create_agent(
    model=llm,
    tools=[get_weather],
    middleware=[context_aware_prompt],
    context_schema=Context,
    checkpointer=InMemorySaver(),
)

The tutorial subsequently invokes the agent with a non-administrative role:

python
config = {'configurable': {'thread_id': str(uuid.uuid4())}}
result = agent.invoke(
    {"messages": [{"role": "user", "content": "What is the weather in Guangzhou today?"}]},
    context=Context(user_role="viewer", deployment_env="production"),
    config=config,
)

The recorded output shows that get_weather is still called for the viewer role.

Technical Analysis

The role restriction exists only as natural-language text in a dynamic system prompt. The tool remains registered and executable for every role. Language-model instructions are probabilistic and cannot serve as an authorization boundary; they may be ignored because of model error, adversarial prompt injection, conflicting context, or implementation changes.

The in ...[truncated 1483 chars]

Remediation
View remediation

Remediation Suggestions

  1. Never use system-prompt text as the sole access-control mechanism.

  2. Construct a role-specific tool list before creating or invoking the agent:

    python
    def tools_for_role(role: str):
        if role == "admin":
            return [get_weather]
        return []
    
  3. Add an authorization check inside every privileged tool so that execution remains protected even if tool registration is misconfigured.

  4. Derive roles from authenticated server-side identity data, not caller-supplied prompt or request fields.

  5. Use a central authorization policy mapping identities and roles to explicit actions.

  6. Deny access by default when context is missing, malformed, or unrecognized.

  7. Log denied calls and successful privileged calls with actor, tool, arguments, and authorization decision.

  8. Add automated tests proving that unauthorized roles cannot execute each protected tool, including under prompt-injection attempts.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/tools/tool_math.py:34
Finding

Agent-callable expression evaluator permits computational denial of service

Content
View full analysis

Vulnerability Details

File Location: scripts/tools/tool_math.py, lines 34–155
Vulnerability Type: Unbounded arithmetic and syntax evaluation
Risk Level: Medium

Vulnerable Code

python
class SafeEvaluator(ast.NodeVisitor):

    BIN_OPS = {
        ast.Add: operator.add,
        ast.Sub: operator.sub,
        ast.Mult: operator.mul,
        ast.Div: operator.truediv,
        ast.Pow: operator.pow,
        ast.Mod: operator.mod,
        ast.FloorDiv: operator.floordiv,
    }

    UNARY_OPS = {
        ast.UAdd: operator.pos,
        ast.USub: operator.neg,
    }

    SAFE_FUNCS = {
        "sqrt": math.sqrt,
        "exp": math.exp,
        "log": math.log,
        "log2": math.log2,
        "log10": math.log10,
        "sin": math.sin,
        "cos": math.cos,
        "tan": math.tan,
        "abs": abs,
    }

    def visit(self, node):
        return super().visit(node)

    def visit_Expression(self, node):
        return self.visit(node.body)

    def visit_Name(self, node):
        raise ValueError(f"Unsupported variable: {node.id}")

    def visit_BinOp(self, node):
        op_type = type(node.op)
        if op_type not in self.BIN_OPS:
            raise ValueError(f"Unsupported binary operator: {op_type}")
        left = self.visit(node.left)
        right = self.visit(node.right)
        return self.BIN_OPS[op_type](left, right)

    def visit_UnaryOp(self, node):
        op_type = type(node.op)
        if op_type not in self.UNARY_OPS:
            raise ValueError(f"Unsupported unary operator: {op_type}")
        operand = self.visit(node.operand)
        return self.UNARY_OPS[op_type](operand)

    def visit_Call(self, node):
        if not isinstance(node.func, ast.Name):
            raise ValueError("Invalid function-call format")

        func_name = node.func.id
        if func_name not in self.SAFE_FUNCS:
           
...[truncated 3622 chars]
Remediation
View remediation

Remediation Suggestions

  1. Enforce a small maximum input length before parsing.
  2. Walk the AST before evaluation and cap total node count and nesting depth.
  3. Reject numeric literals above a defined digit or magnitude limit.
  4. Remove exponentiation unless it is required.
  5. If exponentiation is required, restrict the exponent and base magnitude before calling pow.
  6. Check intermediate values after every operation and abort when they exceed the permitted range.
  7. Execute evaluation in an isolated worker process with strict CPU, memory, and wall-clock limits.
  8. Terminate the worker on timeout rather than relying on cooperative cancellation.
  9. Apply per-user rate limiting and concurrency limits to the tool.
  10. Add adversarial tests for large powers, large literals, long expressions, and deeply nested ASTs.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:18
Finding

Installation instructions use unpinned third-party dependencies

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 18–27; additional occurrences in multiple reference documents
Vulnerability Type: Unpinned and non-reproducible dependency installation
Risk Level: Medium

Vulnerable Code

bash
pip install \
  langgraph \
  "langchain[openai]" \
  langchain-community \
  langchain-mcp-adapters \
  python-dotenv \
  pydantic

Additional unpinned installation examples include:

bash
pip install -r requirements.txt
pip install langgraph langchain
pip install supervisor
pip install langchain-mcp-adapters
pip install -U langgraph-supervisor
pip install dashscope
pip install langchain-community tavily-python
pip install ddgs

These commands appear in:

  • references/1.quickstart.md, lines 27–31 and 64
  • references/7.mcp_server.md, lines 164–194
  • references/8.supervisor.md, lines 190–193
  • references/11.web_search.md, lines 37–45, 143–150, and 261

Technical Analysis

The project recommends installing packages without exact versions, hashes, or a bundled lockfile. Package resolution therefore depends on the latest versions available from the configured package index at installation time.

This prevents reproducible review: the code installed by one user may differ from the code installed by another user or from the versions assessed during this audit. The pip install -r requirements.txt instruction also references a dependency file that is not present in the audited artifact, so its contents cannot be verified here.

No direct evidence of typosquatting, dependency confusion, a malicious package, or an untrusted package index was found. The confirmed issue is the absence of supply-chain controls rather than proof that the named packages are malicious.

Attack Path

  1. A user follows the documented installation command.
  2. pip resolves mutable package versions and transitive dependencies from the configured in ...[truncated 881 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin every direct dependency to a reviewed exact version.
  2. Generate and include a lockfile containing resolved transitive versions.
  3. Use hash-verified installations, such as a requirements file generated with pip-compile --generate-hashes.
  4. Include every referenced requirements or lock file in the distributed artifact.
  5. Configure an approved package index or internal mirror and disable unintended fallback indexes.
  6. Run dependency vulnerability and provenance scanning in continuous integration.
  7. Review and deliberately update pinned versions rather than using -U in routine instructions.
  8. Install the tutorial in an isolated virtual environment or container under a non-privileged account.
  9. Record supported Python and dependency versions to improve reproducibility.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (37)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description says this skill is a comprehensive guide and reference for building agents using LangGraph 1.0, covering ReAct agents, state graphs, and tool integrations. The supplied code is instead a concrete example runner script: it loads .env settings, creates agents, attaches math tools, sends example prompts, prints outputs, lists tools, and demonstrates streaming. While tool integration is partially aligned, the primary purpose is not a guide/reference and there is no visible implementation of LangGraph state graphs or broad LangGraph 1.0 instructional coverage. This is a material description-to-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose says this skill is a guide/reference about building agents with LangGraph 1.0. The supplied code does not provide documentation, examples, agent orchestration, state graph construction, or LangGraph-specific functionality. Instead, it defines callable math tools and a safe expression parser/evaluator. This is a materially different primary purpose, so the description does not accurately represent the code.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/1.quickstart.md (reported line 72)May include surrounding context.

使用 .env.example 创建 .env 文件:

bash
cp .env.example .env

PS: 本教程使用阿里百炼平台的模型。你需要在 阿里百炼平台 注册账号,并将获得的 API 密钥填入 .env 文件中的 DASHSCOPE_API_KEY 变量。

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · references/1.quickstart.md (reported line 138)May include surrounding context.

agent

text

<!-- IMAGE: 1.quickstart/1.quickstart_6_0.png -->

## 三、带工具调用的 Agent

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · references/1.quickstart.md (reported line 138)May include surrounding context.

agent

text

<!-- IMAGE: 1.quickstart/1.quickstart_6_0.png -->

## 三、带工具调用的 Agent

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The masking example claims to protect PII, but it returns a masked assistant message without removing or replacing the original sensitive user message from state. As shown later, the downstream assistant can still access the original path and username and restate them, which defeats the privacy control and can leak sensitive data despite the presence of a 'masking' middleware.

Content

No source excerpt is available for this finding.

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · references/9.parallelization.md (reported line 115)May include surrounding context.

my_graph

text

<!-- IMAGE: 9.parallelization/9.parallelization_3_0.png -->

```python
# 调用图

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · references/9.parallelization.md (reported line 442)May include surrounding context.

react_graph

text

<!-- IMAGE: 9.parallelization/9.parallelization_21_0.png -->

### 2)测试工作流

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · references/9.parallelization.md (reported line 442)May include surrounding context.

react_graph

text

<!-- IMAGE: 9.parallelization/9.parallelization_21_0.png -->

### 2)测试工作流

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/1.quickstart.md (reported line 69)May include surrounding context.

md
def main():
    """主函数"""
    # 从 .env 文件加载环境变量
    load_dotenv()

    # 创建模型

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/1.quickstart.md (reported line 75)May include surrounding context.

md
def main():
    """主函数"""
    # 从 .env 文件加载环境变量
    load_dotenv()

    # 创建模型

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/1.quickstart.md (reported line 87)May include surrounding context.

md
def main():
    """主函数"""
    # 从 .env 文件加载环境变量
    load_dotenv()

    # 创建模型

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/examples.py (reported line 61)May include surrounding context.

python
def main():
    """主函数"""
    # 从 .env 文件加载环境变量
    load_dotenv()

    # 创建模型

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill description and instructional content switch into Chinese and continue in that language, but there is no indication that this is a region-specific skill or that users can opt into another language. This can violate a language/locale policy that requires user choice rather than forcing a specific language by default.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The file begins with a Chinese-only tutorial and presents all instructional content in Chinese without indicating that language selection is optional or that the skill is intended only for a Chinese-speaking audience. Under the language/locale policy rule, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The examples instruct the agent to send user prompts directly to third-party search providers (DashScope, Tavily, DDGS) but do not warn that user-entered queries may contain sensitive, personal, or proprietary data that will leave the local environment. In an agent context, this is more dangerous because the system prompt requires tool use before answering, increasing the likelihood of automatic disclosure of all user requests to external services.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The entire skill documentation and example interaction are presented in Chinese, including the user query and assistant response, with no indication that language selection is optional. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale limitation is explicitly justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The file presents the skill guidance entirely in Chinese and includes hard-coded Chinese prompts such as '你是一个隐私保护助手' for model behavior, with no indication that language selection is optional. Under the policy rule, forcing a specific language or locale without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The PII-detection examples send raw user content, potentially containing sensitive information, to an external model for classification without warning about third-party transmission or discussing trust boundaries. In a real deployment, this can itself constitute a privacy leak because the guardrail processes the secret by exporting it to another service before deciding how to handle it.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The example's final assistant response analyzes and repeats the original unmasked file path and username, demonstrating that the privacy-preserving flow failed in practice. Even if a masked copy is generated, allowing later prompts or responses to reference the original sensitive text undermines confidentiality and can normalize insecure implementation patterns for readers.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The document is entirely written in Chinese and the example interaction and final response are also presented only in Chinese, including user-facing output at L115. This creates a language constraint without any stated opt-in, fallback, or user choice, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The long-term memory examples demonstrate storing and retrieving user profile data such as names, language preferences, and behavioral rules, but they provide no warning about data minimization, consent, retention, access control, or persistence risks. In an agent-building guide, readers may copy this pattern directly into production systems, leading to privacy violations or unauthorized retention/exposure of personal data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The example stores a rule stating the user 'only speaks English & python' without showing user opt-in, verification, or a correction path. This can normalize hard-coded preference memory that restricts user interaction based on inferred attributes, causing exclusionary behavior or incorrect personalization when developers reuse the pattern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The markdown demonstrates storing language preferences as fixed values like "Chinese" and "Korean", then using them to steer assistant responses. This is a natural-language locale policy concern because the skill forces a specific language choice from backend context rather than explicitly offering the user a language or locale choice in the interaction.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.