Back to skill

Security audit

LYGO-MINT Operator Suite (v2)

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly performs local pack hashing and receipt generation, but it needs review because some scripts can write outside the intended workspace, follow symlinks in packs, and run unverified local helper code if invoked.

Review before installing. Run it only on trusted pack folders, avoid elevated privileges, check where state/reference outputs will be written in your environment, and avoid the legacy mint_pack_local.py wrapper unless the external tools it calls are known and pinned. Treat generated ledgers as mutable local records, not independent proof by themselves.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/mint_pack_v2.py:29
Finding
Incorrect Workspace Root Resolution Causes Out-of-Project Filesystem Writes<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/mint_pack_v2.py:29-32, 149-180` - `scripts/mint_pack_local.py:25-27, 101-110` - `scripts/backfill_anchors.py:13-14, 37-39` - `scripts/make_anchor_snippet.py:13-14, 29-30` **Vulnerability Type**: Incorrect path trust boundary and unauthorized filesystem access **Risk Level**: High ### Vulnerable Code From `scripts/mint_pack_v2.py`: ```python WS = Path(__file__).resolve().parents[4] # .../workspace STATE_DIR = WS / "state" REF_DIR = WS / "reference" OUT_DIR = REF_DIR / "minted_v2" ``` ```python # write outputs STATE_DIR.mkdir(parents=True, exist_ok=True) OUT_DIR.mkdir(parents=True, exist_ok=True) manifest_path = OUT_DIR / f"{pack_sha}_manifest.json" manifest_path.write_text(canon_manifest, encoding="utf-8") ``` ```python ledger_path = STATE_DIR / "lygo_mint_v2_ledger.jsonl" with ledger_path.open("a", encoding="utf-8") as f: f.write(json.dumps(record, ensure_ascii=False) + "\n") canon_path = STATE_DIR / "lygo_mint_v2_ledger_canonical.json" canon_map = load_json(canon_path, {}) canon_map[pack_sha] = record canon_path.write_text(json.dumps(canon_map, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") ``` From `scripts/mint_pack_local.py`: ```python ROOT = Path(__file__).resolve().parents[4] # workspace root LEDGER = ROOT / "state" / "lygo_mint_ledger.jsonl" CANON = ROOT / "state" / "lygo_mint_ledger_canonical.json" ``` ```python LEDGER.parent.mkdir(parents=True, exist_ok=True) with LEDGER.open("a", encoding="utf-8") as f: f.write(json.dumps(minted, ensure_ascii=False) + "\n") ``` From `scripts/backfill_anchors.py`: ```python ROOT = Path(__file__).resolve().parents[4] # workspace root LEDGER = ROOT / "state" / "lygo_mint_ledger.jsonl" ``` ```python LEDGER.parent.mkdir(parents=True, exist_ok=True) with LEDGER.open("a", encoding="utf-8") as f: f.write(json.dumps(rec, ensure_ascii=False) + "\n") ``` From `scripts/make_anchor_snippet.py`: ```python ROOT = Path(__file__).re ...[truncated 2097 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace fixed-depth parent traversal with a project-relative root appropriate to the packaged layout: ```python ROOT = Path(__file__).resolve().parents[1] ``` 2. Prefer an explicit `--workspace` argument or a narrowly defined environment variable when integration with an external workspace is required. 3. Resolve the selected workspace strictly and validate every output path before writing: ```python workspace = Path(args.workspace).expanduser().resolve(strict=True) destination = (workspace / "state" / "lygo_mint_v2_ledger.jsonl").resolve() destination.relative_to(workspace) ``` 4. Reject output destinations that escape the authorized workspace. 5. Use atomic replacement for canonical JSON files by writing to a temporary file in the same directory and then calling `Path.replace()`. 6. Add installation-layout tests confirming that all generated files remain under the selected workspace when the Skill is placed at different directory depths. 7. Document every filesystem destination and avoid creating root-level directories implicitly. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/mint_pack_local.py:35
Finding
Legacy Mint Wrapper Executes Unverified External Python Tools<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mint_pack_local.py:35-36, 72-78, 108-110` **Vulnerability Type**: External local-tool substitution and arbitrary code execution **Risk Level**: High ### Vulnerable Code ```python def run_py(path: Path, args: list[str]) -> subprocess.CompletedProcess: return subprocess.run([sys.executable, str(path), *args], cwd=str(ROOT), capture_output=True, text=True) ``` ```python # Mint using existing tool mint_tool = ROOT / "tools" / "lygo_mint" / "mint_pack.py" if not mint_tool.exists(): raise SystemExit(f"Missing mint tool: {mint_tool}") # Expect mint tool to output JSON or text; we treat stdout as the minted record if JSON. # Workspace mint tool expects positional pack_path proc = run_py(mint_tool, [str(pack_path), "--version", args.version, "--champion", args.champion or "", "--anchor", args.anchor or ""]) ``` ```python # Canonicalize ledger canon_tool = ROOT / "tools" / "lygo_mint" / "canonicalize_ledger.py" if canon_tool.exists(): _ = run_py(canon_tool, []) ``` ### Technical Analysis The wrapper executes `mint_pack.py` and `canonicalize_ledger.py` from an external `tools/lygo_mint` directory that is not part of the audited Skill package. It validates only whether the files exist; it does not verify ownership, permissions, a pinned cryptographic digest, or an authenticated package version. As a result, the effective code executed by the Skill can change independently after review. Any actor who can create or replace one of the expected files can cause arbitrary Python code to run with the operator's privileges. The invocation uses a subprocess argument array and does not enable a shell, so direct shell-metacharacter injection through command-line arguments was not identified. The vulnerability instead arises from trusting the executable script path. The incorrect root calculation compounds the issue in the audited layout by resolving the expected tools under `/tools/lygo_mint/`. ### ...[truncated 1130 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the legacy wrapper if the v2 implementation supersedes it. 2. Package all required minting and canonicalization logic inside the reviewed Skill rather than executing mutable workspace scripts. 3. If external tools are unavoidable, require an explicit operator-supplied path rather than silently deriving one. 4. Verify a pinned SHA-256 digest or an authenticated signature before executing each external tool. 5. Confirm that the tool is a regular file, is not a symlink, and is owned by an expected user with non-writable permissions for untrusted principals. 6. Correct the workspace-root calculation before constructing any tool path. 7. Clearly disclose external code execution in `SKILL.md`, including the exact paths, trust assumptions, and inherited privileges. 8. Add tests that substitute a modified tool and confirm that execution is rejected. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/bundle_pack_v2.py:35
Finding
Pack Processing Follows File Symlinks Outside the Selected Directory<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/bundle_pack_v2.py:35-54` - `scripts/mint_pack_v2.py:72-93` **Vulnerability Type**: Symlink traversal and out-of-scope file disclosure **Risk Level**: High ### Vulnerable Code From `scripts/bundle_pack_v2.py`: ```python files = [] for dirpath, _, filenames in os.walk(src): for fn in filenames: fp = Path(dirpath) / fn if fn in {".DS_Store"}: continue if "__pycache__" in fp.parts: continue files.append(fp) files.sort(key=lambda x: relpath_posix(src, x)) with zipfile.ZipFile(outp, "w", compression=zipfile.ZIP_DEFLATED) as z: for fp in files: arc = relpath_posix(src, fp) zi = zipfile.ZipInfo(arc, date_time=FIXED_DT) zi.compress_type = zipfile.ZIP_DEFLATED data = fp.read_bytes() z.writestr(zi, data) ``` From `scripts/mint_pack_v2.py`: ```python def list_files(input_path: Path) -> Tuple[Path, List[Path]]: if input_path.is_file(): root = input_path.parent return root, [input_path] root = input_path files: List[Path] = [] for dirpath, _, filenames in os.walk(root): for fn in filenames: fp = Path(dirpath) / fn # skip common noise if fn in {".DS_Store"}: continue if "__pycache__" in fp.parts: continue files.append(fp) files.sort(key=lambda x: relpath_posix(root, x)) return root, files ``` ```python def hash_file(root: Path, p: Path) -> Dict: raw = p.read_bytes() ``` ### Technical Analysis Neither pack traversal implementation rejects symbolic links or confirms that each resolved file remains beneath the selected input root. Although `os.walk()` does not follow symlinked directories by default, it can enumerate a symbolic link that appears as a file. Calling `Path.read_bytes()` then follows the link and reads the target. A pack can therefore contain a path ...[truncated 1649 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject all symbolic links unless link support is an explicit, securely designed feature: ```python if fp.is_symlink(): raise ValueError(f"Symlinks are not permitted: {fp}") ``` 2. Resolve the pack root strictly and verify every candidate's containment: ```python root_resolved = src.resolve(strict=True) candidate = fp.resolve(strict=True) candidate.relative_to(root_resolved) ``` 3. Verify that each candidate is a regular file before reading it. 4. Where supported, open files using descriptor-level protections such as `O_NOFOLLOW` to reduce time-of-check/time-of-use symlink races. 5. Perform checks immediately before reading each file. 6. Apply the same secure file-collection routine to minting, verification, and bundling so all commands process an identical file set. 7. Add regression tests containing file and directory symlinks that point both inside and outside the pack. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verify_pack_v2.py:30
Finding
Verifier Cannot Reproduce Hashes Using the Documented Workflow<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/mint_pack_v2.py:117-134` - `scripts/verify_pack_v2.py:30-57` - `SKILL.md:35-36` **Vulnerability Type**: Integrity-verification design inconsistency **Risk Level**: Medium ### Vulnerable Code and Documentation The documented command in `SKILL.md` does not provide hash-critical metadata: ```text ### 2) Verify a pack against an anchor snippet or a known hash - `python scripts/verify_pack_v2.py --input ./some_pack_folder --pack-sha256 <hash>` ``` Minting requires and hashes title, version, and author: ```python ap.add_argument("--title", required=True) ap.add_argument("--version", required=True, help="YYYY-MM-DD.vX") ap.add_argument("--author", default="DeepSeekOracle") ``` ```python manifest = { "lygo_mint": {"v": 2, "canon": CANON_RULESET}, "meta": { "title": args.title, "version": args.version, "author": args.author, }, "tags": [t.strip() for t in args.tags.split(",") if t.strip()], "files": file_records, } canon_manifest = canonical_manifest(manifest) pack_sha = sha256_text(canon_manifest) ``` Verification substitutes `None` for omitted metadata: ```python ap.add_argument("--title", default="") ap.add_argument("--version", default="") ap.add_argument("--author", default="") ap.add_argument("--tags", default="LYGO,Δ9Council") ``` ```python manifest = { "lygo_mint": {"v": 2, "canon": CANON_RULESET}, "meta": {"title": args.title or None, "version": args.version or None, "author": args.author or None}, "tags": [t.strip() for t in (args.tags or "").split(",") if t.strip()], "files": file_records, } canon = canonical_manifest(manifest) got = sha256_text(canon) ok = (got.lower() == args.pack_sha256.lower()) ``` ### Technical Analysis The pack hash covers metadata in addition to file records. A pack minted with a required title and version and the default author therefore cannot normally be reproduced by the documented verification ...[truncated 1459 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Distribute the canonical manifest alongside every pack. 2. Change verification to load the manifest, verify its own canonical SHA-256, and then compare every listed file path, size, canonicalization rule, and digest. 3. Alternatively, make every hash-critical metadata field mandatory in the verifier and include those fields in the documented command. 4. Implement actual anchor-snippet parsing if verification against snippets remains a documented feature. 5. Reject malformed expected hashes before comparison. 6. Report missing, additional, and first-differing files as described in the whitepaper. 7. Add an automated round-trip test that: - Mints a sample pack. - Captures its manifest and hash. - Runs the documented verification interface. - Confirms a successful exit status. 8. Add negative tests for modified files, modified metadata, added files, and removed files. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/backfill_anchors.py:23
Finding
Anchor Backfill Allows Semantically Invalid Ledger Records<![CDATA[ ## Vulnerability Details **File Location**: `scripts/backfill_anchors.py:23-39` **Vulnerability Type**: Missing input and ledger-reference validation **Risk Level**: Low ### Vulnerable Code ```python ap = argparse.ArgumentParser() ap.add_argument("--hash", required=True) ap.add_argument("--channel", required=True, help="moltbook|moltx|discord|4claw|x") ap.add_argument("--id", required=True, help="post id or url") args = ap.parse_args() rec = { "ts": utc_now(), "kind": "anchor_update", "hash": args.hash, "channel": args.channel, "anchor_id": args.id, } LEDGER.parent.mkdir(parents=True, exist_ok=True) with LEDGER.open("a", encoding="utf-8") as f: f.write(json.dumps(rec, ensure_ascii=False) + "\n") ``` ### Technical Analysis The command accepts arbitrary strings for the pack hash, channel, and anchor identifier. The channel list appears only in help text and is not enforced with `choices`. The script also does not validate the documented 64-character hexadecimal hash format or confirm that the referenced pack exists in the canonical ledger. JSON serialization prevents raw newline injection from creating additional JSONL records, but it does not prevent semantic ledger poisoning. A caller can append a syntactically valid record that associates a nonexistent pack with an unsupported channel or misleading URL. Because the ledger is described as an append-only audit trail, invalid entries cannot be cleanly removed without violating that model. ### Attack Path 1. A caller invokes the backfill script with an arbitrary hash, channel, and identifier. 2. The script performs no format, membership, or existence checks. 3. It serializes the supplied values as a valid JSON object. 4. The object is appended to the audit ledger. 5. Downstream tooling or operators may treat the invalid association as a legitimate anchor update. ### Impact Assessment The issue does not grant operating-system privileges. Its scope is the integrity and r ...[truncated 295 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce a strict SHA-256 format: ```python import re if not re.fullmatch(r"[0-9a-fA-F]{64}", args.hash): raise SystemExit("Invalid SHA-256 hash") ``` 2. Enforce the supported channel list through `argparse`: ```python ap.add_argument( "--channel", required=True, choices=["moltbook", "moltx", "discord", "4claw", "x"], ) ``` 3. Load the canonical ledger and reject updates for hashes that do not correspond to an existing minted pack. 4. Define platform-specific validation for post IDs and URLs. 5. For URLs, permit only expected schemes such as HTTPS and reject control characters. 6. Include an explicit record schema version and validate every record before append. 7. Use file locking if concurrent writers are supported to prevent interleaved or corrupted JSONL writes. 8. Provide a separate append-only correction event for erroneous updates rather than silently accepting malformed data. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents a broad operator suite with multiple core functions: pack canonicalization, hashing, dual-ledger output, anchor snippet generation, and pack verification. The supplied code chunk does not implement those functions. Instead, it is a single-purpose backfill tool that appends an `anchor_update` entry to `state/lygo_mint_ledger.jsonl` using CLI-provided values. While append-only ledger behavior is loosely consistent with part of the description, the actual primary purpose is materially narrower and different from the declared suite capabilities. Therefore, the description does not accurately represent this code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description presents a broad, advanced LYGO-MINT suite with multiple trust/anchoring functions: canonicalization, hashing, ledger production, anchor snippet generation, and verification. The supplied code chunk performs only one narrow task: creating a deterministic-style zip bundle from a directory by sorting files and fixing zip timestamps. While this may support part of a bundling/canonicalization workflow, it does not implement most of the declared capabilities and its primary behavior is substantially narrower than the description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a broad operator suite with multiple core capabilities: pack canonicalization, hash generation, ledger writing, machine-readable snippet generation, and third-party verification. The supplied code only implements a narrow subset: it parses CLI arguments, optionally reads a canonical ledger JSON file, extracts matching metadata for a provided hash, and prints a portable text anchor snippet template. It does not create or update ledgers, does not compute hashes, does not canonicalize multi-file packs, and does not verify third-party packs. This is a material description-behavior mismatch because the code chunk's actual purpose is much narrower than the declared suite.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description presents a broad 'operator suite' with several substantive capabilities: canonicalizing packs, computing hashes, writing ledgers, generating snippets, and verifying packs. The supplied code only formats and prints an anchor snippet from command-line arguments, with minimal platform-specific handling for Discord code fences. It does not inspect files, compute hashes, canonicalize content, write any ledger files, or verify external packs. While snippet generation is one stated capability and the supported platforms align with the description, the overall declared purpose materially overstates what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents a broad 'Advanced LYGO-MINT Operator Suite (v2)' with multiple capabilities: multi-file pack canonicalization, per-file and bundle hashing, canonical and append-only ledgers, machine-readable multi-platform anchor snippets, and third-party verification. The supplied code chunk is much narrower. It accepts a single --pack path, invokes an external mint_pack.py tool, records the returned output into a JSONL ledger, optionally runs canonicalize_ledger.py, and prints a simple text anchor snippet. It does align partially with ledger writing and canonical ledger updating, but the major advertised capabilities are not implemented in this chunk: there is no multi-file orchestration logic, no per-file hash generation, no bundle hash logic, no verification workflow, and no distinct multi-platform snippet generation beyond plain console text. Therefore the code's actual behavior is materially narrower than the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Most of the declared behavior is represented: the code accepts a file or directory, canonicalizes text files, computes per-file hashes and a bundle-level manifest hash, and writes both append-only and canonical ledger artifacts. However, the description materially overstates two capabilities. First, it says the suite can verify third-party packs, but this code only mints/records a pack and contains no verification workflow or validation logic against external packs. Second, it advertises machine-readable multi-platform Anchor Snippets across specific platforms, but the implementation emits one generic newline-delimited snippet embedded in JSON, with no platform-specific formatting for MoltX/Moltbook/X/Discord/4claw. Therefore the description does not accurately match the supplied code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description presents a broad operator suite with multiple capabilities: pack canonicalization, per-file and bundle hash generation, ledger writing, anchor snippet production, and third-party pack verification. The supplied code chunk only implements the verification portion. It computes hashes and canonicalizes a manifest as part of verification, but does not write ledgers, generate anchor snippets, or expose the broader suite functionality described. Since the described primary purpose is substantially broader than the actual behavior of this code chunk, the description does not accurately represent what this specific supplied code does.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises shell, file read, and file write driven workflows but declares no explicit tool scope or permission boundaries. That creates an avoidable trust gap: an agent may be induced to perform filesystem mutations or shell execution without a narrowly defined allowlist, increasing the chance of unintended command execution or writes outside the expected workspace.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_py(path: Path, args: list[str]) -> subprocess.CompletedProcess:
    return subprocess.run([sys.executable, str(path), *args], cwd=str(ROOT), capture_output=True, text=True)


def main() -> None:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script appends to and overwrites files under workspace-controlled state/reference directories as part of normal execution, without any confirmation, dry-run mode, or explicit warning to the caller. In an agent-skill context, this is security-relevant because running the skill mutates persistent workspace state and could overwrite ledger/canonical records or create durable artifacts without the operator fully realizing it.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Static analysis

No suspicious patterns detected.