Back to skill

Security audit

Chemical Storage Sorter

Security checks for vulnerabilities and agentic risk

Overview

This skill is not malware, but it needs Review because it gives real-world chemical storage advice with unreliable logic and broader agent permissions than its core function needs.

Only install this with tight sandboxing and reduced permissions, and do not rely on its output for real chemical storage decisions. Treat results as a rough draft that must be checked against current SDSs, institutional EHS procedures, and qualified human review, especially for unknowns, mixtures, multi-hazard chemicals, toxics, cyanides, oxidizers, and any compatibility decision.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:48
Finding
Unsafe Fail-Open Chemical Classification Can Produce Hazardous Storage Guidance<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:48-75` **Vulnerability Type**: Unsafe fail-open classification and incomplete input validation **Risk Level**: High ### Complete Code Snippet ```python def classify_chemical(self, name): """Classify chemical into storage group.""" name_lower = name.lower() for group, data in self.COMPATIBILITY_GROUPS.items(): for example in data["examples"]: if example.lower() in name_lower: return group # Check keywords acid_keywords = ["acid", "hcl", "sulfuric", "nitric", "acetic"] base_keywords = ["hydroxide", "naoh", "koh", "ammonia", "amine"] flammable_keywords = ["ethanol", "methanol", "acetone", "ether", "hexane"] oxidizer_keywords = ["peroxide", "permanganate", "hypochlorite", "nitrate"] if any(k in name_lower for k in acid_keywords): return "acids" elif any(k in name_lower for k in base_keywords): return "bases" elif any(k in name_lower for k in flammable_keywords): return "flammables" elif any(k in name_lower for k in oxidizer_keywords): return "oxidizers" return "general" ``` ### Technical Analysis The classifier relies on case-insensitive substring matching and silently assigns every unrecognized chemical to the `general` storage group. In a safety-critical classification system, this is an unsafe fail-open behavior: absence of a recognized keyword is treated as evidence that a chemical is low risk. The implementation also returns the first matching group and therefore cannot represent multiple hazards or select the most restrictive applicable storage category. For example, nitric acid appears among the acid examples and is returned as `acids` before its oxidizing properties can be considered. Toxic-specific classification keywords are absent, meaning a name such as `Sodium cyanide` is not reliably assigned to `toxics` and can fall through to `general`. ...[truncated 1447 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the `general` fallback with an explicit `unknown` or `manual_review` classification. 2. Do not issue storage recommendations for unknown chemicals until identity and hazards have been verified. 3. Use authoritative SDS or CAS-based hazard data rather than chemical-name substrings as the primary classification source. 4. Represent all applicable hazards for a chemical and implement a documented rule for choosing the most restrictive storage category. 5. Add explicit toxic, reducing-agent, sulfide, cyanide, halogenated-compound, water-reactive, and pyrophoric classifications where supported. 6. Normalize names carefully and require exact aliases or structured identifiers rather than unrestricted substring matches. 7. Validate input type, reject empty names, and flag malformed or ambiguous values. 8. Add regression tests for sodium cyanide, nitric acid, mixtures, alternate names, typographical errors, unknown chemicals, and chemicals with multiple hazards. 9. Clearly state that automated results require confirmation against the current SDS and institutional EHS rules. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:77
Finding
Broad Group Matching Incorrectly Declares Chemical Pairs Compatible<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:77-94` **Vulnerability Type**: Unsafe compatibility authorization based on incomplete classifications **Risk Level**: High ### Complete Code Snippet ```python def check_compatibility(self, chemical1, chemical2): """Check if two chemicals can be stored together.""" group1 = self.classify_chemical(chemical1) group2 = self.classify_chemical(chemical2) if group1 == group2: return True, f"Same group ({group1})" # Check if group2 is in group1's incompatible list incompatibles = self.COMPATIBILITY_GROUPS[group1]["incompatible"] if group2 in incompatibles: return False, f"INCOMPATIBLE: {group1} cannot be stored with {group2}" # Check reverse incompatibles = self.COMPATIBILITY_GROUPS[group2]["incompatible"] if group1 in incompatibles: return False, f"INCOMPATIBLE: {group2} cannot be stored with {group1}" return True, "Compatible with precautions" ``` ### Technical Analysis The method discards compound identity after assigning each input to one broad category. It then unconditionally declares chemicals compatible whenever their broad groups match. Membership in the same general hazard category is not sufficient proof of chemical compatibility. The fallback also returns `True` whenever neither broad group explicitly lists the other as incompatible. This treats missing compatibility knowledge as affirmative compatibility rather than an unknown result. In addition, several values in the incompatibility matrix—such as `cyanides`, `sulfides`, `halogenated`, and `reducing`—are not possible output groups from `classify_chemical()`. Consequently, those matrix entries cannot directly match `group1` or `group2`, leaving intended incompatibility checks unreachable for relevant chemical names. ### Attack Path 1. An input provider selects two incompatible compounds that map to the same broad group, or supplies a compound ...[truncated 1134 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the unconditional `group1 == group2` compatibility approval. 2. Use three-state results: `compatible`, `incompatible`, and `unknown/manual review`. 3. Treat absent compatibility evidence as `unknown`, not as `True`. 4. Implement compound-specific incompatibility rules backed by authoritative SDS data. 5. Ensure every category referenced by the incompatibility matrix can be produced by the classifier. 6. Preserve chemical identity and all identified hazard classes throughout compatibility evaluation. 7. Require manual SDS/EHS review for mixtures, unknowns, multi-hazard chemicals, and pairs without an explicit validated rule. 8. Add tests covering incompatible compounds within the same broad group and every matrix category. 9. Include the basis and confidence level for each compatibility result so users can distinguish verified guidance from heuristic output. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:4
Finding
Skill Declares Unnecessary Command Execution and File Modification Permissions<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:4` **Vulnerability Type**: Excessive Agent tool permissions and violation of least privilege **Risk Level**: Medium ### Complete Code Snippet ```yaml allowed-tools: [Read, Write, Bash, Edit] ``` ### Technical Analysis The skill declares access to `Write`, `Edit`, and unrestricted `Bash`, although the shipped Python implementation only parses command-line strings, performs in-memory classification, and prints output. It does not require Agent-level arbitrary file modification or general shell execution to deliver its core behavior. Granting capabilities beyond the legitimate task creates an unnecessary privilege boundary expansion. The reviewed content does not itself exploit these permissions, but the declaration increases the consequences of future malicious modifications, prompt injection from surrounding content, or unsafe extensions. ### Attack Path 1. The skill is loaded with all declared tools available to the Agent. 2. Attacker-controlled instructions are introduced through a future skill modification or another untrusted context processed during the session. 3. Those instructions induce use of `Bash`, `Write`, or `Edit`. 4. The Agent can execute commands or modify files even though those actions are unnecessary for chemical classification. 5. The resulting impact is bounded by the runtime account, sandbox, and tool policy. The current reviewed files contain no command that performs this exploitation; this is an excessive-permission exposure rather than evidence of an embedded malicious payload. ### Impact Assessment If the excess capabilities are abused, the Agent may execute arbitrary commands available to its runtime identity and create, overwrite, or alter files accessible within the tool environment. Potential scope includes project files and other paths permitted by the sandbox. There is no evidence in the reviewed implementation of sandbox escape, privilege escalation to a d ...[truncated 169 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `Write` and `Edit`, because the current implementation does not modify files. 2. Remove unrestricted `Bash` if the platform can invoke the bundled Python entry point through a narrower execution interface. 3. If shell execution is unavoidable, allow only `scripts/main.py` with validated arguments and a fixed interpreter. 4. Retain `Read` only if documentation or inventory-file reading is an intentional, implemented requirement. 5. Enforce filesystem and process sandboxing outside the skill manifest rather than merely claiming it in documentation. 6. Deny network access and access to credentials, sensitive environment variables, and unrelated filesystem locations. 7. Reassess the permission list whenever functionality changes and document the necessity of each retained capability. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

Harmful Content Injection

Critical
Category
Prompt Injection
Content
demo_inventory = [
    "HCl (concentrated)",
    "NaOH pellets",
    "Ethanol",
    "Hydrogen peroxide",
    "Sodium cyanide",
    "PBS",
    "Acetone"
]

groups = sorter.sort_chemicals(demo_inventory)
Confidence
70% confidence
Finding
This content may contain harmful instructions that could cause physical harm if followed. CRITICAL: Review carefully before use.

Missing User Warnings

High
Confidence
96% confidence
Finding
This tool produces laboratory chemical storage classifications and compatibility guidance in a safety-critical domain, but it does not warn users that the logic is simplified, incomplete, and unsuitable as the sole basis for storage decisions. Because the classifier relies on substring matching and limited hard-coded groups, it can misclassify hazardous chemicals and create a false sense of safety, potentially leading to dangerous co-storage decisions.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This skill provides chemical classification and storage recommendations in a safety-critical domain, but the cited section lacks a prominent warning not to rely on generated output as the sole basis for handling unknown or incompletely identified chemicals. Overreliance on heuristic classifications in lab storage could cause incompatible storage decisions, leading to toxic release, fire, or other physical harm.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The skill’s Security Checklist and Risk Assessment claim there is no file system access, but the document elsewhere includes file-reading logic (`open(file_path, 'r')`) and describes generating output files. This mismatch can mislead operators, policy engines, or reviewers into granting broader trust than warranted, increasing the chance of unintended file access in real deployments.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
The related-skills section uses Chinese terms '上游' and '下游' alongside English, which introduces a locale-specific presentation choice without any user opt-in or explanation. Under the language policy, skills should avoid imposing a specific language or locale unless it is optional or clearly justified.

Intent-Code Divergence

Low
Confidence
75% confidence
Finding
The docstring says the function processes a chemical inventory from a text file with one chemical per line, but the implementation also computes hazard statistics and builds an incompatibility report before returning a structured analysis object. While related to the same workflow, the docstring understates what the function actually does.

Static analysis

No suspicious patterns detected.