Back to skill

Security audit

LYGO External Lattice Anchor (World Network Layer C)

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does the advertised LYGO public verification work, but it needs Review because a manifest can steer its verifier to contact arbitrary HTTPS hosts from the user's environment.

Install only if you understand that this skill can run Python against a LYGO stack checkout and can make outbound HTTPS GET requests listed in a manifest. Use only a stack and manifest you control, avoid custom manifests from others, and prefer the default fixed verification path unless you specifically need local rebuilds or snapshots.

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

Warning
Location
scripts/verify_public_anchors.py:72
Finding

Manifest-Controlled URLs Permit SSRF and Redirect-Based Policy Bypass

Content
View full analysis
tuple[int, bytes | None, str | None]: req = urllib.request.Request(url, headers={"User-Agent": UA}, method="GET") try: with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.status, resp.read(), None except urllib.error.HTTPError as e: return e.code, None, str(e) except Exception as e: return 0, None, str(e) ``` ```python man_path = Path(args.manifest) if args.manifest else stack / "docs" / "public_verify_manifest.json" built = False if args.build_manifest: if not args.i_trust_stack: print( json.dumps( { "verdict": "BLOCKED", "errors": ["build_manifest_requires_i_trust_stack"], "hint": "pass --i-trust-stack only for a stack checkout you control", } ), file=sys.stderr, ) return 2 if not man_path.is_file(): builder = Path(__file__).resolve().parent / "build_public_verify_manifest.py" if builder.is_file(): from _safe_invoke import run_python_script # noqa: E402 code, out = run_python_script( builder, ["--stack-root", str(stack)], cwd=stack, stack=stack, ) built = code == 0 if code != 0: print(f"build_manifest_failed code={code} {out[:300]}", file=sys.stderr) endpoints = [] if man_path.is_file(): man = json.loads(man_path.read_text(encoding="utf-8")) endpoints = man.get("public_endpoints") or [] else: endpoints = list(DEFAULT_ENDPOINTS) ALLOWED_VERIFY = {"http_required", "http_soft", "skip"} results = [] hard_fail = ...[truncated 3774 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/verify_public_anchors.py:72
Finding

Unbounded HTTP Response Reads Permit Memory Exhaustion

Content
View full analysis
tuple[int, bytes | None, str | None]: req = urllib.request.Request(url, headers={"User-Agent": UA}, method="GET") try: with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.status, resp.read(), None except urllib.error.HTTPError as e: return e.code, None, str(e) except Exception as e: return 0, None, str(e) ``` ### Technical Analysis The verifier calls `resp.read()` without a maximum size, causing the entire response body to be accumulated in memory. Neither the `Content-Length` header nor the actual number of bytes read is limited. The connection timeout does not impose a maximum response size. A malicious, compromised, redirected, or unexpectedly large endpoint can therefore consume substantial process memory. A server may also stream data incrementally while avoiding an individual socket timeout. The verifier only needs endpoint availability, response metadata, and small JSON documents. Reading arbitrary complete HTML or dataset responses exceeds the minimum resources necessary for the declared verification task. ### Attack Path 1. An endpoint configured in the manifest is compromised, attacker-controlled, or redirects to an attacker-controlled server. 2. The verifier sends its normal HTTPS GET request. 3. The server returns a very large response or continuously streams data. 4. `resp.read()` accumulates the response in memory without enforcing a byte limit. 5. The Python process experiences excessive memory use, severe slowdown, or termination by the operating system or runtime environment. ### Impact Assessment Exploitation can deny service to the verification process and may affect other workloads on the same constrained Agent host thro ...[truncated 214 chars]
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The documentation claims a narrow, mostly non-mutating verification workflow, yet the detected behavior includes execution of local Python scripts, working-directory changes, and a capability surface broader than simple HTTP GET plus local JSON handling. That mismatch is dangerous because users and policy engines may approve the skill based on a benign description while the actual behavior can execute code from a user-designated stack root, leading to arbitrary code execution or unintended local mutations if the trusted-root assumption fails.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises least-privilege behavior, but the manifest does not declare explicit tool permissions or allowed-tools despite requiring environment access, file reads, optional file writes, network access, and Python/script execution. This creates an authorization gap where the runtime may grant broader capabilities than users expect, making review and enforcement harder and increasing the risk of unintended shell or filesystem access.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest emphasizes verifying public mirrors and says verification is non-mutating by default, with extra consent only for snapshotting. This script always creates directories and writes a JSON proposal file under the stack or a user-supplied path, which is a mutating filesystem operation not conveyed by the read-oriented verification framing.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/verify_world_lattice.py (reported line 150)May include surrounding context.

python
if unified.is_file():
        try:
            unified.relative_to(stack.resolve())
            p = subprocess.run(
                [sys.executable, str(unified), "--json"],
                cwd=str(stack),
                capture_output=True,

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The module docstring says 'Does NOT publish. Consent not required for dry manifest build,' which frames the operation as a dry/non-publishing build. However, the implementation persists output by creating parent directories and writing public_verify_manifest.json or a user-specified path, so the behavior is not purely non-mutating even if it avoids network publication.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The inline documentation suggests a dry build, which ordinarily implies no persisted side effects. In contrast, lines L272-L274 create directories and write the generated manifest to disk, so the documentation actively misstates the operational effect.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The documentation stresses non-live-writing behavior, which implies safety from mutation, but the implementation later creates directories and writes a JSON file to disk. While it does not modify the live star chart, the docstring omits and potentially downplays the fact that the script has local write side effects.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.