Now in early access, book a 30-minute demo →
← Back to blog Research

What We Find When We Scan AI Agent Configs Across Managed Fleets

When you inventory the AI agent configuration files sitting on a fleet of managed laptops, the same handful of risk patterns show up over and over. They are not exotic zero-days. They are ordinary config: a settings file with an API key pasted in, a permission grant that says yes to everything, an MCP server someone installed from a public registry on a Tuesday and never reviewed. This post is a research-style walk through the recurring patterns Anomity surfaces when it inventories the local AI layer on Windows, macOS, and Linux endpoints, and why these files matter to a security team that has never had reason to read them before. The examples below are illustrative of what this layer typically looks like, not a published dataset.

The reason this layer goes unexamined is structural. EDR watches processes, DLP watches data egress, a gateway watches network flows. None of them parse the JSON that tells an AI agent what it is allowed to do on the machine. That file is where the actual blast radius is defined, and until you read it you are governing the part of the agent you can see while ignoring the part that holds the keys.

Plaintext secrets in agent config files

The most common and most immediately exploitable pattern is a credential living in cleartext inside an agent's settings file. Agents read environment variables and config blocks to authenticate to the tools they orchestrate, and the path of least resistance for a developer in a hurry is to paste the secret directly. API keys, database connection strings with embedded passwords, JWTs, and occasionally a private key all turn up in files like ~/.claude/settings.json or an equivalent under a user profile.

{
  "env": {
    "DATABASE_URL": "postgres://app:[email protected]:5432/customers",
    "STRIPE_API_KEY": "sk_live_4eC39HqLyjWDarjtT1zdp7dc",
    "INTERNAL_JWT": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdmMifQ.signature"
  },
  "permissions": {
    "allow": ["Bash(*)", "Write(*)", "Read(*)"]
  }
}

A file like this fails on two axes at once. The secrets are recoverable by anything that can read the user's home directory, and they are now scattered across however many endpoints the developer works on, well outside the secret manager that was supposed to be the single source of truth. When Anomity surfaces this, the secret value never leaves the machine. The daemon redacts it on the endpoint and reports the finding as metadata: a credential is present in plaintext at this path on this device. That is enough for the security team to act, and it never puts the secret itself on the wire.

Blanket permission grants like Bash(*) and Write(*)

The permissions block in the example above is the second pattern, and it is worth dwelling on. A grant of Bash(*) means the agent may execute any shell command. Write(*) means it may write any file. These wildcards usually appear not because anyone decided the agent should have unrestricted machine access, but because scoped permissions are tedious to enumerate and a blanket allow makes the friction go away. The grant is a convenience that quietly hands the machine to whatever prompt happens to be running.

The risk is that the boundary collapses. An agent with Bash(*) is one prompt injection, one poisoned document, or one confused-deputy chain away from running arbitrary commands with the user's full privileges. The config does not look alarming at a glance, which is precisely why it survives. Anomity treats wildcard grants as a policy-evaluable condition rather than a judgment call, so the appearance of Bash(*) or Write(*) on a managed endpoint becomes a violation that routes to wherever the team already works: SIEM, Slack, email, or Jira.

Unvetted MCP servers from public registries

The third pattern moves from permissions to supply chain. MCP servers extend an agent with new capabilities, and installing one is as easy as adding an entry to a config file and pointing it at a package from a public registry. The convenience is the problem: a server pulled off a registry and wired in once will keep running with whatever filesystem, shell, network, and credential access its config grants, and nobody revisits it.

{
  "mcpServers": {
    "mcp-sqlite-cloud": {
      "command": "npx",
      "args": ["-y", "mcp-sqlite-cloud@latest"],
      "env": {
        "DB_TOKEN": "tok_live_9f2c8a1e"
      }
    }
  }
}

A server like this one looks innocuous, but consider what it actually has: it spawns a process via npx that pulls the latest published version on every run, it carries a live token in its environment, and a database connector of this kind typically reaches across filesystem, shell, and network. None of that was reviewed. Anomity's discovery engine runs a multi-signal trust classifier over each MCP, weighing vendor, command, and fingerprint to sort it as official, community, or unknown, then infers its capabilities and flags dangerous combinations. An unknown server combining shell and network access with a live credential is exactly the kind of finding that should never reach production silently.

An MCP server is a legitimate process doing exactly what its config allows. The question is whether anyone ever decided that was allowed.Daniel Soto, Anomity

Extensions and tooling that drift off the allowlist

The fourth pattern is quieter and accumulates over time. IDE and agent extensions for VS Code, Cursor, and JetBrains install in seconds, and most developers have a handful that were never on any approved list. An extension that reads the editor buffer, talks to a remote completion service, or injects tooling into the agent's context is a real channel for code and data to move, yet it sits below the radar of every fleet control that is not reading the editor's extension manifest.

Off-allowlist drift is rarely malicious. It is the natural entropy of developers trying tools. But entropy is still exposure, and the only way to manage it is to maintain an inventory and compare it against what was approved. Anomity enumerates installed extensions per endpoint and evaluates them against the allowlist continuously, so an extension appearing on a machine that never approved it becomes a tracked deviation rather than a thing you discover during an incident.

Why these findings matter as a set

Taken individually, each of these is a minor hygiene issue. Taken together on the same endpoint, they compound. A plaintext credential plus a Bash(*) grant plus an unknown MCP server with network access is not four small problems; it is a single coherent path from an injected prompt to data exfiltration, and it lives entirely in config files that no existing tool was reading. The recurring shape of these patterns is the argument for treating the local AI layer as something to inventory and govern on purpose.

What changes the equation is making the layer continuously visible. Anomity's daemon discovers and classifies these artifacts on every managed endpoint, evaluates them against policy as they change, and keeps a 90-day audit trail of every added, removed, or modified MCP, permission, extension, plugin, skill, and hook. The point of the research is not that these patterns are surprising. It is that they are predictable, they are everywhere we look, and you can only act on the ones you can see.

Ask AI about Anomity
ChatGPT Claude Perplexity Google AI Grok