Back to skill

Security audit

lgd-badge-verify

Security checks for vulnerabilities and agentic risk

Overview

This badge-verification skill is small and not visibly malicious, but its verifier can label self-consistent certificates as verified without authoritative registry, revocation, or evidence-hash validation.

Install only if you understand this as a lightweight integrity checker, not authoritative badge authentication unless you supply a trusted registry and separately validate revocation and evidence hashes. Prefer a pinned, reviewed source and local or isolated installation, and do not rely on verified:true alone for compliance, access, or trust decisions.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/badge_verify.py:50
Finding
Certificates Can Be Reported as Verified Without Authoritative Validation## Vulnerability Details **File Location**: `scripts/badge_verify.py`, lines 50-69 **Vulnerability Type**: Authentication bypass caused by optional trust-source validation **Risk Level**: High **Vulnerable Code**: ```python canon = json.dumps({k: cert[k] for k in REQUIRED[:-1]}, ensure_ascii=False, sort_keys=True) recomputed = sha256_of(canon) checks.append(("指纹防篡改", recomputed == cert.get("fingerprint"), f"重算={recomputed[:16]}… vs 证书={str(cert.get('fingerprint'))[:16]}…")) reg_ok, reg_note = None, "未提供台账,跳过对账" if a.registry: rp = pathlib.Path(a.registry) if rp.exists(): reg = json.loads(rp.read_text(encoding="utf-8")) hit = [x for x in reg.get("issued", []) if x.get("serial") == cert.get("serial")] reg_ok = bool(hit) and hit[0].get("fingerprint") == cert.get("fingerprint") reg_note = "台账命中且指纹一致" if reg_ok else "台账无此编号或指纹不一致(伪造/已吊销)" else: reg_ok = False reg_note = f"台账不存在: {a.registry}" if reg_ok is not None: checks.append(("台账对账", reg_ok, reg_note)) ok = all(c[1] for c in checks) ``` ### Technical Analysis The certificate fingerprint is an unkeyed SHA-256 digest calculated exclusively from certificate fields controlled by the party presenting the certificate. It proves only that the fields and fingerprint are internally consistent; it does not prove that a trusted issuer created the certificate. Registry validation is optional. When `--registry` is omitted, no authoritative check is added to `checks`, and `all(c[1] for c in checks)` can return `True` based solely on structural completeness and an attacker-generated fingerprint. The resulting output uses `verified: true` or an equivalent success message, which overstates the security property established by the code. ### Attack Path 1. An attacker creates arbitrary values for `badge`, `serial`, `holder`, `issuer`, `issued_at`, and `evidence_sha256`. 2. The attacker ...[truncated 717 chars]
Remediation
## Remediation Suggestions - Require a trusted registry for any result labeled `verified`. - If no registry is supplied, return a distinct result such as `integrity_valid_but_authenticity_unverified` and use a non-success exit status where authenticity is required. - Prefer issuer authentication through a digital signature verified with a pinned or otherwise trusted issuer public key. - Define a versioned canonicalization and signature format to prevent implementation differences. - Clearly distinguish integrity checks from issuer-authenticity checks in CLI output and documentation. - Add tests proving that a self-generated certificate cannot receive an authoritative verified status without a trusted registry or valid issuer signature.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/badge_verify.py:47
Finding
Documented Evidence-Hash and Revocation Validation Is Not Implemented## Vulnerability Details **File Location**: `scripts/badge_verify.py`, lines 47-62; documented claims also appear in `README.md`, line 3, and `SKILL.md`, line 32 **Vulnerability Type**: Incomplete security validation and misleading verification result **Risk Level**: Medium **Vulnerable Code**: ```python miss = [k for k in REQUIRED if k not in cert] checks.append(("结构完整", not miss, "缺失字段: " + ",".join(miss) if miss else "字段齐全")) canon = json.dumps({k: cert[k] for k in REQUIRED[:-1]}, ensure_ascii=False, sort_keys=True) recomputed = sha256_of(canon) checks.append(("指纹防篡改", recomputed == cert.get("fingerprint"), f"重算={recomputed[:16]}… vs 证书={str(cert.get('fingerprint'))[:16]}…")) reg_ok, reg_note = None, "未提供台账,跳过对账" if a.registry: rp = pathlib.Path(a.registry) if rp.exists(): reg = json.loads(rp.read_text(encoding="utf-8")) hit = [x for x in reg.get("issued", []) if x.get("serial") == cert.get("serial")] reg_ok = bool(hit) and hit[0].get("fingerprint") == cert.get("fingerprint") reg_note = "台账命中且指纹一致" if reg_ok else "台账无此编号或指纹不一致(伪造/已吊销)" ``` The relevant required-field declaration is: ```python REQUIRED = ["badge", "serial", "holder", "issuer", "issued_at", "evidence_sha256", "fingerprint"] ``` ### Technical Analysis The implementation checks only whether `evidence_sha256` is present. It does not verify that the value is a 64-character hexadecimal SHA-256 digest, nor does it hash any evidence and compare the resulting digest. Registry validation checks only whether the serial exists and whether the first matching entry has the same fingerprint. It does not inspect a revocation list, revocation timestamp, active-status field, or equivalent state. Consequently, a registry entry can contain a revocation marker and still pass if its fingerprint matches. These omissions conflict with the documented claims that the tool validates evidence-hash form ...[truncated 1043 chars]
Remediation
## Remediation Suggestions - Require `evidence_sha256` to be a string matching the exact format `^[0-9a-fA-F]{64}$`. - Normalize accepted digest case or define one required representation. - If evidence is locally available, calculate its SHA-256 digest and compare it with the certificate value. - Define a registry schema with explicit status information, such as `active`, `revoked`, and `expired`. - Reject entries with a revocation flag, revocation timestamp, or serial listed in a dedicated revocation collection. - Reject duplicate serial entries or define deterministic conflict handling rather than trusting the first match. - Validate registry and certificate documents against a documented schema. - Add tests for malformed hashes, revoked entries, duplicate serials, absent status fields, and mismatched evidence. - Update the documentation if any claimed verification property remains unsupported.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:79
Finding
Installation Instructions Use Unpinned Mutable Supply-Chain Sources## Vulnerability Details **File Location**: `SKILL.md`, lines 79-83 **Vulnerability Type**: Unpinned third-party package execution and mutable repository installation **Risk Level**: Medium **Vulnerable Code**: ```bash # One-command installation through the skills CLI npx skills add zhaoxinghua09-cell/agent-skills -g # Or manual installation from the repository git clone https://github.com/zhaoxinghua09-cell/agent-skills.git cp -r agent-skills/skills/lgd-badge-verify ~/.workbuddy/skills/ ``` ### Technical Analysis The `npx skills` command does not pin the CLI package to a reviewed version. Depending on the local environment and package-manager behavior, `npx` can retrieve and execute a current package release. The command also requests a global Skill installation from a mutable repository reference. The alternative `git clone` command retrieves the repository's current default branch rather than an immutable tag or commit. Therefore, the installed code can differ from the artifact reviewed in this audit. No evidence in the reviewed project establishes that these upstream sources are currently malicious. The risk arises because future compromise, package replacement, or ordinary upstream changes can alter the code executed or installed after review. ### Attack Path 1. A user follows the documented `npx` or `git clone` installation instructions. 2. The package registry account, CLI package, repository account, or default branch is compromised or changed. 3. The unpinned command retrieves content different from the audited artifact. 4. The CLI executes during installation or installs modified Skill instructions and scripts globally. 5. The modified content runs later with the permissions of the invoking user or Agent environment. ### Impact Assessment The reviewed commands do not themselves demonstrate privilege escalation. Retrieved code would generally obtain the invoking user's privileges and could affect ...[truncated 265 chars]
Remediation
## Remediation Suggestions - Pin the `skills` CLI to an exact reviewed version, such as an explicit package version. - Pin the Skill source to an immutable commit hash or signed release tag. - Publish cryptographic checksums or signed provenance for release artifacts. - Instruct users to verify the checksum or signature before installation. - Avoid global installation by default; prefer a project-local or isolated installation. - Document the exact package, repository commit, and expected file hashes corresponding to each Skill release. - Use dependency lock files or equivalent reproducible installation controls where supported.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/badge_verify.py:44
Finding
Malformed Certificates and Registries Can Trigger Unhandled Exceptions## Vulnerability Details **File Location**: `scripts/badge_verify.py`, lines 44-60 **Vulnerability Type**: Insufficient input validation and exception handling **Risk Level**: Low **Vulnerable Code**: ```python cert = load_cert(a.cert) checks = [] miss = [k for k in REQUIRED if k not in cert] checks.append(("结构完整", not miss, "缺失字段: " + ",".join(miss) if miss else "字段齐全")) canon = json.dumps({k: cert[k] for k in REQUIRED[:-1]}, ensure_ascii=False, sort_keys=True) recomputed = sha256_of(canon) checks.append(("指纹防篡改", recomputed == cert.get("fingerprint"), f"重算={recomputed[:16]}… vs 证书={str(cert.get('fingerprint'))[:16]}…")) reg_ok, reg_note = None, "未提供台账,跳过对账" if a.registry: rp = pathlib.Path(a.registry) if rp.exists(): reg = json.loads(rp.read_text(encoding="utf-8")) hit = [x for x in reg.get("issued", []) if x.get("serial") == cert.get("serial")] ``` ### Technical Analysis The code records missing certificate fields but continues immediately into a dictionary comprehension that directly indexes every required non-fingerprint field. A missing field therefore raises `KeyError` before the program can return its intended structured failure result. The parsed certificate is not verified to be a JSON object. JSON arrays, strings, numbers, or `null` can cause type-related exceptions when membership checks or `.get()` operations are performed. Registry file reading and JSON parsing are also not protected. Invalid UTF-8, malformed JSON, permission errors, directories supplied as file paths, unexpected registry root types, or non-object entries in `issued` can produce uncaught exceptions and raw tracebacks. This conflicts with the documented behavior that failures return readable errors without exposing raw stack frames. ### Attack Path 1. An attacker or external caller supplies a certificate missing a required field, or supplies valid JSON with a non-object root. 2. T ...[truncated 778 chars]
Remediation
## Remediation Suggestions - Verify that the certificate root is a JSON object before accessing fields. - If required fields are missing, emit the structured failure result and stop before fingerprint calculation. - Validate expected field types and reasonable size limits. - Catch `OSError`, `UnicodeError`, `json.JSONDecodeError`, `TypeError`, and schema-validation failures around certificate and registry processing. - Verify that the registry root is an object, `issued` is a list, and every list element is an object. - Return the documented exit code `2` for malformed input or environment errors. - Emit sanitized error messages without raw tracebacks during normal CLI operation. - Add negative tests for missing fields, non-object JSON roots, malformed registry JSON, unreadable files, and invalid registry entries.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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 (5)

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Verify a badge certificate: recompute fingerprint (anti-tamper) + evidence hash format + registry cross-check (serial exists & not revoked).

**Pain point**: Issuance without verification is forgeable: the loop lacked reverse checking and the trust chain broke.

Part of the **LGD moat loop**: Passport (registered) → Evidence chain (evidenced) → Gate (gated) → **Badge (issued/verified)**.
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill describes file-based verification behavior and references local scripts, but it does not declare any explicit tool scope such as permissions or allowed-tools. In an agent ecosystem, this can cause the host or operator to grant broader file-read access than necessary, increasing the chance of unintended access to local files during execution.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The markdown content presents the skill instructions, warnings, and usage guidance only in Chinese, which can impose a fixed language on users without explicit opt-in. The policy allows locale constraints when documented and justified, but this file does not state that the skill is Chinese-only or provide an alternative language path.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
The installation instructions use 'npx skills add' without pinning a specific package version. This creates a supply-chain risk because the resolved package can change over time or be replaced by a compromised release, leading users to fetch and execute unreviewed code or tooling.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The category value uses Chinese-only wording ("AI 治理"), which signals a fixed language choice in user-facing metadata. The manifest does not offer any language/locale alternative or document why the skill is intentionally region- or language-specific.

Static analysis

No suspicious patterns detected.