How AllowRepoCommands Works in no-mistakes: Securing Repo Config Execution
The AllowRepoCommands field in no-mistakes defaults to false to prevent supply-chain attacks, ensuring code-executing configurations like shell commands and agent selections are only read from the trusted default branch, never from a contributor's potentially malicious pushed branch.
The no-mistakes repository implements a default-deny security model for repository configuration. By separating code-executing fields from safe configuration options, the system prevents malicious contributors from injecting arbitrary shell commands through pull requests.
The Security Problem: Code-Executing vs. Safe Config Fields
no-mistakes categorizes repository configuration into two distinct risk classes. Code-executing fields include commands.{lint,test,format} and agent, which specify shell commands the daemon runs on the host and the AI agent spawned with maintainer credentials. Safe fields include ignore patterns, auto-fix limits, intent extraction settings, and test configurations—data that cannot execute arbitrary code.
If the daemon honored code-executing fields from a contributor's pushed branch, a malicious actor could inject rm -rf / into the test command or switch the agent to an untrusted executable. The AllowRepoCommands field exists specifically to gate whether these high-risk configurations can be sourced from untrusted branches.
How AllowRepoCommands Is Defined
The configuration structure lives in internal/config/config.go. The RepoConfig struct defines the field with explicit documentation about its security constraints:
type RepoConfig struct {
// … other fields …
// AllowRepoCommands opts in to honoring the code‑executing selection fields
// (commands.{test,lint,format} and agent) from a contributor's pushed branch
// instead of the trusted default‑branch copy. It is read ONLY from the
// trusted default‑branch copy of .no‑mistakes.yaml (never the pushed SHA),
// so a contributor cannot self‑enable it. Default false.
AllowRepoCommands bool `yaml:"allow_repo_commands"`
// … other fields …
}
(source: config.go line 70‑80)
Crucially, the daemon only reads this flag from the trusted default-branch copy. A contributor cannot enable this feature on their feature branch because the system ignores the flag when parsing the pushed configuration.
The EffectiveRepoConfig Logic
When a run starts, the daemon loads two copies of the repository configuration: the trusted version from the default-branch tip, and the pushed version from the contributor's checkout. The EffectiveRepoConfig function in internal/config/config.go implements the merging logic:
func EffectiveRepoConfig(pushed, trusted *RepoConfig, allowRepoCommands bool) *RepoConfig {
if pushed == nil { pushed = &RepoConfig{} }
effective := *pushed
if allowRepoCommands {
return &effective // honour everything from the pushed copy
}
// otherwise, pull only the safe fields from the pushed copy
if trusted != nil {
effective.Commands = trusted.Commands
effective.Agent = trusted.Agent
} else {
effective.Commands = Commands{}
effective.Agent = ""
}
return &effective
}
(source: config.go line 86‑106)
If allowRepoCommands is false (the default), the function overwrites the pushed branch's Commands and Agent fields with values from the trusted copy, or clears them entirely if no trusted copy exists. This ensures executable commands never originate from untrusted input unless the maintainer explicitly opts in via the default branch configuration.
Daemon Implementation
The daemon's manager retrieves the flag from the trusted configuration before calling the effective config logic. As implemented in internal/daemon/manager.go:
allowRepoCommands := trustedRepoCfg != nil && trustedRepoCfg.AllowRepoCommands
(source: daemon/manager.go line 379)
This boolean then drives the security decision. The daemon uses LoadRepoFromBytes to parse the trusted default-branch YAML and LoadRepo to parse the pushed working tree configuration. Only the trusted copy determines whether contributor-defined commands are permissible.
Configuration Examples
Secure Default Configuration
# .no-mistakes.yaml on the default branch
agent: claude
commands:
lint: "golangci-lint run ./..."
test: "go test ./..."
# allow_repo_commands omitted → defaults to false
With this configuration, any contributor attempting to modify commands in a feature branch will see their changes ignored. The daemon executes only the trusted default-branch commands.
Blocked Malicious Attempt
# .no-mistakes.yaml on a feature branch
commands:
lint: "rm -rf $HOME/* && echo 'pwned'"
allow_repo_commands: true # <-- ignored because the trusted copy doesn't have it
Even though the contributor sets allow_repo_commands: true, the daemon ignores this value because it only reads the flag from the trusted default branch. The malicious lint command is discarded and replaced with the trusted version.
Maintainer Opt-In Scenario
# .no-mistakes.yaml on the default branch
allow_repo_commands: true
agent: claude
commands:
lint: "golangci-lint run ./..."
When the maintainer commits this to the default branch, contributors may now customize commands and agent in their feature branches. The daemon honors these values because the trusted copy explicitly enables the functionality.
Summary
AllowRepoCommandsdefaults tofalseto enforce a secure-by-default posture.- The flag is only read from the trusted default-branch copy, preventing contributors from self-enabling command execution.
- Code-executing fields (
commandsandagent) are overwritten with trusted values when the flag isfalse. - Safe fields (ignore patterns, limits, intent settings) are always honored from the pushed branch regardless of the flag.
- The logic is implemented in
EffectiveRepoConfigwithininternal/config/config.goand invoked by the daemon manager ininternal/daemon/manager.go.
Frequently Asked Questions
What happens if a contributor sets allow_repo_commands: true in their PR?
The daemon ignores the flag. Because AllowRepoCommands is only read from the trusted default-branch copy of .no-mistakes.yaml, a contributor cannot enable command execution from their feature branch. The system uses the trusted copy's value (default false) and sanitizes the pushed configuration accordingly.
Which configuration fields are considered "code-executing" in no-mistakes?
The commands struct (containing lint, test, and format shell commands) and the agent field (specifying which AI agent to spawn) are treated as code-executing. These fields directly influence process execution on the host system, making them high-risk targets for injection attacks.
How can a maintainer safely enable contributor-defined commands?
To permit contributors to customize commands, the maintainer must add allow_repo_commands: true to the .no-mistakes.yaml file on the default branch and commit this change. Once present in the trusted configuration, the daemon will honor commands and agent values from pushed branches, allowing per-branch customization while maintaining auditability through the default branch history.
Where are the security guarantees tested?
The end-to-end security guarantees are verified in internal/e2e/repo_config_security_test.go, which tests that malicious configurations from pushed branches cannot execute. Unit tests for the loading and defaulting behavior reside in internal/config/config_repo_trust_test.go. These files ensure the AllowRepoCommands logic prevents supply-chain attacks even as the codebase evolves.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →