Back to skill

Security audit

Evez Rqns

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a local anomaly-detection demo, but it includes an unsafe model-loading path that can execute attacker-controlled code if a local pickle file is present.

Review before installing. The skill does not show exfiltration, persistence hooks, or deceptive instructions, but it should not be run in directories where an untrusted data/qtable_pretrained.pkl could exist. Prefer replacing pickle with a non-executable model format and treating the event log and sensor output as demo-quality rather than tamper-evident security telemetry.

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

Error
Location
rqns_agent.py:31
Finding

Unsafe Pickle Deserialization Enables Arbitrary Code Execution

Content
View full analysis

Vulnerability Details

File Location: rqns_agent.py:31-34
Vulnerability Type: Unsafe deserialization
Risk Level: High

Vulnerable Code

python
if Path("data/qtable_pretrained.pkl").exists():
    import pickle
    with open("data/qtable_pretrained.pkl", "rb") as f:
        self.Q = pickle.load(f)

Technical Analysis

pickle.load() is capable of invoking attacker-selected Python callables while reconstructing serialized objects. It must therefore never be used on files whose provenance and integrity are not guaranteed.

The file path is relative to the process's current working directory rather than a trusted package directory. An attacker who can create or replace data/qtable_pretrained.pkl in that directory can supply a malicious pickle. The payload is automatically deserialized when ContextualBanditAgent is instantiated, without authenticity verification, schema validation, or type validation.

Attack Path

  1. The attacker obtains write access to the application's working directory or otherwise controls the directory from which the application is launched.
  2. The attacker creates data/qtable_pretrained.pkl containing a malicious pickle whose reconstruction method executes a selected command or Python callable.
  3. The application creates ConcreteRQNSPipeline, which initializes ContextualBanditAgent.
  4. The existence check succeeds and pickle.load(f) deserializes the malicious object.
  5. The embedded operation executes with the privileges and environmental access of the Python process.

Impact Assessment

Successful exploitation provides arbitrary code execution under the account running the application. The attacker could read or modify files available to that account, access process environment data, alter application behavior, invoke local programs, or establish additional persistence where operating-system permissions permit. The scope is limited by the process's pr ...[truncated 32 chars]

Remediation
View remediation

Remediation Suggestions

  • Replace pickle with a non-executable serialization format, such as JSON, or use a NumPy format loaded with allow_pickle=False.
  • Resolve the model path relative to a trusted, read-only package or configuration directory rather than the current working directory.
  • Validate the loaded object's dimensions, numeric data type, finite values, and maximum permitted size before assigning it to self.Q.
  • Protect model artifacts with deployment-time integrity controls, such as a cryptographic signature or a hash stored in trusted configuration.
  • Restrict file ownership and write permissions so that untrusted users cannot replace model artifacts.
  • If legacy pickle support is unavoidable, migrate trusted data offline rather than deserializing arbitrary pickle files in the live application. A custom restricted unpickler reduces some risks but is not preferable to eliminating pickle.

T09 · Insecure Skill Coding Practices

Warning
Location
interfaces.py:104
Finding

Mutable Event Log and Incomplete Hash Chain Allow Undetected Audit-History Tampering

Content
View full analysis

Vulnerability Details

File Location: interfaces.py:104-121
Vulnerability Type: Audit-log integrity failure
Risk Level: Medium

Vulnerable Code

python
class EventSpine:
    """The immutable event log. History IS state."""
    
    def __init__(self):
        self.events: List[SpineEvent] = []
        self._last_hash = "genesis"
    
    def append(self, domain: str, event_type: str, payload: Dict[str, Any]) -> SpineEvent:
        import hashlib
        event = SpineEvent(
            domain=domain,
            event_type=event_type,
            payload=payload,
            hash_prev=self._last_hash
        )
        # Chain integrity: hash of (prev_hash + event_id + timestamp)
        chain_input = f"{self._last_hash}:{event.event_id}:{event.timestamp}"
        self._last_hash = hashlib.sha256(chain_input.encode()).hexdigest()[:16]
        event.hash_prev = self._last_hash
        self.events.append(event)
        return event

Technical Analysis

Although the event log is described as immutable, its events list is public, each SpineEvent remains mutable, and each event's payload is a mutable dictionary. Any code holding a reference to the spine, an event, or a supplied payload can modify or delete recorded history.

The hash calculation does not cover domain, event_type, or payload, so changing the security-relevant contents does not invalidate the calculated chain value. In addition, hash_prev is initially assigned the previous digest and then overwritten with the newly calculated digest, meaning it no longer records the prior link as its name implies. Only 16 hexadecimal characters of the SHA-256 digest are retained, and no chain-verification routine exists.

Consequently, the implementation does not provide the claimed append-only or tamper-evident guarantees.

Attack Path

  1. Attacker-controlled code obtains a reference to the exposed pipeline ...[truncated 1107 chars]
Remediation
View remediation

Remediation Suggestions

  • Make the internal event collection private and expose only immutable snapshots, iterators, or defensive copies.
  • Define immutable event records, for example with a frozen dataclass, and recursively freeze or canonicalize payload data.
  • Deep-copy caller-provided payloads before storing them to prevent post-append mutation through shared references.
  • Store previous_hash and event_hash as separate fields; do not overwrite the previous-link value.
  • Compute each event hash over the complete canonical representation of the previous hash, event ID, timestamp, domain, event type, and payload.
  • Retain the full cryptographic digest rather than truncating it to 16 hexadecimal characters.
  • Implement verification that reconstructs and validates the complete chain, including ordering and the genesis value.
  • Prevent callers from appending directly to the underlying collection.
  • If durable audit guarantees are required, write events to access-controlled append-only storage and use authenticated signing or an HMAC whose key is not available to ordinary event writers.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding

Suspicious Unicode normalization or mixed-script content

Content

No source excerpt is available for this finding.

Insecure deserialization: pickle.load()

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

Using pickle.load() on a file from disk is unsafe because pickle deserialization can execute attacker-controlled code during loading. If an attacker can replace or influence data/qtable_pretrained.pkl, instantiating this agent can lead to arbitrary code execution, which is especially dangerous in an automated agent pipeline.

Content

Scanner excerpt · rqns_agent.py (reported line 34)May include surrounding context.

python
if Path("data/qtable_pretrained.pkl").exists():
            import pickle
            with open("data/qtable_pretrained.pkl", "rb") as f:
                self.Q = pickle.load(f)
        
        # Dynamic thresholds (hot-swapped by PatchPipeline)
        self.delegation_thresholds = {

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code silently discards non-list raw_data and substitutes synthetic random input, causing the detection result to no longer reflect the actual signal being analyzed. In a security sensing context this can mask real anomalies, produce arbitrary classifications, and undermine trust in downstream automation because malformed or unexpected inputs degrade into plausible-looking but fabricated results instead of failing safely.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstring asserts that every cycle is logged to an append-only spine and that the agent hot-swaps its own thresholds based on cumulative learning. In this file, the code only drives a pipeline, prints results, and later reads from pipeline.spine.events; no append-only enforcement or threshold modification logic appears here, so the documentation overstates what the file itself does.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The docstring for export_q says it exports the Q-table for persistence and specifies an 'append-only' behavior, implying storage-side side effects or persistence semantics. In reality, the method only returns json.dumps(self.Q.tolist()) and does not write, append, or persist anything, so the documentation actively misstates what the code does.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.