The permission system is what makes the fix-apply layer safe. Before IssueLoop executes any shell command as part of a proposed fix, it checks the command against an explicit allowlist defined inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/onenot8/issueLoop/llms.txt
Use this file to discover all available pages before exploring further.
permission.yaml. If no matching entry exists, the command is blocked, logged, and a PermissionDenied exception is raised. Nothing runs implicitly — every allowed command must be declared.
permission.yaml structure
The file has two top-level sections:global entries apply to every repository, while per_repo entries apply only to the named repository. Both sections support exact string matches and regular expression patterns.
allowed_exact and allowed_patterns lists inside each section are independent; a command only needs to satisfy one of them.
Allowed exact vs allowed patterns
IssueLoop uses two distinct matching strategies:allowed_exact — the command string must match the listed value character-for-character. This is the most restrictive and most predictable option. Use it when the command is fixed and never varies.
allowed_patterns — the command string is tested against each pattern using Python’s re.fullmatch. The pattern must match the entire command string, not just a substring. Use this when the command includes variable arguments such as file paths or flags.
Test manifest commands are always allowed
Commands listed intest_manifest.json for a given repository are automatically trusted for that repository. This covers the commands IssueLoop already knows how to run — pytest, unittest, or whatever runner the repo uses — so re-running tests after a fix never requires an explicit permission entry.
The manifest is stored at data/test_manifest.json and is populated by issueloop.scan_repo(). Commands from the manifest count as an implicit allowed_exact set scoped to their repository. They are merged with any explicit entries before the check is evaluated.
Config file resolution order
IssueLoop resolvespermission.yaml by searching the following locations in order, stopping at the first match:
ISSUELOOP_PERMISSION_PATHenvironment variable — if set, the value is used as an absolute path to the config file, with no further searching../config/permission.yaml— relative to the current working directory when the process starts.config/permission.yamlin the IssueLoop checkout — theconfig/directory at the root of the cloned repository (only relevant for development installs with-e .).- Bundled package defaults — the
_defaults/permission.yamlfile shipped inside theissuelooppackage itself.
pip install issueloop (non-editable) always falls back to the bundled defaults rather than failing silently. You only need to create a local config/permission.yaml when you actually want to allowlist specific commands.
Audit log
Every permission check — whether it results in an allow or a deny — is appended todata/logs/permission_audit.jsonl. Each line is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
ts | ISO-8601 string | UTC timestamp of the check. |
event | string | "allowed_exact", "allowed_pattern", or "denied". |
repo | string | The repository name the command was checked against. |
command | string | The full command string that was evaluated. |
detail | string | For "allowed_pattern" events, the pattern that matched. Empty for other events. |
PermissionDenied exception
run_guarded is the internal function that enforces permissions before running any shell command. If check_permission returns False, run_guarded raises PermissionDenied immediately — the subprocess is never spawned.
run_guarded directly (for custom tooling built on top of IssueLoop), you should handle PermissionDenied explicitly: