Back to skill

Security audit

agent-id.io

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it handles long-lived identity keys and tokens with several under-scoped defaults that deserve manual review.

Install only if you trust agent-id.io and are comfortable with local agents managing identity private keys. Prefer encrypted key output from the start, avoid plaintext /tmp key or token files, do not use AGENT_ID_API with production tokens unless the destination is explicitly trusted, and treat SSH/PGP derivation as a separate high-impact opt-in decision.

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

Error
Location
scripts/crypto_utils.py:111
Finding

Arbitrary HTTPS API override can disclose bearer tokens to an attacker-controlled host

Content
View full analysis
str: """Resolve the API base URL, enforcing HTTPS. Reads AGENT_ID_API environment variable. Rejects non-HTTPS URLs with a clear error message to prevent TLS downgrade attacks. """ url = os.environ.get("AGENT_ID_API", default).rstrip("/") if not url.startswith("https://"): print( f"Error: AGENT_ID_API must use https:// (got {url!r}). " "HTTP connections expose credentials in transit.", file=sys.stderr, ) sys.exit(1) return url ``` The resulting value is used with a bearer token in `scripts/rotate_keys.py:125-153`: ```python if args.apply: token = args.token or os.environ.get("AGENT_ID_TOKEN") if not token: print("Error: --apply requires --token or AGENT_ID_TOKEN.", file=sys.stderr) sys.exit(1) # ... response = requests.post( f"{API_BASE}/agents/{agent_id}/keys/rotate", headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }, json=payload, timeout=10, ) ``` ### Technical Analysis The API resolver only verifies that the configured URL begins with `https://`. It does not restrict the hostname to `agent-id.io` or another explicit trust allowlist. HTTPS protects a connection from passive interception, but it does not make an arbitrary recipient trustworthy. Because `API_BASE` is initialized from the `AGENT_ID_API` environment variable, an attacker who can influence the process environment can redirect authenticated requests to an attacker-controlled HTTPS server. The rotation flow then places the agent JWT in the HTTP `Authorization` hea ...[truncated 1569 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/keygen.py:74
Finding

Private identity keys are persisted unencrypted by default

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (32)

Tainted flow: 'token' from os.environ.get (line 125, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/rotate_keys.py (reported line 149)May include surrounding context.

python
print("Error: --apply requires agent_id in the existing keys file.", file=sys.stderr)
                sys.exit(1)

            response = requests.post(
                f"{API_BASE}/agents/{agent_id}/keys/rotate",
                headers={
                    "Authorization": f"Bearer {token}",

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description promises substantial identity, authentication, key management, verification, and sponsorship capabilities for agent-id.io. The actual code chunk does not implement any of these behaviors; it is effectively empty aside from a comment. This is a material description-to-behavior mismatch because the code’s observable purpose is just a placeholder/import bridge, not the declared service integration.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description promises a broad agent-id.io identity/trust service integration with capabilities like agent registration, challenge/response authentication, key/passkey management, verification workflows, sponsorships, and public profile inspection. The actual code chunk does none of that. It is narrowly focused on encrypting and decrypting a local keyfile (agent_keys.json) using scrypt-derived AES-GCM encryption, reading/writing files and prompting for a passphrase. While keyfile protection could be a supporting detail for key management, this chunk by itself does not implement the declared service integration or any of the listed user-facing operations. Therefore the code’s primary purpose is materially different from the declared description.

Content

No source excerpt is available for this finding.

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

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

md
# → Outputs: agent_keys.json (private, keep secret) + public keys as base64

# Register
curl -s -X POST https://agent-id.io/v1/agents/register \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "my-agent",

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

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

→ Outputs the auth/verify payload as JSON

POST the signed payload

TOKEN=$(curl -s -X POST https://agent-id.io/v1/auth/verify
-H "Content-Type: application/json"
-d "$(cat signed_challenge.json)" | python3 -c "import json,sys; print(json.load(sys.stdin)['token'])")

text

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Importing a deterministically derived PGP private key into the user's GPG keyring introduces long-lived credential material into a broadly used trust store. In combination with the shared master seed design, compromise or misuse of the skill can plant or reuse credentials beyond the narrow agent-id.io context.

Content

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

~/.ssh/agent_pgp_private.asc (PGP private key, ASCII-armored)

~/.ssh/agent_pgp_public.asc (PGP public key)

Import PGP key to GPG keyring

gpg --import ~/.ssh/agent_pgp_private.asc

text

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/secure_keyfile.py encrypt agent_keys.json

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/secure_keyfile.py encrypt agent_keys.json

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/secure_keyfile.py encrypt agent_keys.json

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/secure_keyfile.py encrypt agent_keys.json

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/secure_keyfile.py encrypt agent_keys.json

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

The documented workflow writes decrypted secrets to predictable /tmp file paths and then deletes them with rm. Predictable temporary filenames can be abused via symlink or race conditions in some environments, and plain rm does not provide secure erasure or guarantee exclusive access during the file's lifetime.

Content

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

md
# Decrypt on use (preferred: interactive prompt)
python3 scripts/secure_keyfile.py decrypt agent_keys.json.enc --out /tmp/keys.json
python3 scripts/authenticate.py /tmp/keys.json --save-token /tmp/agent_token.jwt
rm /tmp/keys.json /tmp/agent_token.jwt  # delete immediately after use

# Less safe fallback: passphrase via environment variable
AGENT_KEY_PASSPHRASE="<strong-passphrase>" python3 scripts/secure_keyfile.py encrypt agent_keys.json

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% 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 · references/api.md (reported line 671)May include surrounding context.

md
**Errors:** `403 not_owner`, `409 passkey_limit_reached`

#### DELETE /agents/{agent_id}/passkeys/{passkey_id}

**Response 204:** No content.

Known Vulnerable Dependency: cryptography==46.0.7 — 7 advisory(ies): GHSA-537c-gmf6-5ccf (Vulnerable OpenSSL included in cryptography wheels); CVE-2026-69247 (cryptography: PKCS#7 EnvelopedData decryption exposes a Bleichenbacher oracle th); CVE-2026-69249 (python-cryptography: Duplicate self-signed intermediates can cause exponential p) +4 more

High
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is pinned to a version reported as affected by multiple security advisories, including issues in cryptographic processing and vulnerable OpenSSL bundled in wheels. In a skill that registers identities, performs challenge/response authentication, manages passkeys, and handles cryptographic keys, using a vulnerable crypto library directly increases the chance of authentication bypass, key-handling weaknesses, oracle attacks, or trust-validation failures.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill documents shell, network, and environment-variable based operations but does not declare any explicit tool scope or permissions boundaries. In an agent ecosystem, that omission increases the chance the runtime grants broader capabilities than users expect, enabling sensitive actions like outbound requests and credential handling without clear consent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
# → Outputs: agent_keys.json (private, keep secret) + public keys as base64

# Register
curl -s -X POST https://agent-id.io/v1/agents/register \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "my-agent",

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

→ Outputs the auth/verify payload as JSON

POST the signed payload

TOKEN=$(curl -s -X POST https://agent-id.io/v1/auth/verify
-H "Content-Type: application/json"
-d "$(cat signed_challenge.json)" | python3 -c "import json,sys; print(json.load(sys.stdin)['token'])")

text

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

Add a passkey

bash
curl -s -X POST "https://agent-id.io/v1/agents/$AGENT_ID/passkeys" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill extends beyond agent-id identity management by deriving SSH and PGP private keys from the same master seed. That broadens the blast radius of a single compromise: theft or misuse of agent_keys.json now affects not only agent-id authentication but also unrelated infrastructure and communications identities.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Creating SSH and PGP credentials is context-inappropriate for a service-specific identity skill and introduces powerful secondary credential-generation capability. In an agent setting, undocumented or weakly justified credential creation increases the risk of lateral movement, persistence, or user confusion about what identities are being created and reused.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The instructions decrypt sensitive key material and write both decrypted keys and JWTs into /tmp, then rely on later deletion. Temporary directories are commonly accessible to other local processes, may persist unexpectedly, and deletion does not guarantee the data was never exposed or recoverable.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/authenticate.py (reported line 82)May include surrounding context.

python
credential_id = b64url_encode(pub_bytes)

        # Step 1: Get challenge
        resp = requests.post(f"{API_BASE}/auth/challenge",
                             json={"agent_id": agent_id}, timeout=10)
        if resp.status_code == 403 and "revoked" in resp.text:
            print("Error: Agent revoked. Unrevoke first.", file=sys.stderr)

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/authenticate.py (reported line 113)May include surrounding context.

python
signature = private_key.sign(signed_data)

        # Step 2: Verify
        resp = requests.post(f"{API_BASE}/auth/verify", json={
            "agent_id": agent_id,
            "credential_id": credential_id,
            "authenticator_data": b64url_encode(authenticator_data),

Tainted flow: 'client_data' from requests.post (line 101, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/authenticate.py (reported line 113)May include surrounding context.

python
signature = private_key.sign(signed_data)

        # Step 2: Verify
        resp = requests.post(f"{API_BASE}/auth/verify", json={
            "agent_id": agent_id,
            "credential_id": credential_id,
            "authenticator_data": b64url_encode(authenticator_data),

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 234)May include surrounding context.

md
python3 derive_keys.py agent_keys.json --uid "My Agent <myagent@example.com>"

Output:
    id_agent_ed25519        SSH private key (OpenSSH format, chmod 600)
    id_agent_ed25519.pub    SSH public key
    agent_pgp_public.asc    PGP public key (ASCII-armored, importable with gpg)
    agent_pgp_private.asc   PGP private key (ASCII-armored, importable with gpg)