Review what a clone installs and runs before it merges
ControlLoop reviews the files that silently install tools and run scripts every time a repo is opened or committed to, and flags any new, changed, or unpinned entry in the pull request.
Team: 7 days free, then $199 a month per organization. Cancel any time. Compare plans →
These files run before anyone reads the code
Opening a repository in a dev container installs the extensions and features its devcontainer.json names and runs its lifecycle commands, initializeCommand on the developer's own machine before the container exists. pre-commit runs its hook repositories on every commit, and Neovim, Vim, and Emacs can run a startup file committed at the project root when the folder opens.
A tag can be moved after review. An extension with no @version, a feature on :1 or :latest, or a pre-commit rev that names a tag instead of a commit installs whatever its publisher ships next, on every machine that uses the repository.
"customizations": { "vscode": {
+ "extensions": ["ms-python.python"]
} },
+ "initializeCommand": "bash .devcontainer/init.sh"
policy.unpinned-agent-componentMediumAdded: editor extension ms-python.python has no version pin, so the next clone installs whatever version is current.
policy.agent-hook-command-changeHighAdded: initializeCommand for Dev Containers runs bash .devcontainer/init.sh on the developer's machine. No approval is recorded.
EXIT 1What ControlLoop reports
Pinned means exact
An extension is pinned by @major.minor.patch, a devcontainer feature by an exact version or a sha256 digest, a pre-commit repository by a full commit SHA, and a JetBrains plugin when its minimum and maximum versions match. Anything else is reported as unpinned.
Commands and startup files by digest
Lifecycle commands and local pre-commit hooks are reported when they are added or changed, and a committed script they run is tracked by SHA-256. Editor startup files are never parsed; a changed file is reported by its digest.
Read, never run
Nothing is installed, pulled, cloned, or run. The gate reports the change for a person to review; it does not judge whether an extension is safe.
How it works
- 01
Re-record the baseline
Scan with CLI 0.7.0 or later. A baseline from an earlier version has none of these entries, so the first scan against it reports each one as added.
- 02
Gate every pull request
A pull request that adds an extension, moves a pre-commit rev to a tag, or edits a lifecycle command gets one line per change, with the file it came from.
- 03
Pin it, or approve it
Pin the entry to an exact version, digest, or commit and the unpinned finding clears. A reviewed command is recorded as an approval, which turns the block into a note.
Questions
Does ControlLoop install the extensions or run the commands?
No. The files are read as text. Nothing is installed, pulled, cloned, or run, and editor startup files are recorded by path and SHA-256 without being parsed.
Which files are read?
devcontainer.json in .devcontainer/, one folder below it, or .devcontainer.json; .vscode/extensions.json; *.code-workspace files; .pre-commit-config.yaml; .nvim.lua, .nvimrc, .exrc, .lvimrc, .dir-locals.el, and .dir-locals-2.el at the repository root; and .idea/externalDependencies.xml.
Does this replace an editor's extension allowlist?
No. Device-level controls such as VS Code's extensions.allowed policy cover what a person installs by hand. The gate covers what the repository declares, so the two work together.
Go deeper
Read the guide: Claude Code hooks and permissions: what a pull request can grant an agent
More use cases
Catch the pull request that hands an agent your whole shell
A one-line settings change lets an agent run any command without asking. See it before it merges.
See how ControlLoop catches itStop a committed hook before it runs on every laptop
A new SessionStart hook runs on every machine that opens the repo. Review the command before it does.
See how ControlLoop catches itCatch an MCP server that changes behind the same name
postmark-mcp was clean for 15 releases, then it wasn't. Catch the version change before it ships.
See how ControlLoop catches itFind the agent that can read your data, take orders from strangers, and send it out
Sensitive data, untrusted input, and an outbound channel in one agent is a leak waiting for a prompt.
See how ControlLoop catches itKeep AI agents in CI from holding write tokens with no brakes
An agent triggered by any issue comment, holding a write token, takes orders from the public.
See how ControlLoop catches itShow your auditor what every agent could do, and who approved it
Every verdict, exception, and approver for an audit window, mapped to SOC 2 and ISO/IEC 42001.
See how ControlLoop catches it