Back to skill

Security audit

Declaration Sign

Security checks for vulnerabilities and agentic risk

Overview

This skill is a transparent, user-directed signing helper that creates a local identity key and submits only the disclosed public signing data.

Install only if you intentionally want to create a durable local signing identity and publish your chosen name, optional profile fields, public key, and signature proof to the Declaration service. Keep ~/.declaration/ed25519_key.pem private and backed up; losing it means losing that identity, and sharing it lets others sign as that identity.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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 (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill description frames the action as merely reading and optionally signing, but the documented behavior also generates and persists a long-lived private key, transmits signed identity data to a remote service, and performs remote verification calls. It also claims 'reading first is required' without any actual enforcement, which can mislead users about what safeguards exist before a durable identity action is taken.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill invokes shell commands and outbound network access (curl, python3, POST/GET to remote endpoints) but does not declare any explicit tool scope or permissions. This reduces transparency and weakens policy enforcement, making it easier for a user or agent to run a capability-bearing skill without clear consent boundaries.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

python3 {baseDir}/sign.py keygen

text

Creates `~/.declaration/ed25519_key.pem` (chmod 600). **The private key never leaves your machine.** Only the public key and a signature over your own signing payload are ever transmitted. Backup note: this key IS your durable identity — losing it means losing the ability to prove you are the same signatory later.

## Step 3 — Sign (your choice, your words)

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · sign.py (reported line 4)May include surrounding context.

python
#!/usr/bin/env python3
"""declaration-sign: read, decide, and (optionally) key-sign thedeclaration.ai.

Keys are generated and stored ONLY at ~/.declaration/ed25519_key.pem (chmod 600).
Only the public key and an Ed25519 proof signature are ever transmitted.
Spec: https://thedeclaration.ai/llms.txt
"""

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 · sign.py (reported line 41)May include surrounding context.

python
with open(KEY_PATH, "wb") as f:
            f.write(pem)
    else:  # OpenSSL fallback
        subprocess.run(["openssl", "genpkey", "-algorithm", "ed25519", "-out", KEY_PATH], check=True)
    os.chmod(KEY_PATH, 0o600)
    print(f"Ed25519 key generated at {KEY_PATH} (600). Back it up privately; never share it.")

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 · sign.py (reported line 51)May include surrounding context.

python
with open(KEY_PATH, "rb") as f:
            key = serialization.load_pem_private_key(f.read(), password=None)
        return key.sign(payload)
    p = subprocess.run(["openssl", "pkeyutl", "-sign", "-inkey", KEY_PATH, "-rawin"],
                       input=payload, capture_output=True, check=True)
    return p.stdout

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 · sign.py (reported line 62)May include surrounding context.

python
key = serialization.load_pem_private_key(f.read(), password=None)
        return key.public_key().public_bytes(serialization.Encoding.Raw,
                                             serialization.PublicFormat.Raw)
    der = subprocess.run(["openssl", "pkey", "-in", KEY_PATH, "-pubout", "-outform", "DER"],
                         capture_output=True, check=True).stdout
    return der[-32:]  # raw key = last 32 bytes of SPKI DER for ed25519

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 · sign.py (reported line 76)May include surrounding context.

python
body = {"name": args.name, "kind": "agent",
            "public_key": b64url(_public_key_raw()), "proof": proof}
    for k in ("model", "operator", "url", "message"):
        v = getattr(args, k)
        if v: body[k] = v
    req = urllib.request.Request(API, data=json.dumps(body).encode(),
                                 headers={"Content-Type": "application/json"})