Security¶
Compass adds executable hooks, a Python CLI and agent instructions to a development environment. Review it as tooling that can read and change the repositories available to your user account.
Trust boundaries¶
| Surface | What it can do | Primary control |
|---|---|---|
| Claude Code hooks | Inspect and block tool calls; write issue state | Review the scripts and pin the installed source. |
| Compass CLI | Read project files, write .compass/, run configured checks |
Limit project-command execution and CI permissions. |
| Commands, agents and skills | Instruct Claude Code how to act on a repository | Install only from a trusted, reviewed revision. |
| Project governance | Change routing and, when enabled, run project checks | Treat governance changes as code changes. |
.compass/ artifacts |
Persist issue content and command evidence in Git | Keep secrets and sensitive output out of artifacts. |
Before installing¶
Review and pin the source¶
Compass publishes through the Claude Code plugin marketplace. A marketplace install is still third-party executable code arriving in your environment - treat it with the same care you would give any dependency, and pin what you install.
Do not install a fork or mutable branch you do not trust. For organisational use:
- pin Compass to a reviewed commit SHA;
- mirror it to a location your organisation controls where appropriate;
- review changes before updating the pin; and
- protect the mirror and release process like other development tooling.
The most important executable surfaces are:
cli/compass
cli/compass_pkg/
hooks/
scripts/install.sh
governance/*.yml
Commands, agents, skills and always-loaded instructions are also security relevant: they influence an agent that can change your repository.
Understand the hooks¶
The Claude Code adapter registers four local hooks:
| Hook | Purpose | Can block? |
|---|---|---|
pre-tool.sh |
Applies approach-aware red-before-green checks before edits. | Yes |
post-tool.sh |
Updates the issue history after relevant actions. | No |
stop.sh |
Warns about unfinished or inconsistent issue state. | No |
session-start.sh |
Loads the operating contract into the session. In every repository, it also keeps the status line launcher, compass-statusline, in the plugin's own data folder under ~/.claude/plugins/data/; it writes nothing else and prints nothing for it. |
No |
They run locally with the same permissions as your user. The shipped hooks do not need network access, but check the installed revision rather than relying on this document.
Dependencies¶
The CLI bundles a pinned copy of PyYAML under cli/vendor/yaml/. It adds that
copy to sys.path only inside Compass processes; it does not install packages
into the user's Python environment.
THIRD-PARTY-NOTICES.md records the version, source, hash and licence. Teams
with stronger supply-chain requirements can reproduce the vendored tree from
the pinned upstream source and compare it.
jsonschema is optional and is not bundled. Installing it enables fuller JSON
Schema validation in policy and issue linting.
Run compass --version to see which Compass and PyYAML versions are active.
This repository's own CI pins every action by commit SHA and installs its
test tools from .github/requirements/, which lists each package at an exact
version; the cucumber-js adapter installs from its lockfile with npm ci. The
workflows in ci/ that you copy pin their actions the same way.
Project guardrails are executable code¶
A project guardrail can use command-passes - a shipped check whose
parameter is the command, not a setting that selects one. compass check
runs that string with subprocess.run(..., shell=True) from the project root. Its command: form still runs a shell; the script:
form does not - it runs a file directly, so a value cannot smuggle in a
pipeline or a substitution. That difference is the reason to prefer the script
form, and it is the only reason.
The command form runs from the project root with the Compass process's permissions.
checks: [command-passes]
params:
command: "python3 scripts/architecture-fitness.py"
Treat governance files that declare command-passes as code, not as configuration - anything a shell can do, a project guardrail can do. It matters because
continuous integration normally runs on a pull request before it is
approved, so a contribution's command runs before anyone has read it. The
explicit opt-in that closes this gap is
issue #65.
Safer default¶
Project commands are disabled unless .compass/config.yml opts in:
allow_project_commands: true
This prevents accidental execution; it is not a security boundary because a repository change can edit both the command and the setting.
Prefer the non-shell script form:
checks: [command-passes]
params:
script: scripts/architecture-fitness.py
args: ["--strict"]
Compass resolves the script beneath the project root and passes arguments without shell interpolation. A symlink that resolves outside the root is refused.
Untrusted pull requests¶
Compass refuses project commands unless the CI environment positively reports the contribution as trusted. Unknown, blank or unreadable trust state is a refusal, not permission.
This reduces exposure but is not an unforgeable sandbox. A pull-request branch can often change its workflow and environment. The effective boundary remains the CI provider's controls, including restricted secrets, read-only tokens and approval policy.
For an unrecognised CI provider, set trust outside repository-controlled files:
env:
COMPASS_CONTRIBUTION_TRUST: trusted
Do this only for a job whose trigger and source are genuinely trusted.
CI hardening¶
Compass cannot defend against a contributor with push access. They can add a command, set the opt-in and merge. No arrangement of in-repo configuration defends a repository against its own contents - GitHub's own answer to the same problem is to withhold secrets from forks rather than to trust a setting.
And the refusal is not unforgeable either. On a pull_request
event GitHub runs the workflow from the contribution's own merge ref, so the
contribution controls every environment variable Compass sees. The cheap
attacks fail: blanking GITHUB_EVENT_NAME or CI refuses, claiming
COMPASS_CONTRIBUTION_TRUST=trusted does not override GitHub's report of a
fork, and a payload inside the checkout is rejected. What remains is a forged
event payload written outside the checkout and pointed at. That takes real
work rather than one line of YAML - and you should know it is there.
What actually bounds a fork pull request is that GitHub withholds your secrets from it and issues a read-only token. That is the boundary; the steps below limit what a fork can reach inside it.
- Declare minimum token permissions. The reference workflow uses
permissions: contents: read. - Do not expose secrets to untrusted contributions.
- Avoid
pull_request_targetfor workflows that check out and run untrusted code. - Add a required-approval gate for workflows from forks where appropriate.
- Review changes to workflows, governance, scripts and hooks using code-owner rules or equivalent protection.
- Run project commands in an isolated, short-lived runner where possible.
Persistent artifacts¶
.compass/work/ is committed to the repository. It can contain command
output, test evidence, approvals, decisions and an append-only development
log.
Do not place secrets, access tokens, personal data or sensitive production output in these files. Redact evidence before committing it, while retaining enough information for the gate to remain meaningful.
Known limits¶
- Shell commands can hide file writes inside scripts and build tools. Hook detection is best-effort for shell activity.
- Shell scripts, makefiles and extensionless scripts are not part of the default production-file classification for red-before-green checks.
- A contributor with trusted push and merge rights is inside the repository's trust boundary.
- Agent instructions are powerful even when they are Markdown rather than executable code.
- Compass does not sandbox Claude Code, Python, Git or project commands.
See the safety contract for the guarantees Compass makes despite these limits.
Recommended adoption posture¶
- Inspect the pinned revision.
- Install in a disposable repository.
- Run the install smoke test.
- Start with project commands disabled.
- Add least-privilege CI permissions.
- Enable project commands only after reviewing each declared script.
- Treat Compass upgrades as tooling upgrades, with diff review and rollback.
If your environment needs a stronger boundary, add sandboxing and policy at the runner or operating-system level. Do not infer isolation from Compass itself.