Repository navigation
Validate repo policy overrides and the safety config maps - #61
Conversation
A repo's .github/ion.yml `policy` block was accepted as any object, and
anyone who can push to that repo can edit it. A value such as
`required_files: 1` or `branch_protection: true` then crashed
compute_analysis for the whole org on every run, and
`required_approvals: two` crashed the execute preflight. The org and
repo `policy` now share one schema that types the keys ion reads, so a
malformed override is ignored and reported as `bad_override` like any
other invalid override. A falsy value still turns a check off.
`quorum_by_kind` was missing from the schema, and `writes.caps` and
`guards` accepted any key. A misspelt kind, write type or guard
silently kept the weaker default, and `quorum_by_kind: {merge: 0}`
loaded and then raised IndexError in evaluate() for every awaiting
proposal of that kind. These are now validated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PpXZJdMN5FExq9THy5VNg
|
The
Generated by Claude Code |
|
Independent review by a different agent than the drafter. Verdict: approve. What I checked. The diff touches only
Suite on the branch: 261 passed, against 250 on This PR and #62 both touch This is posted as a comment because the reviewer's account is the same GitHub account that authored the PR, so GitHub will not accept a formal approving review from it. Review is requested from the other maintainer accounts. Under the repo rule (a PR merges once a different agent than its drafter has reviewed it), this review is the cross-agent pass.
|
Found in a third audit pass. Each change has a test that fails without it.
Changes
ion/config.py(REPO_OVERRIDE_SCHEMA): a repo's.github/ion.ymlpolicyblock was accepted as any object. Per the README, anyone who can push to a repo can edit that file, and a malformed value took down the whole org:policy: {required_files: 1}or{required_files: [1]}→TypeError/AttributeErrorincompute_analysis(policy.py:127-128).ion runprintsanalyze failed: ...and skips propose, sync and execute for every repo, every run, until someone finds the file.ion plan-configcrashes the same way.policy: {branch_protection: true}→AttributeErroratpolicy.py:84(bp.geton a bool).policy: {branch_protection: {required_approvals: two}}with orgbranch_protection: fix→ValueErrorin the execute preflight (writes.py,int("two")), so no approved proposal runs.The org
policyand the repo override now share one schema. It types the keys ion reads:default_branch,branch_protectionand its five fields,dependabot_alerts,secret_scanning,required_files. A malformed override is ignored and reported asbad_override, like any other invalid override. A falsy value (branch_protection: false,required_files: null) still turns a check off, as before; unknown keys are still ignored.ion/config.py(SCHEMA): three safety-relevant maps silently kept the weaker default on a typo.quorum_by_kind(in the README key table) was not in the schema at all.{archives: 2}or{Archive: 2}left archive quorum atquorum.{merge: 0}loaded, althoughquorum: 0is refused, and thenevaluateraisedIndexErroratcore.py:70for every awaiting merge, which failed sync-approvals and the execute preflight. Kinds are now limited totracker.KINDS, with values ≥ 1.writes.caps:{archive: 0}(forarchive_repo) left the archive cap at 1. Keys are now limited towrites.TYPES.guards:{max_archive: 0}left the breaker's archive limit at 2. Unknown keys are now refused.A config with one of these typos now fails at load with
invalid config: ....ion doctorreports it too.Not in this PR: a repo override can still validly ask for weaker branch protection than the org (for example
require_pr: false). With the org atbranch_protection: fix, the breaker then refuses the lowering, and that aborts the whole--applyrun. That is the same root cause as #64 (repos stricter than the policy are reported as drift).Validation
uv run pytest -q: 261 passed (250 existing, 11 new). 9 of the new tests fail with theconfig.pychange reverted. The other 2 are regression guards: the org config already refusedbranch_protection: true, and the falsy opt-out must keep working.scripts/check_licenses.py: 0 not allowed.🤖 Generated with Claude Code
https://claude.ai/code/session_016PpXZJdMN5FExq9THy5VNg