Back to skill

Security audit

CP2K Cross-Code Input Studio

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for CP2K draft generation, but its helper scripts have real file-write and generated-input validation risks that warrant Review before installation.

Install only if you trust the CP2K inputs and request JSON you will process, and avoid running it in automated workflows over untrusted files until project-name path containment and strict generated-input field validation are added. The skill does not show exfiltration, persistence, or hidden network behavior, but its local file-writing helpers need hardening.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/render-cp2k-input.py:115
Finding
Unsanitized Request Fields Permit CP2K Input Directive Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/normalize-request.py:199-203`, `scripts/normalize-request.py:268-272`, `scripts/render-cp2k-input.py:115-126`, and `scripts/render-cp2k-input.py:318-323` **Vulnerability Type**: Generated-input injection through unvalidated template fields **Risk Level**: Medium ### Complete Code Snippet ```python def infer_xc(raw: Dict[str, Any], defaults: List[str]) -> str: explicit = raw.get("xc_functional") if isinstance(explicit, str) and explicit.strip(): return explicit.strip().upper() defaults.append("Defaulted xc_functional=PBE") return "PBE" ``` ```python def infer_basis_and_potential(job: Dict[str, Any], raw: Dict[str, Any], defaults: List[str], review: List[str]) -> None: if isinstance(raw.get("basis_family"), str) and raw["basis_family"].strip(): job["basis_family"] = raw["basis_family"].strip() if isinstance(raw.get("potential_family"), str) and raw["potential_family"].strip(): job["potential_family"] = raw["potential_family"].strip() ``` ```python def format_kind_blocks(elements: List[str], basis_family: str, xc: str) -> str: chunks = [] for e in elements: chunks.append( "\n".join([ f" &KIND {e}", f" BASIS_SET {basis_for(e, basis_family)}", f" POTENTIAL {potential_for(e, xc)}", " &END KIND", ]) ) return "\n".join(chunks) ``` ```python sections += [ periodic_poisson(job['periodicity']), " &QS", " METHOD GPW", " &END QS", scf_block(job), " &MGRID", f" CUTOFF {job['cutoff']}", f" REL_CUTOFF {job['rel_cutoff']}", " &END MGRID", " &XC", " &XC_FUNCTIONAL", f" &{job['xc_functional'].upper()}\n &END {job['xc_functional'].upper()}", " &END XC_FUNCTIONAL", ``` ### Technical Analysis The normalizer accepts arbitrary strings ...[truncated 2287 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define explicit allowlists for supported exchange-correlation functionals, basis families, and potential families. 2. Reject values containing carriage returns, line feeds, tabs, control characters, `&`, `@`, quotes, or other CP2K grammar metacharacters. 3. Apply strict full-string validation, for example with a conservative identifier pattern appropriate to each field rather than a generic free-form string. 4. Validate normalized data again in the renderer. Do not assume that every job specification was produced by the bundled normalizer. 5. Represent supported methods as internal enum values and map those values to fixed CP2K fragments rather than interpolating user text. 6. Apply equivalent validation to every field inserted into generated inputs, including project names, periodicity, run types, SCF modes, optimizers, MD values, and element labels. 7. Add negative tests using multiline values, section terminators, preprocessor syntax, and unexpected Unicode control characters. Tests should verify that rendering fails safely instead of emitting modified CP2K structure. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/convert-cp2k-input.py:241
Finding
CP2K Project Name Enables Output Path Traversal During Gaussian and ORCA Conversion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/convert-cp2k-input.py:20-31` and `scripts/convert-cp2k-input.py:241-253` **Vulnerability Type**: Path traversal and arbitrary file creation or overwrite **Risk Level**: Medium ### Complete Code Snippet ```python def parse_cp2k(inp: str) -> Dict: lines = inp.splitlines() data = { 'project': 'converted_job', 'run_type': 'ENERGY', 'charge': 0, 'multiplicity': 1, 'periodicity': 'NONE', 'kpoints': [1, 1, 1], 'cell': None, 'coords': [], 'warnings': [], 'xc_functional': 'PBE', } in_coord = False for line in lines: s = line.strip() if s.startswith('PROJECT '): data['project'] = s.split(None, 1)[1] ``` ```python def main() -> None: args = parse_args() data = parse_cp2k(read_text(args.cp2k_input)) out = Path(args.output) out.mkdir(parents=True, exist_ok=True) if args.target == 'gaussian': write_text(str(out / f"{data['project']}.gjf"), render_gaussian(data)) print(out / f"{data['project']}.gjf") elif args.target == 'orca': write_text(str(out / f"{data['project']}.inp"), render_orca(data)) print(out / f"{data['project']}.inp") ``` The write helper overwrites existing files: ```python def write_text(path: str, text: str) -> None: Path(path).write_text(text.rstrip() + '\n', encoding='utf-8') ``` ### Technical Analysis The converter treats the value following the CP2K `PROJECT` directive as a trusted filename component. For Gaussian and ORCA conversion, that value is joined directly to the user-selected output directory. `pathlib` does not neutralize traversal components. A value such as `../../target` results in a destination equivalent to `output_directory/../../target.gjf`. An absolute project path can also cause the left-hand output directory to be discarded by path joining. No basename conversion, character allowli ...[truncated 1612 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat `PROJECT` as metadata rather than a filesystem path. 2. Enforce a conservative project-name allowlist such as `[A-Za-z0-9][A-Za-z0-9._-]{0,127}`. 3. Reject names containing `/`, `\`, `..`, drive prefixes, null bytes, control characters, or absolute-path syntax. 4. Derive a safe basename explicitly and reject the input if sanitization would change its meaning; silently rewriting hostile input can conceal attacks. 5. Resolve and verify the destination before writing: ```python root = Path(args.output).resolve() destination = (root / f"{safe_project}.gjf").resolve() if destination.parent != root: raise ValueError("Output path escapes the selected directory") ``` 6. Use an exclusive file-creation mode by default so existing files are not silently overwritten. Require an explicit overwrite flag when replacement is intended. 7. Apply the same containment validation to every generated output path, even fixed filenames, to keep the output API consistently hardened. 8. Add regression tests for relative traversal, absolute paths, Windows path separators and drive prefixes, nested names, symlinked output directories, and existing destination files. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code only implements one narrow slice of the declared description: conversion of an existing CP2K input file into draft inputs for Gaussian, ORCA, VASP, or Quantum ESPRESSO. Even that conversion is limited to a small hand-parsed subset of CP2K syntax and emits placeholder/manual-review warnings. The declared purpose is substantially broader, emphasizing CP2K draft generation, refinement, explanation, review, conservative-default selection, and natural-language/structure-to-CP2K workflow support. None of those capabilities appear in the code. Therefore the description materially overstates and mischaracterizes the actual behavior.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
## 9. Non-goals for automatic routing

These tasks should not be silently auto-generated from a vague one-line prompt without warning:
- NEB / CI-NEB / transition-state search
- excited-state workflows beyond basic documented templates
- GW / RPA / MP2 / double hybrids
Confidence
80% confidence
Finding
Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
- an xyz file is being treated as a periodic crystal/slab
- a specialized workflow is requested beyond the supported draft space

## Output rules

Always emit or explain:
- what task was inferred
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill explicitly instructs use of local scripts and implies file read, file write, and shell-style execution, but it declares no tool scope or permissions boundaries. That creates unnecessary ambient authority: if the runtime grants broader defaults, the skill could access or modify files beyond what is needed, increasing the blast radius of prompt injection, path abuse, or accidental destructive actions.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The policy includes Chinese example requests and Chinese trigger keywords such as “帮我算一下这个结构”, “弛豫”, “动力学”, and “晶格常数”, but nowhere in the file does it state that multilingual handling is optional or limited to a justified region-specific use case. Under the stated policy rules, embedding a specific language/locale expectation without opt-in can be a natural-language policy violation.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
job_inp = outdir / 'job.inp'
    report_md = outdir / 'report.md'

    subprocess.run([sys.executable, str(NORMALIZER), args.raw_request_json, '-o', str(normalized)], check=True)
    subprocess.run([sys.executable, str(RENDERER), str(normalized), args.structure_file, '-o', str(job_inp)], check=True)

    job = load_json(normalized)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
report_md = outdir / 'report.md'

    subprocess.run([sys.executable, str(NORMALIZER), args.raw_request_json, '-o', str(normalized)], check=True)
    subprocess.run([sys.executable, str(RENDERER), str(normalized), args.structure_file, '-o', str(job_inp)], check=True)

    job = load_json(normalized)
    template = REPORT_TEMPLATE.read_text(encoding='utf-8')
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The file says these defaults are used by the "CP2K OpenClaw skill," while the provided manifest identifies the skill as "cp2k-crosscode-input-studio." This is an active documentation contradiction about which skill the defaults belong to, creating intent ambiguity for maintainers and auditors.

Vague Triggers

Low
Confidence
84% confidence
Finding
This markdown file defines defaults 'intended as skill defaults' but does not specify when the skill should apply this mapping versus when it should abstain. Without explicit trigger phrases, scope boundaries, or negative examples, the activation condition is ambiguous for a manifest/markdown-style skill reference.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
The natural-language parsing logic includes both English and Chinese trigger terms directly in the task inference tables, but the file does not state that multilingual parsing is intentional or user-selectable. This can create an undocumented language/locale behavior where request handling differs by language without explicit opt-in or justification.

Static analysis

No suspicious patterns detected.