Back to skill

Security audit

QR Password

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent and local-only, but it handles real passwords in ways that can expose them through logs, temporary files, browser state, or the displayed QR code.

Review this before installing. Use it only in physically controlled settings, avoid entering real passwords into shell commands or prompts, do not rely on stdout/tool history as a secret channel, delete generated images immediately, and prefer short-lived or one-time credentials until the skill enforces safer secret handling.

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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:23
Finding

Credential Exposure Through Shell History and Process Telemetry

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:23-24
Vulnerability Type: Plaintext credential exposure in command-line instructions
Risk Level: High

Vulnerable Code

bash
echo '{"username":"USER","password":"PASS","domain":"DOMAIN"}' | \
  python3 skills/qr-password/scripts/generate-qr.py /tmp/qr-out.png

Technical Analysis

The documented workflow instructs users or agents to place a plaintext password directly in a shell command. Although the credential is passed to the Python process through standard input, the command used to construct that input may be recorded in interactive shell history.

Depending on the execution environment, the complete command may also be retained by terminal recording, endpoint monitoring, audit infrastructure, agent tool-call history, or process execution telemetry. This contradicts the project's mandatory rule that credentials must never be logged.

Attack Path

  1. A user or agent follows the documented command and substitutes a real credential for PASS.
  2. The shell, agent runtime, terminal recorder, or endpoint monitoring system retains the command text.
  3. An attacker with access to the user's history file, audit records, support bundle, or execution trace searches for the QR-generation command.
  4. The attacker extracts the plaintext password and uses it against the associated username and domain.

Impact Assessment

An attacker who can read local shell history or execution telemetry can obtain the complete transferred credential. The vulnerability does not independently grant elevated system privileges, but the exposed credential may grant access to the associated external service with all privileges assigned to that account.

Remediation
View remediation

Remediation Suggestions

  • Do not include passwords directly in shell command text.
  • Add a secure interactive input mode based on Python's getpass.getpass().
  • Prefer direct vault integration or a protected file descriptor over command-line interpolation.
  • If structured input is required, read it from a temporary descriptor created with restrictive permissions and remove it immediately after use.
  • Warn users not to place credentials in shell history, agent prompts, or command arguments.
  • Review execution and audit logging policies to ensure standard input containing credentials is not captured.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/read-qr.py:34
Finding

Decoded Passwords Are Written to Standard Output

Content
View full analysis

Vulnerability Details

File Location: scripts/read-qr.py:34-35; equivalent behavior also exists at scripts/qr-format.py:30
Vulnerability Type: Plaintext sensitive-data disclosure through standard output
Risk Level: High

Vulnerable Code

scripts/read-qr.py:

python
cred = qr_format.decode(data)
print(json.dumps(cred))

scripts/qr-format.py:

python
else:
    print(json.dumps(decode(sys.stdin.read().strip())))

The returned dictionary includes the password:

python
return {"username": obj["u"], "password": obj["p"], "domain": obj["d"]}

Technical Analysis

Both decoding interfaces serialize the complete credential object, including the plaintext password, to standard output. Standard output is not a protected secret channel. It can be captured by terminal scrollback, shell redirection, agent tool results, CI logs, process supervisors, debugging tools, or centralized logging systems.

This implementation directly conflicts with the documented requirements to avoid logging credentials and to redact passwords from conversation output. In an agent environment, tool output may become part of the conversation or execution history even if the agent subsequently avoids repeating it.

Attack Path

  1. A credential-bearing QR image is supplied to read-qr.py, or a payload is passed to the decode mode of qr-format.py.
  2. The payload is decoded into a dictionary containing the username, password, and domain.
  3. The script serializes the complete dictionary to standard output.
  4. Terminal capture, an agent runtime, a process supervisor, or logging infrastructure retains the output.
  5. An attacker with access to those retained records extracts and uses the credential.

Impact Assessment

The issue exposes the complete decoded credential to every component capable of observing or retaining process output. The attacker receives no direct local privilege escalati ...[truncated 197 chars]

Remediation
View remediation

Remediation Suggestions

  • Do not print decoded passwords to standard output by default.
  • Return a reference to a protected secret object or place the secret directly into an approved credential consumer.
  • Redact passwords in all human-readable output, for example by returning "password": "****".
  • If raw secret transfer is unavoidable, require an explicit protected output descriptor and verify that it is not a terminal or ordinary log stream.
  • Remove or harden the standalone decode CLI in scripts/qr-format.py because it has the same disclosure behavior.
  • Add tests that fail if plaintext passwords appear on standard output or standard error.
  • Configure agent integrations so secret-bearing tool results are never added to conversation history.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate-qr.py:24
Finding

Predictable and Insecure Temporary Credential QR File

Content
View full analysis

Vulnerability Details

File Location: scripts/generate-qr.py:24-28
Vulnerability Type: Unsafe temporary file containing plaintext credentials
Risk Level: High

Vulnerable Code

python
out_path = sys.argv[1] if len(sys.argv) > 1 else os.path.join(tempfile.gettempdir(), "qr-password.png")

img = qrcode.make(payload, error_correction=qrcode.constants.ERROR_CORRECT_M)
img.save(out_path)
print(out_path)

Technical Analysis

When no output path is supplied, the script writes the QR image to a fixed, predictable filename in the system temporary directory. The image encodes the plaintext password and is therefore itself a sensitive credential artifact.

The script does not use exclusive file creation, enforce mode 0600, reject symbolic links, generate an unpredictable name, or remove the image after use. Cleanup is only described as a later manual instruction in SKILL.md; direct invocation, interruption, or failure can leave the credential image on disk.

In a shared temporary directory, an attacker may monitor the predictable path, read a stale image, or prepare a symbolic link before generation. File permissions depend on the process umask and library behavior rather than an explicit security guarantee.

Attack Path

  1. An attacker predicts the default path, such as /tmp/qr-password.png.
  2. The attacker monitors the path for creation or prepares a symbolic link at that location.
  3. A victim invokes the generator without a safe, unique output path.
  4. The QR image containing the plaintext credential is written to the predictable location or to the symbolic-link target.
  5. The attacker copies and decodes the image before manual cleanup, or retrieves it after an interrupted workflow.

Impact Assessment

A local attacker with access to the shared temporary directory may recover the username, password, and domain encoded in the image. A successful symbolic-link attack may also redirect ...[truncated 190 chars]

Remediation
View remediation

Remediation Suggestions

  • Prefer in-memory QR rendering and avoid writing credential images to disk.
  • If a file is necessary, use tempfile.mkstemp() or NamedTemporaryFile with an unpredictable name and mode 0600.
  • Use exclusive creation and reject symbolic links.
  • Validate user-provided output paths and avoid writing through pre-existing links.
  • Delete the generated image in a finally block after display or consumption.
  • Ensure abnormal termination and timeout handlers also trigger cleanup.
  • Avoid a reusable default filename and document the residual risk of filesystem snapshots and backups.

T09 · Insecure Skill Coding Practices

Error
Location
assets/qr-generator.html:187
Finding

Offline Generator Retains the Password and Credential QR Indefinitely

Content
View full analysis

Vulnerability Details

File Location: assets/qr-generator.html:187-221
Vulnerability Type: Persistent plaintext credential retention in the browser DOM and display
Risk Level: High

Vulnerable Code

javascript
const domain = document.getElementById('domain').value.trim();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;

// Create JSON payload
const payload = {
  v: 1,
  u: username,
  p: password
};

if (domain) {
  payload.d = domain;
}

const jsonString = JSON.stringify(payload);

// Clear previous QR code
qrCodeDiv.innerHTML = '';

// Generate new QR code
qrCode = new QRCode(qrCodeDiv, {
  text: jsonString,
  width: 200,
  height: 200,
  colorDark: '#1a1a2e',
  colorLight: '#ffffff',
  correctLevel: QRCode.CorrectLevel.M
});

// Show QR container
qrContainer.classList.add('visible');

// Clear password from form after generating (security)
// document.getElementById('password').value = '';

The embedded QR library also assigns the full payload to the generated element's title:

javascript
this._el.title=a

Technical Analysis

Password clearing is present only as commented-out code, so the plaintext password remains in the form after QR generation. The generated QR remains visible indefinitely because no timeout clears or hides it. This violates the mandatory instruction to clear the display after 30 seconds.

The QR library also stores the full JSON payload, including the password, in the container's title attribute. Consequently, the secret is retained in multiple forms: the password input, JavaScript variables, the visible QR image, and a DOM attribute.

Although no network transmission was identified, offline operation does not protect against local observation, shoulder surfing, browser automation, developer-tools inspection, or later access to an unattended devi ...[truncated 907 chars]

Remediation
View remediation

Remediation Suggestions

  • Immediately clear the password input after QR generation.
  • Implement a 30-second timeout that clears the QR drawing, removes the visible class, and deletes secret-bearing DOM attributes.
  • Modify the embedded QR library usage so the payload is never assigned to an element's title.
  • Clear the QR before generating a replacement and when the page loses visibility or is unloaded.
  • Avoid retaining credential payloads in global variables.
  • Add an explicit “Clear now” control for immediate manual removal.
  • Consider obscuring or clearing the username and domain alongside the password.
  • Add automated browser tests verifying that the password, QR, and title attribute are removed after expiration.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:57
Finding

Unpinned Third-Party Dependencies Installed from the Configured Package Index

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:57-61; the same command appears in README.md:21-24
Vulnerability Type: Unpinned and unhashed dependency installation
Risk Level: Medium

Vulnerable Code

markdown
## Dependencies

Python 3 with: `qrcode`, `Pillow`, `opencv-python-headless`

Install: `python3 -m pip install --user qrcode Pillow opencv-python-headless`

The README provides the same installation pattern:

bash
python3 -m pip install --user qrcode Pillow opencv-python-headless

Technical Analysis

The installation instructions do not pin package versions or require cryptographic hashes. The installed code therefore depends on whichever package releases and artifacts are resolved by the user's current package-index configuration at installation time.

This weakens reproducibility and exposes users to future compromised releases, malicious index configuration, dependency substitution, or an unintended breaking release. Package installation can execute build backends or installation-related code with the invoking user's privileges.

No evidence was found that the named packages are intentionally malicious. The vulnerability is the absence of dependency integrity and version controls.

Attack Path

  1. A user follows the documented installation command.
  2. The package resolver contacts the configured package index and selects current package versions without hash validation.
  3. An attacker compromises a resolved release, package-index configuration, mirror, or dependency artifact.
  4. The malicious package or build component executes during installation or is imported when the QR scripts run.
  5. The attacker obtains code execution with the privileges of the user running the installation or scripts.

Impact Assessment

Successful supply-chain compromise can execute arbitrary code under the installing user's account. This may expose credentials processed by the ski ...[truncated 195 chars]

Remediation
View remediation

Remediation Suggestions

  • Pin every direct dependency to a reviewed version.
  • Maintain a lock file containing transitive dependency versions.
  • Require cryptographic hashes, such as with pip install --require-hashes -r requirements.txt.
  • Install dependencies in a dedicated virtual environment rather than directly into the user environment.
  • Document and enforce the trusted package index.
  • Regularly scan pinned dependencies for known vulnerabilities and update them through a controlled review process.
  • Record the provenance and reviewed version of the embedded QRCode.js library.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · SKILL.md (reported line 33)May include surrounding context.

canvas present /tmp/qr-out.png

Wait 30s

canvas hide rm /tmp/qr-out.png

text

When reporting to user, say "QR displayed" — never echo the password.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
88% confidence
Finding

This deletion example operates on a placeholder path derived from the captured or user-provided image, which in real use could become variable or attacker-influenced. If an agent blindly substitutes an arbitrary path into rm, it could delete unintended files, especially if the path is malformed, points outside a safe workspace, or is interpreted unsafely by surrounding tooling.

Content

Scanner excerpt · SKILL.md (reported line 51)May include surrounding context.

md
3. Output is JSON: `{"username":"...","password":"...","domain":"..."}`
4. Use the credential (fill, copy, deliver) — **never echo password to chat**
5. Delete the image: `rm /path/to/image.png`

## Offline QR Generator

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README explicitly describes moving credentials between devices by rendering and scanning QR codes, but it provides no warning that the secret is exposed in plaintext to anyone who can view the screen, observe the camera feed, capture screenshots, or access saved images. In the context of a credential-handling skill, omitting these operational security warnings can lead users to deploy the workflow in unsafe environments and leak passwords during transfer.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This UI renders raw credentials into a scannable visual artifact without clearly warning that anyone who can see, photograph, screen-capture, or inspect the page can recover the secrets. In the context of credential handling, omission of explicit exposure warnings materially increases the chance of unsafe use and accidental disclosure.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The page claims credentials 'never leave this device,' but its core function is to encode those credentials into a QR code for transfer to another device. That misleading assurance can cause users to underestimate shoulder-surfing, camera capture, screenshots, or compromise of the receiving device during credential transfer.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.