Back to skill

Security audit

Pharmaclaw Cheminformatics

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real local cheminformatics toolkit, but it should be reviewed because it can consume substantial compute and write user-directed output files without strong limits.

Review before installing in a shared or automated environment. Use a dedicated output directory, avoid running it on untrusted or very large molecules, set external CPU/memory/time limits, and install reviewed pinned dependency versions. Do not treat its FDA/IP/downstream-agent suggestions as legal or regulatory advice.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/conformer_gen.py:48
Finding
Unbounded Computational Parameters Permit Resource Exhaustion## Vulnerability Details **File Locations**: - `scripts/conformer_gen.py:48-55` - `scripts/conformer_gen.py:175-180` - `scripts/stereoisomers.py:85-91` - `scripts/stereoisomers.py:184-196` - `scripts/recap_fragment.py:139-153` - `scripts/recap_fragment.py:231-244` **Vulnerability Type**: Uncontrolled resource consumption **Risk Level**: Medium ### Vulnerable Code `scripts/conformer_gen.py:48-55`: ```python # ETKDG parameters params = rdDistGeom.ETKDGv3() params.randomSeed = seed params.pruneRmsThresh = prune_rms params.numThreads = 0 # use all cores # Generate conformers conf_ids = rdDistGeom.EmbedMultipleConfs(mol, numConfs=num_confs, params=params) ``` `scripts/conformer_gen.py:175-180`: ```python parser.add_argument("--num_confs", type=int, default=10, help="Number of conformers") parser.add_argument("--optimize", choices=["mmff", "uff", "none"], default="mmff") parser.add_argument("--energy_window", type=float, default=None, help="Energy window in kcal/mol (filter high-energy confs)") parser.add_argument("--prune_rms", type=float, default=0.5, help="RMSD threshold for pruning similar conformers") ``` `scripts/stereoisomers.py:85-91`: ```python # Configure enumeration options opts = StereoEnumerationOptions() opts.onlyUnassigned = only_unassigned opts.maxIsomers = max_isomers opts.unique = unique # Enumerate isomers = list(EnumerateStereoisomers(mol, options=opts)) ``` `scripts/stereoisomers.py:184-196`: ```python parser.add_argument("--max_isomers", type=int, default=64) parser.add_argument("--only_unassigned", action="store_true", help="Only enumerate unassigned stereocenters") args = parser.parse_args() if args.action == "analyze": result = analyze_stereo(args.smiles) elif args.action == "compare": result = compare_enantiomers(args.smiles) else: result = enumerate_stereois ...[truncated 3040 chars]
Remediation
## Remediation Suggestions - Enforce strict ranges for every caller-controlled numeric parameter. For example, limit conformers and stereoisomers to a deployment-approved maximum and restrict RECAP depth to a small positive value. - Reject zero, negative, non-finite, or otherwise nonsensical values. - Replace `params.numThreads = 0` with a small configurable upper bound. - Limit molecular complexity using atom count, heavy-atom count, rotatable-bond count, ring count, and potential stereocenter count rather than relying only on SMILES length. - Apply process-level CPU, memory, and wall-clock limits around RDKit operations. - Avoid eagerly materializing potentially large generators where incremental processing is possible. - Apply request quotas and concurrency controls when exposing these scripts through an Agent or service interface. - Return a controlled error when an input exceeds the permitted complexity or resource budget.

T08 · Insecure Dependencies

Note
Location
SKILL.md:165
Finding
Third-Party Dependencies Are Unpinned and Not Integrity-Verified## Vulnerability Details **File Location**: `SKILL.md:165-171` **Related Locations**: RDKit import-error installation prompts in the Python scripts, including `scripts/chain_entry.py:25-29` **Vulnerability Type**: Dependency supply-chain weakness **Risk Level**: Low ### Vulnerable Code `SKILL.md:165-171`: ```markdown ## Dependencies - Python ≥ 3.10 - rdkit-pypi - Pillow (for pharmacophore map PNG) - numpy ``` `scripts/chain_entry.py:25-29`: ```python try: from rdkit import Chem except ImportError: print(json.dumps({"error": "RDKit not installed. pip install rdkit-pypi"})) sys.exit(1) ``` ### Technical Analysis The project names its dependencies but does not pin exact versions, provide a lock file, or specify cryptographic hashes. The runtime error message recommends a direct package installation command that resolves the currently available package from the operator's configured Python package index. This does not prove that any listed dependency is malicious. However, mutable dependency resolution prevents reproducible builds and increases exposure to compromised releases, unsafe package indexes, dependency substitution, or future incompatible versions. Dependencies such as RDKit, Pillow, and NumPy process complex data and include native components, increasing the importance of controlled provenance and timely security updates. ### Attack Path 1. An operator deploys the Skill in an environment without RDKit. 2. The script displays the recommendation to run `pip install rdkit-pypi`. 3. The operator installs the package without a locked version or verified hash. 4. The package manager resolves artifacts from the configured index at installation time. 5. If that index, package release, or dependency chain has been compromised, attacker-controlled installation or runtime code may execute with the privileges of the installing user. This exploitation path depends on an external su ...[truncated 581 chars]
Remediation
## Remediation Suggestions - Provide a dependency lock file containing exact, reviewed versions. - Use cryptographic hashes for downloaded artifacts, such as pip hash-checking mode. - Install packages only from an approved and authenticated package repository. - Build and deploy from a controlled environment rather than asking runtime users to install dependencies interactively. - Generate and maintain a software bill of materials. - Integrate dependency vulnerability scanning and automated update review into the release process. - Pin versions compatibly with the supported Python runtime and test upgrades before release. - Document the verified package source and installation procedure instead of presenting an unconstrained `pip install` command.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is much broader than the supplied code. The code only implements molecular format conversion and basic 3D coordinate generation during export to structure formats. It can read SDF/MOL/PDB/MOL2 files or SMILES, convert to SMILES/InChI/InChIKey/MOL/SDF/PDB/XYZ, and batch-process multi-molecule files. However, it does not implement pharmacophore analysis, RECAP fragmentation, stereoisomer enumeration, or general cheminformatics profiling, all of which are central parts of the declared purpose. The primary purpose is therefore materially narrower than advertised. There is no concerning undeclared external access, but the behavior does not accurately represent the broad declared functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The description substantially overstates the code’s scope. The supplied script is specifically a pharmacophore analysis tool with limited supporting 3D embedding. It does match parts of the description related to pharmacophore feature extraction, fingerprints, comparison, and mapping, and it does generate a 3D conformer internally for feature positioning. However, the declared purpose presents a much broader 'advanced cheminformatics agent' covering format conversion, RECAP fragmentation, stereoisomer enumeration, library design support, docking prep, and integration chaining, none of which appear in the code. This is a material description-versus-behavior mismatch because the primary declared capability set is far broader than the actual implemented behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The description substantially overstates the skill's scope. The supplied code only supports one subset of the declared functionality: stereoisomer analysis/enumeration from SMILES, plus limited descriptor reporting. It does not perform 3D molecular analysis, conformer generation, force-field optimization, pharmacophore mapping, file format conversion, RECAP fragmentation, or workflow chaining. While stereoisomer enumeration is accurately represented, the overall declared purpose does not accurately describe what this specific code chunk actually does, making this a material description-behavior mismatch.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises scripts that write output files such as SDF, PDB, PNG, and arbitrary output paths, but the manifest declares no explicit tool scope or permission boundaries. In an agent framework, undocumented file-write capability increases the chance of unauthorized filesystem modification, path misuse, or unsafe chaining because the platform cannot constrain what the skill is allowed to write.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The trigger list is very broad, including generic chemistry and workflow terms such as 'format conversion,' 'fragmentation,' 'library design,' and 'docking prep,' which can cause the skill to activate on ordinary requests outside its safest intended scope. Over-triggering is risky in an agent ecosystem because it can route sensitive or unrelated tasks into a file-writing chemistry skill and amplify the effect of inaccurate outputs or unsafe side effects.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
def run_module(module_name: str, func_name: str, **kwargs):
    """Dynamically import and run a module function."""
    try:
        mod = __import__(module_name)
        func = getattr(mod, func_name)
        return func(**kwargs)
    except Exception as e:
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest describes this skill as performing cheminformatics operations such as conformer generation, pharmacophore mapping, format conversion, RECAP fragmentation, stereoisomer enumeration, and profiling. The code goes further by emitting normative drug-development/regulatory advice ("FDA requirement") and recommending downstream patent-expansion and catalyst-design agents, which are business/workflow decisions rather than core cheminformatics analysis.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
Recommending "ip-expansion" based on the presence of stereoisomers introduces an intellectual-property strategy capability. That is not an obvious or necessary part of performing molecular analysis, pharmacophore extraction, format conversion, fragmentation, or stereoisomer enumeration as described in the manifest.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
"""Dynamically import and run a module function."""
    try:
        mod = __import__(module_name)
        func = getattr(mod, func_name)
        return func(**kwargs)
    except Exception as e:
        return {"error": f"{module_name}.{func_name} failed: {str(e)}"}
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Missing User Warnings

Low
Confidence
88% confidence
Finding
This code writes conformer data to a user-specified output file when --output is provided, but there is no confirmation prompt or visible runtime message indicating that a file will be created or overwritten. Although the CLI help mentions an output file path, the execution path itself provides no user-facing disclosure around the write operation.

Missing User Warnings

Low
Confidence
81% confidence
Finding
This code writes converted molecular data to a user-specified path, but provides no explicit user-facing warning, confirmation prompt, or log message at the point of write. Although the CLI exposes an --output parameter and the module docstring shows examples, the code itself does not clearly disclose that existing files may be created or overwritten.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The SDF writer persists data to disk when an output path is supplied, but there is no explicit warning, confirmation, or user-facing log around this write. The action affects local files and should be disclosed more clearly to the user.

Missing User Warnings

Low
Confidence
83% confidence
Finding
In batch mode, the script writes multiple molecules to a single SDF file, but there is no user-facing disclosure at the time of the write and no warning about potential overwrite of the destination. This is a safety-relevant file modification operation under the code-file criteria.

Static analysis

No suspicious patterns detected.