Back to skill

Security audit

codesys-toolkit

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its PLC automation purpose, but it has high-impact code execution and project-mutation paths that need review before installation.

Install only if you trust the publisher, the internal GitLab source, and every configured workspace. Prefer the bundled reviewed tools over download_tools.ps1 unless you verify the exact remote contents. Use backups and non-production PLC projects first, review env.json paths carefully, avoid untrusted workspaces containing generator_override.py, and consider running with -NoKill to avoid terminating unrelated InoProShop sessions.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/tools/download_tools.ps1:5
Finding

Mutable Remote Scripts Are Downloaded Without Integrity Verification

Content
View full analysis
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/tools/run_script.ps1:91
Finding

Generate Alias Can Be Silently Hijacked by a Workspace Script

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (27)

Hidden Instructions

High
Category
Prompt Injection
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · SKILL.md (reported line 1)May include surrounding context.

md
---
name: codesys-toolkit
version: 1.0.0
description: "Automate CODESYS/InoProShop PLC programming �� project generation, POU export/patching, compile c..."

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 35)May include surrounding context.

md
| `generator_runner.py` | IronPython | 工程生成:模板→创建 POU/GVL/DUT→挂�?Task→编�?|

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation explicitly describes actions that kill existing InoProShop processes and patch or generate PLC projects, but it does not prominently warn about destructive side effects, backup requirements, or the risk of modifying production engineering artifacts. In an industrial automation context, unclear disclosure around process termination and project mutation can lead to accidental disruption, data loss, or unsafe deployment workflows.

Content

No source excerpt is available for this finding.

Tainted flow: '_err_early' from os.environ.get (line 36, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/tools/check_compile.py (reported line 40)May include surrounding context.

python
try:
        if not os.path.exists(os.path.dirname(_err_early)):
            os.makedirs(os.path.dirname(_err_early))
        with open(_err_early, 'ab') as _fe:
            _fe.write(u'[FATAL] workspace_dir \u672a\u8bbe\u7f6e\uff0c\u8bf7\u5148\u5728 env.json \u4e2d\u5199\u5165 workspace_dir \u5b57\u6bb5\n'.encode('utf-8'))
    except:
        pass

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The file header describes a narrow purpose: exporting POUs from a project into the st/ directory. In addition to that, the implementation removes the entire existing st/ tree before recreating it (L202-L206), writes .committed.json for future patch tracking (L381-L410), and writes task_mounts.json containing task configuration details (L412-L469). These behaviors extend beyond the stated intent in the documentation.

Content

No source excerpt is available for this finding.

Tainted flow: '_err_early' from os.environ.get (line 25, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/tools/export_pou.py (reported line 29)May include surrounding context.

python
try:
        if not os.path.exists(os.path.dirname(_err_early)):
            os.makedirs(os.path.dirname(_err_early))
        with open(_err_early, 'ab') as _fe:
            _fe.write(u'[FATAL] workspace_dir \u672a\u8bbe\u7f6e\uff0c\u8bf7\u5148\u5728 env.json \u4e2d\u5199\u5165 workspace_dir \u5b57\u6bb5\n'.encode('utf-8'))
    except:
        pass

Tainted flow: '_log_path' from os.environ.get (line 164, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/tools/export_pou.py (reported line 165)May include surrounding context.

python
os.makedirs(_log_dir)
    # reopen log in the correct location
    _log_path = os.path.join(_log_dir, 'export_pou_log.txt')
    open(_log_path, 'wb').close()

log('--- export_pou.py start ---')
log('project : ' + PROJ_PATH)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script unconditionally deletes the entire st/ directory with shutil.rmtree(_st_dir) before rebuilding it. In this skill context, st/ is a workspace export area, so the main risk is destructive data loss or accidental deletion of unintended files if workspace_dir is misconfigured or manipulated.

Content

No source excerpt is available for this finding.

Tainted flow: '_tm_path' from os.environ.get (line 461, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/tools/export_pou.py (reported line 465)May include surrounding context.

python
_tm_json = json.dumps(_tm_out, ensure_ascii=False, indent=2)
    if isinstance(_tm_json, unicode):
        _tm_json = _tm_json.encode('utf-8')
    with open(_tm_path, 'wb') as _tmf:
        _tmf.write(_tm_json)
    log(u'task_mounts.json written ({} tasks)'.format(len(_tm_out)))
except Exception as _tme:

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The comments near the configuration loading state that task_mounts are read from workspace_dir/task_mounts.json, implying they will be used to control task mounting. Later, the script deletes task_mounts.json and the active mounting logic explicitly uses a fallback strategy that appends program calls to a template PROGRAM, while create=true entries only create empty tasks and do not mount POUs as described.

Content

No source excerpt is available for this finding.

Tainted flow: '_template' from open (line 35, file read) → shutil.copy (file write)

Medium
Category
Data Flow
Confidence
88% confidence
Finding

The script reads the template path from env.json and copies whatever path is supplied into the workspace without constraining it to an approved base directory or validating that it is an expected project template. In this automation context, that enables path traversal or unintended file access if an attacker can influence env.json, causing the tool to ingest arbitrary local files as project inputs and potentially poison downstream project generation.

Content

Scanner excerpt · scripts/tools/generator_runner.py (reported line 146)May include surrounding context.

python
# ── 复制模板(先删旧文件避免只读属性冲突) ──
if os.path.exists(_new_project):
    os.remove(_new_project)
shutil.copy(_template, _new_project)
log(u'Copied template -> ' + _new_project)

# ── 打开工程,置询等待加载(最长 30s) ──

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The comments describe two strategies, with Strategy A using the official task pous API when task_mounts is configured. However, the actual execution path below always uses Strategy B to append calls into an existing template PROGRAM, and task_mounts handling is reduced to optionally creating empty tasks, which materially contradicts the documented intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The build routine explicitly parses compiler categories and summaries using Chinese terms such as '编译', '编译完成', '错误', and '警告', while the file comments and user-facing context are also written in Chinese. This creates a locale dependency that assumes a Chinese environment and does not offer any user choice or documented opt-in, matching the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The file header describes the tool as a POU patcher that opens a project, overwrites specified POUs, saves, and compiles. In addition to patching existing POUs, the code can create folders, POUs, DUTs, GVLs, actions, tasks, and mount POUs into tasks when missing, which is materially broader than the stated behavior.

Content

No source excerpt is available for this finding.

Tainted flow: '_err_early' from os.environ.get (line 31, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/tools/patch_pou.py (reported line 35)May include surrounding context.

python
try:
        if not os.path.exists(os.path.dirname(_err_early)):
            os.makedirs(os.path.dirname(_err_early))
        with open(_err_early, 'ab') as _fe:
            _fe.write(u'[FATAL] workspace_dir \u672a\u8bbe\u7f6e\uff0c\u8bf7\u5148\u5728 env.json \u4e2d\u5199\u5165 workspace_dir \u5b57\u6bb5\n'.encode('utf-8'))
    except:
        pass

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The nearby comments and docstring frame this section as applying TASK_MOUNTS to an existing Task Configuration. However, when create is enabled, the code will create a new task via task_cfg.create_task(task_name), which is a stronger side effect than merely applying mounts to existing tasks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The script opens an existing automation project, mutates code objects and task configuration, then saves the project without any interactive confirmation, dry-run mode, or explicit guardrail at the point of overwrite. In an industrial-control/project-engineering context, silent persistence of unintended changes can cause integrity loss, operational disruption, and unsafe deployment if upstream inputs such as env.json, st/ content, or task_mounts.json are wrong or tampered with.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The launcher explicitly accepts an arbitrary absolute or relative path from the user and passes it to InoProShop via /runscript, which enables execution of any script reachable by the operator, not just the vetted aliases. In a skill context, this materially expands capability and bypasses any trust boundary implied by the built-in tool mapping, making accidental or unauthorized code execution much easier.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Allowing user-supplied script paths without an explicit safety warning increases the likelihood that operators will run unreviewed or unexpected code under the trusted InoProShop workflow. This is especially risky because the wrapper makes arbitrary script execution look equivalent to running the built-in maintenance aliases.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script forcefully terminates all InoProShop processes by default unless -NoKill is specified, which can destroy unsaved work or interrupt unrelated sessions. Making destructive behavior the default is unsafe in shared or interactive environments because a normal invocation can unexpectedly affect other work on the host.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill description is presented in Chinese only, and the README does not offer any language or locale choice for users. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is clearly documented and justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The natural-language instructions and usage guidance are predominantly presented in Chinese, and the file does not indicate that this language choice is optional or region-specific. Under the language/locale policy, forcing a specific language without user opt-in can be a policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The module docstring presents usage, inputs, and outputs exclusively in Chinese. This can violate a language/locale policy when users are not given an explicit choice of language and the file does not document that it is intentionally region-specific.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The header documentation explicitly states that log/check_compile_result.txt is a single-line result of the form 'OK / FAIL:<错误数>'. However, on success the implementation writes 'OK:<warning_count> warnings' at L336, which contradicts the documented output contract rather than merely omitting detail.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The fatal error written to export_pou_error.txt is only in Chinese (workspace_dir 未设置,请先在 env.json 中写入 workspace_dir 字段). This imposes a fixed language choice on users without offering a locale option or documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.