Back to skill

Security audit

quantum-sim

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent local quantum simulator, but documented inputs can consume extreme memory or CPU, so it should be reviewed before installation.

Install only if you are comfortable reviewing or constraining local simulations. Avoid high qubit counts and very large shot counts unless the script is fixed or run inside a memory- and CPU-limited environment.

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
scripts/quantum_sim.py:104
Finding

Dense gate construction permits severe memory-exhaustion denial of service

Content
View full analysis
20: raise ValueError("Qubit count must be 1-20") self.n = n_qubits self.dim = 1 << n_qubits # 2^n self.state = _zeros(self.dim) self.state[0] = 1+0j self.log: List[str] = [] def _apply_single(self, gate_mat, qubit: int): """Apply a single-qubit gate to the statevector.""" n = self.n if _USE_NUMPY: op = np.array([[1+0j]]) for i in range(n-1, -1, -1): if i == qubit: op = np.kron(op, np.array(gate_mat, dtype=complex)) else: op = np.kron(op, np.eye(2, dtype=complex)) self.state = op @ self.state else: op = [[1+0j]] for i in range(n-1, -1, -1): if i == qubit: op = _kron(op, gate_mat) else: op = _kron(op, _I2) self.state = _matmul_vec(op, self.state) ``` ### Technical Analysis The constructor accepts up to 20 qubits, for which the statevector contains `2^20` amplitudes. However, applying any single-qubit gate does not update those amplitudes directly. Instead, `_apply_single` constructs a complete dense `2^n × 2^n` operator through repeated Kronecker products. At 20 qubits, the resulting matrix contains `2^40` complex values. With NumPy `complex128` values, the final matrix alone would require approximately 16 TiB of memory, excluding temporary arrays produced by repeated `np.kron` operations. The pure-Python fallback has even greater object and list overhead. This behavior conflicts with the documented implication that a 20-qubit simulation ...[truncated 1780 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/quantum_sim.py:190
Finding

Unbounded measurement sampling enables CPU-exhaustion denial of service

Content
View full analysis
Dict[str, int]: probs = self.probabilities() counts: Dict[str, int] = {} for _ in range(shots): r = random.random() cumulative = 0.0 chosen = self.dim - 1 for i, p in enumerate(probs): cumulative += p if r < cumulative: chosen = i break bitstr = format(chosen, f'0{self.n}b') counts[bitstr] = counts.get(bitstr, 0) + 1 return dict(sorted(counts.items())) ``` ```python def print_results(qc: QuantumCircuit, shots: int, json_out: bool, label: str = ''): probs = qc.probabilities() counts = qc.measure(shots) sv = qc.statevector() ``` ```python parser.add_argument('--shots', type=int, default=1024) parser.add_argument('--json', action='store_true') parser.add_argument('--list-presets', action='store_true') parser.add_argument('--statevector-only', action='store_true') ``` ### Technical Analysis The `--shots` argument accepts an arbitrary Python integer without a positive lower bound or safe upper bound. `measure` performs an outer loop once per requested shot. For every shot, it may scan the complete probability array containing `2^n` entries. Its worst-case time complexity is therefore `O(shots × 2^n)`. A caller can request an extremely large number of shots and force the process to consume CPU for an impractical duration. Large circuits amplify the cost of each shot. The documented `--statevector-only` option is intended to skip measurement simulation, but the parsed value is never inspected. `print_results` always invokes `qc.measure(shots)`. Consequently, callers cannot use the advertised option to avoid this expensive operation. Neg ...[truncated 1999 chars]
Remediation
View remediation
MAX_SHOTS: parser.error(f"--shots must be between 1 and {MAX_SHOTS}") ``` 2. Implement the documented `--statevector-only` behavior. Pass the option to the output function and do not call `measure` when it is enabled: ```python def print_results(qc, shots, json_out, label='', statevector_only=False): probs = qc.probabilities() counts = {} if statevector_only else qc.measure(shots) sv = qc.statevector() ``` 3. Improve sampling complexity. Build a cumulative probability distribution once and use binary search for each random value, reducing sampling from worst-case `O(shots × 2^n)` to approximately `O(2^n + shots × n)`. If NumPy is available, use a bounded vectorized sampling operation. 4. Apply a combined workload budget based on both the number of shots and the state dimension rather than validating each independently. 5. Add execution timeouts and CPU limits at the hosting-process or container level as defense in depth. 6. Add tests confirming that excessive and negative shot counts are rejected and that `--statevector-only` never invokes measurement. ]]>
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (3)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill advertises trigger phrases including broad, common terms such as "qubit" and "superposition," which may appear in general educational or conceptual discussions that do not actually request execution of this simulator. Overly broad activation can cause unintended invocation, leading to confusing behavior, incorrect tool routing, and increased exposure of any downstream tool functionality beyond the user's intent.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/quantum_sim.py (reported line 288)May include surrounding context.

python
elif qc is None:
            raise ValueError("First instruction must be: qubits N")
        elif op in ('h','x','y','z','s','t','sdg','tdg'):
            getattr(qc, op)(int(parts[1]))
        elif op in ('rx','ry','rz','p'):
            getattr(qc, op)(float(parts[1]), int(parts[2]))
        elif op in ('cx','cnot'):

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/quantum_sim.py (reported line 290)May include surrounding context.

python
elif op in ('h','x','y','z','s','t','sdg','tdg'):
            getattr(qc, op)(int(parts[1]))
        elif op in ('rx','ry','rz','p'):
            getattr(qc, op)(float(parts[1]), int(parts[2]))
        elif op in ('cx','cnot'):
            qc.cx(int(parts[1]), int(parts[2]))
        elif op == 'cz':

Static analysis

No suspicious patterns detected.