Safeguard
AI Security•

How to Hunt Planted AI Agent and IDE Config Files in Your Repos

The tensorlake worm committed .claude/settings.json and .vscode/tasks.json into victims' repos. Find files like these across your org, then lock down who can change them.

Safeguard Security Team
6 min read

On 8 October 2026, the worm inside tensorlake 0.5.144 added a new persistence trick. With a stolen GitHub token, StepSecurity reports, it "writes .claude/settings.json and .vscode/tasks.json files into repos it can reach, so it runs again when someone opens the project in Claude Code or VS Code." A month earlier, CVE-2026-85674 (published 4 September) showed the same idea in aider, where a repository's .aider.conf.yml can set a test-cmd that runs "at startup."

What's affected

Any repository that developers open in a tool that reads project config. These are the files to hunt:

  • .claude/settings.json for Claude Code. It can define hooks on events such as SessionStart, an env block and permissions.allow rules.
  • .mcp.json, which lists MCP servers for Claude Code to start or connect to.
  • .vscode/tasks.json, where a task with "runOn": "folderOpen" under runOptions runs "when the containing folder is opened."
  • .vscode/settings.json. VS Code's docs warn that some workspace settings contain paths to executables.
  • .aider.conf.yml and a repo-root .env. Aider's docs say the .env file can set "many general aider options" as well as keys, and AIDER_TEST_CMD is one of them.

Trust prompts help less than you'd hope here, because the worm writes into repositories you already trust. VS Code runs automatic tasks only in trusted workspaces, and you trusted your own repo long ago. Claude Code's permissions docs say that hooks in settings files run when you have trusted only a parent folder, and also run in claude -p or SDK sessions that never show the trust dialog. A CI job that runs claude -p on a pull request branch fits that second case exactly.

How to check if you're exposed

1. Search the org for the riskiest content. Code search through the API covers default branches only, so it's a first pass:

ORG=your-org
gh search code folderOpen --owner "$ORG" --filename tasks.json --json url --jq '.[].url'
gh search code hooks --owner "$ORG" --filename settings.json --json path,url \
  --jq '.[] | select(.path | test("(^|/)[.]claude/")) | .url'
gh search code mcpServers --owner "$ORG" --filename .mcp.json --json url --jq '.[].url'
gh search code test-cmd --owner "$ORG" --filename .aider.conf.yml --json url --jq '.[].url'

2. Look for the worm's commits. StepSecurity says they used the author claude@users.noreply.github.com and the message "chore: update dependencies":

gh search commits --author-email claude@users.noreply.github.com --owner "$ORG"

Then list recent changes to these paths in each repository, on every branch you care about:

for p in .claude/settings.json .mcp.json .vscode/tasks.json .vscode/settings.json .aider.conf.yml; do
  gh api "repos/$repo/commits?path=$p&since=2026-10-01T00:00:00Z" \
    --jq '.[] | [.sha[0:8], .commit.author.email, .commit.message] | @tsv'
done

3. Sweep local clones, including branches you haven't pushed:

find ~/src -type f \( -path '*/.claude/settings*.json' -o -name .mcp.json \
  -o -path '*/.vscode/tasks.json' -o -name .aider.conf.yml \) -not -path '*/node_modules/*' \
  | xargs grep -lE '"hooks"|"folderOpen"|"mcpServers"|test-cmd|lint-cmd' 2>/dev/null
grep -lE '^AIDER_(TEST|LINT)_CMD' ~/src/*/.env 2>/dev/null

In any single repo, git log --diff-filter=A --format='%h %ae %s' -- .claude .vscode .mcp.json .aider.conf.yml shows who first added each file. Read every hit the way you'd read a shell script from a stranger.

What to do now

  1. Require review on these paths. Add owners in .github/CODEOWNERS, where a pattern like .claude/ matches that directory anywhere in the repo:

    .claude/          @your-org/security
    .vscode/          @your-org/security
    .mcp.json         @your-org/security
    .aider.conf.yml   @your-org/security
    /.github/         @your-org/security
    

    Then enable "Require review from Code Owners" in branch protection. CODEOWNERS only gates pull requests, so also require a pull request before merging. Otherwise a stolen token with push access commits straight past it.

  2. Restrict hooks and MCP servers with Claude Code managed settings. Put a managed-settings.json in /Library/Application Support/ClaudeCode/ on macOS, /etc/claude-code/ on Linux and WSL, or C:\Program Files\ClaudeCode\ on Windows:

    {
      "allowManagedHooksOnly": true,
      "allowManagedMcpServersOnly": true,
      "allowedMcpServers": [{ "serverName": "github" }]
    }
    

    allowManagedHooksOnly blocks user, project and local hooks while managed hooks still run. The MCP pair makes the managed allowlist the only one that counts.

  3. Run headless agents without project config. For claude -p on code you didn't write, the docs recommend --setting-sources user or --bare, or --settings '{"disableAllHooks": true}' for that run. Setting disableAllHooks in your user settings isn't enough, because project settings take precedence and can set it back. To open a suspect repo interactively, claude --safe-mode skips hooks and MCP servers from your own config and the project, though managed policy still applies.

  4. Keep VS Code automatic tasks off. Set "task.allowAutomaticTasks": "off" in user settings, and pick Disallow if VS Code asks. Don't trust broad parent folders like ~/src, since subfolders inherit that trust.

  5. Pin aider to your own config. aider --config ~/.aider.conf.yml loads only that file. Aider still reads a repo-root .env, so keep the AIDER_ grep above in your routine.

  6. Clean up properly. Delete planted files in a reviewed pull request, check other branches, and treat anyone who opened the repo after the planting commit as possibly compromised.

Why it matters

Agent config files are executable code with a JSON or YAML extension. They live in the repo, travel with every clone and run with the developer's credentials. Tensorlake shows attackers have noticed that this is better persistence than a cron job: it reinfects whoever opens the project next.

The fix is ordinary repository hygiene applied to a new set of paths. Give them owners, require review and decide centrally which hooks and servers may run at all.

Try it yourself

Run the step 1 searches against your own org and count the hits. For each one, decide whether the file should exist, who owns it and whether it runs a command. Then see how a pull request gate can flag changes to these paths before merge.

Click through it at your own pace, or open it full screen.

Quick check

Why doesn't workspace trust stop a planted tasks.json?

The worm commits into repositories developers already trust. VS Code blocks automatic tasks only in untrusted workspaces, so the task.allowAutomaticTasks setting is the control that matters.

Do Claude Code project hooks run in claude -p sessions?

Yes. The docs say hooks in settings files are used in claude -p and SDK sessions even when the folder was never trusted. Use --setting-sources user, --bare or a disableAllHooks override for untrusted code.

Is CODEOWNERS enough to stop a stolen token?

No. It only applies to pull requests. Pair it with branch protection that requires a pull request before merging.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.