01. Observed Symptoms & Why Standard Package Audits Fail

Observed (scope): The assets in question are SKILL.md files, .cursorrules, and mcp.json. Related instruction files such as CLAUDE.md, AGENTS.md and Cursor .mdc rules follow the same trust model. An agent loads them as instructions and executes tool calls with whatever credentials are in its environment.

Symptoms: (1) An agent starts reading ~/.ssh or .env files for a task that has no reason to touch them. (2) A third-party skill or rules file diffs cleanly in review, yet the agent behaves differently from what the visible text says. (3) An MCP server in mcp.json is launched with npx and no pinned version, so what runs changes between sessions. (4) Nobody can list every skill and MCP server installed on a team member's machine.

Why package audits fail: npm audit and Dependabot track package manifests. A markdown skill is not a package. The malicious part may be a sentence the model follows, or a hook line in a script the skill tells the agent to run, so no CVE exists for it.

Observed (Unicode): Bidirectional control characters (U+202A to U+202E, U+2066 to U+2069, U+200E, U+200F, U+061C), zero-width characters (U+200B to U+200D, U+2060, U+FEFF) and tag characters (U+E0000 to U+E007F) render as nothing or reorder text on screen but are still present in the bytes a tokenizer receives. Inference: whether a given model acts on hidden tag characters or reordered text depends on its tokenizer and training, which is why tokenizer hygiene (HadayaLab Badge #1453, DeepMind tokenizer hygiene) is a review step and not an afterthought.

02. Root-Cause Forensic Analysis (Runtime & Protocol Level)

Root cause 1, confused presentation: human review sees rendered text, while the model sees code points. Any character that is invisible or reorders display creates a gap between what was approved and what is executed. A code review that does not show these characters cannot catch them.

Root cause 2, ambient authority: an agent process usually inherits your shell environment, home directory and tokens. A skill that instructs 'read the project config' can be steered to read .env, ~/.ssh/id_rsa or cloud credentials, then send them to an attacker-controlled endpoint via curl, a webhook, or an MCP tool call.

Root cause 3, floating dependencies: an MCP server launched with an unpinned npx or uvx command runs whatever the registry serves at that moment.

The scanner below is a deterministic first pass you can run on any machine. It flags invisible and Bidi code points in agent assets, and lines that mention common exfiltration hooks. It produces findings for human review. A hit is not proof of malice (legitimate skills do mention .env), and a clean result is not proof of safety. Windows Git Bash note: invoke Python as shown, since plain python resolves to the Microsoft Store stub.

skill_scan.py: run with: PYTHONUTF8=1 py -3.12 skill_scan.py . PYTHON
import sys, pathlib

TARGETS = {'SKILL.md', '.cursorrules', 'mcp.json', 'CLAUDE.md', 'AGENTS.md'}
SKIP = {'.git', 'node_modules', '.venv'}
HOOKS = ('.env', '.ssh', 'id_rsa', 'authorized_keys', 'aws_secret', 'webhook.site',
         'ngrok', 'pastebin', 'base64 -d', 'curl ', 'wget ')

def bad(ch, i):
    o = ord(ch)
    if o == 0xFEFF and i == 0:
        return False   # legitimate BOM
    return (0x202A <= o <= 0x202E or 0x2066 <= o <= 0x2069 or 0xE0000 <= o <= 0xE007F
            or o in (0x200B, 0x200C, 0x200D, 0x200E, 0x200F, 0x2060, 0x061C, 0xFEFF))

root = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else '.')
hits = 0
for p in root.rglob('*'):
    if not p.is_file() or SKIP & set(p.parts):
        continue
    if p.name not in TARGETS and p.suffix != '.mdc':
        continue
    text = p.read_text(encoding='utf-8', errors='replace')
    for i, ch in enumerate(text):
        if bad(ch, i):
            line = text.count(chr(10), 0, i) + 1
            print(f'INVISIBLE/BIDI U+{ord(ch):04X} {p}:{line}')
            hits += 1
    for n, line in enumerate(text.splitlines(), 1):
        low = line.lower()
        for h in HOOKS:
            if h in low:
                print(f'HOOK[{h.strip()}] {p}:{n}: {line.strip()[:120]}')
                hits += 1
sys.exit(1 if hits else 0)
1:1 EMBEDDED SOLUTION PIPELINE · HL-R6-MCPAUDIT

5-Register Quarantine & Attestation Engine

AWAITING INSPECTION
REGISTER 1 In-Browser AST & Lockfile Inspector
$0 · ALWAYS FREE

Evaluated 100% locally in your browser inside a zero-exfiltration sandbox (connect-src 'none'). Your credentials and proprietary code never touch our servers.

REGISTER 2 Non-Breaking Vaccine & Config Diff
10 cr ($1.00)

Generates targeted unified .patch diffs and perimeter WAF rules. Non-breaking compatibility is mathematically verified against the complete dependency graph.

✓ NON-BREAKING VERIFIED: Compatible across 14 downstream dependencies (0 semver conflicts)
TARGET FILES: CRYPTOGRAPHICALLY VERIFIED
mcp_sanitizer_policy.yaml & AI Agent Skill Gateway
Invisible Unicode strip, bidirectional override reject, and path jail
REGISTER 3 Signed CycloneDX 1.6 SBOM & Attestation PDF
20 cr ($2.00)

Official cryptographic audit certificate with DSSE Ed25519 digital signature, 7-day rolling attestation window, and compliance mapping for NIST CSF 2.0 and SOC2.

ATTESTATION SPEC: No Findings in Scope
Auditor Authority: 45x Google Cloud & DeepMind Skill Badges
Attestation Window: Valid for 7 days (next variant sweep required in 7 days)
Signature Spec: in-toto Statement v1 / DSSE Ed25519 envelope (hadayalab-attest-2026-ed25519-k1)
Inspect Sample Ed25519 Signed SBOM (/sbom/PAIN-2026-006.cdx.json) →
REGISTER 4 Side-Effect & Rollback Manifest
10 cr ($1.00)

Instant crash and compatibility analyzer. Computes peerDependency, PHP extension, and engine compatibility changes with a 1-command rollback manifest.

ROLLBACK INTEGRITY SPEC:
Atomic git-level rollback manifest with state-reversal guarantees for PAIN-2026-006.
REGISTER 5 Continuous CI/CD GitHub Actions OIDC Sweeper
1 cr / run ($0.10)

Automated weekly variant sweep via GitHub Actions OIDC (id-token: write). Auto-renews 7-day rolling attestation tokens without long-lived secrets.

AUTOMATION TARGET:
.github/workflows/hadayalab-attest.yml
Zero long-lived API secrets · OIDC Short-Lived JWT Verification

03. Immediate Zero-Downtime Triage (Non-Breaking Mitigation)

Step 1: run the scanner above over every repository, every user-level agent directory and every MCP config on developer and CI machines. Review each hit by viewing the file as code points, not rendered text.

Step 2: run three independent scanners and compare results: NVIDIA/SkillSpector (https://github.com/NVIDIA/SkillSpector), snyk/agent-scan (https://github.com/snyk/agent-scan) and affaan-m/agentshield (https://github.com/affaan-m/agentshield). Inference: their detection rules differ, so one tool's PASS is not conclusive. Command-line flags change between releases, so follow each repository's README rather than copying flags from a blog post. HadayaLab's relevant credential references are Badge #1832 (Build Agent Skills with Google) for skill authoring practice.

Step 3: freeze what you have. Hash every agent asset into a lock file, sign it, and verify the hashes before agent sessions in CI. Any silent modification then fails loudly.

Step 4: pin MCP servers. Replace unpinned launch commands with exact versions, and keep secrets out of mcp.json by referencing environment variables injected at runtime.

Lock and sign agent assets; verify in CI BASH
git ls-files | grep -E '(^|/)(SKILL[.]md|[.]cursorrules|mcp[.]json|CLAUDE[.]md|AGENTS[.]md)$' | sort | xargs sha256sum > agent-assets.lock
cosign sign-blob --yes --bundle agent-assets.lock.bundle agent-assets.lock

# CI / pre-session gate
sha256sum -c agent-assets.lock || { echo 'agent asset changed since last signed review'; exit 1; }

04. Permanent Remediation & Cryptographic SBOM Attestation

Policy: (1) no third-party skill, rules file or MCP server enters a repo without a scanner pass and a human diff that shows invisible characters; (2) an allowlist of MCP servers, each pinned to an exact version or digest; (3) agents run in a sandbox or container with a minimal environment, no home-directory mount and no access to ~/.ssh or production .env; (4) egress allowlisting so a hook cannot reach arbitrary hosts.

OAuth2 guardrails: give agents their own client identity, request the narrowest scopes, use short-lived tokens, bind tokens to the target resource server's audience, and never forward a user's token to a downstream service the agent calls (token passthrough). Confirm the exact requirements in the current MCP authorization specification before you implement. Revoke by client, not by user, so one compromised agent does not force a rotation for everyone.

Attestation: treat the signed agent-assets.lock as the agent-side equivalent of an SBOM. Record the scanner versions and results beside it, and re-sign on every reviewed change. Where agents ship inside a container, attach the lock and the CycloneDX SBOM to the image digest (PAIN-2026-005).

Primary sources: https://github.com/NVIDIA/SkillSpector | https://github.com/snyk/agent-scan | https://github.com/affaan-m/agentshield

HadayaLab operates with 45x Google Cloud & DeepMind Skill Badges (https://www.skills.google/public_profiles/49cdbdb6-20d4-4661-934a-d882c7f5c6cf). We audit and harden custom Claude MCP servers, OpenClaw-style agent deployments and skill libraries, with scoped OAuth2 and signed asset locks.

Frequently Asked Questions

Can invisible Unicode in SKILL.md or .cursorrules really hijack an AI agent?

Invisible and Bidi code points do not render, but they are present in the bytes the model receives, so a reviewer and the model can see different text. Whether a specific model acts on them depends on its tokenizer and training. Treat them as findings to remove from instruction files regardless.

How do I audit an MCP server or agent skill before installing it?

Scan it with more than one tool (NVIDIA/SkillSpector, snyk/agent-scan, affaan-m/agentshield), run a code-point scan such as skill_scan.py, read the diff with invisible characters made visible, pin the exact version, and run it in a sandbox with no access to ~/.ssh or production .env files.

How do I stop an agent from exfiltrating .env files or SSH keys?

Remove the access, not just the instructions: run agents in a container or sandbox with a minimal environment, mount only the project directory, keep secrets out of the agent's environment, and allowlist outbound hosts. Scanning detects known patterns, while sandboxing limits the damage when a pattern slips through.

What OAuth2 guardrails should a custom Claude MCP server have?

Use a dedicated client per agent, narrow scopes, short-lived tokens bound to the resource server's audience, no token passthrough to downstream APIs, and per-client revocation. Verify the details against the current MCP authorization specification before you deploy.