Skip to content

fix(review): make the safety feature override force-on-only - #2386

Merged
JSONbored merged 1 commit into
mainfrom
claude/safety-feature-precedence
Jul 1, 2026
Merged

fix(review): make the safety feature override force-on-only#2386
JSONbored merged 1 commit into
mainfrom
claude/safety-feature-precedence

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

What

resolveConvergedFeature's precedence for the safety converged feature (prompt-injection defanging via defangReviewInput) was: global env kill-switch → per-repo features.safety override (either direction) → allowlist default. Since .gittensory.yml lives in the repo itself — writable by a lower-trust actor than the self-host operator — a repo owner (or anyone with push access to a repo) could set features.safety: false and silently defeat a security control the operator explicitly turned on instance-wide, with no warning surfaced anywhere. This is distinct from secret_leak, which is a separate, already-unconditional mechanism.

Fix

resolveConvergedFeature (src/review/feature-activation.ts) now special-cases safety: an override can only tighten (force it on), never loosen it. features.safety: false is treated as "no opinion" and falls through to the GITTENSORY_REVIEW_REPOS allowlist default instead of forcing the feature off. The master env kill-switch is checked first as before, so a true override still can't turn safety on when the operator hasn't enabled it globally.

rag, reputation, and unifiedComment are untouched — their per-repo override still wins in both directions, since they aren't security-critical and the issue's acceptance criteria explicitly calls for their existing flexibility to be preserved.

Went with the primary recommended fix (precedence inversion) rather than the issue's fallback (an audit_events warning on opt-out): resolveConvergedFeature is a deliberately pure, synchronous, no-I/O function (per its own doc comment) reused across many hot-path call sites, so bolting an async DB write onto it would be a real architecture change for a feature this narrow. Inverting the precedence closes the gap completely rather than just making it visible.

Tests

  • 3 new tests on the pure resolveConvergedFeature: a false override is ignored (falls through to the allowlist default, both allowlisted and not), a true override still force-activates even when not allowlisted, and the master kill-switch still wins over a true override.
  • 1 new test on the async convergedFeatureActive DB-backed path, confirming the fix holds through the full stack.
  • All pre-existing feature-activation.test.ts tests (which exercise rag specifically, plus an all-features sweep that never happens to hit safety as the "other feature should stay off" case) pass unchanged, confirming the other three features are unaffected.
  • npx tsc --noEmit clean.
  • Scoped: feature-activation.test.ts — 12 passed.
  • Targeted regression sweep: safety-wiring.test.ts, safety.test.ts, ai-review.test.ts, ai-review-advisory.test.ts, gate-check-policy.test.ts (the tests most likely to exercise this path) — 214 passed.
  • Diff-range coverage-gap check on src/review/feature-activation.ts: fully covered.
  • Full unsharded npm run test:coverage: 5604 passed, 4 skipped (pre-existing/unrelated), 0 failed.
  • npm audit --audit-level=moderate: 0 vulnerabilities.

Advances #1936. Closes #2269.

resolveConvergedFeature's precedence let a per-repo features.safety
override force the prompt-injection defense OFF even when the
operator enabled it globally. .gittensory.yml lives in the repo
itself, writable by a lower-trust actor than the operator, so a repo
owner (or anyone with push access) could silently defeat a security
control the operator believed was instance-wide, with no warning.

Special-case safety in resolveConvergedFeature: an override can only
tighten (force-on), never loosen. false is now treated as "no
opinion" and falls through to the allowlist default instead of
forcing the feature off. rag/reputation/unifiedComment are
unaffected — their override still wins in both directions.
@dosubot dosubot Bot added the size:S label Jul 1, 2026
@loopover-orb

loopover-orb Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ Gittensory review result - approve/merge recommended

Review updated: 2026-07-01 19:21:25 UTC

2 files · 1 AI reviewer · no blockers · readiness 75/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
The change correctly makes only the `safety` converged feature asymmetric: the global flag remains the master kill-switch, `features.safety: true` can force activation, and `features.safety: false` now falls through to the allowlist default instead of disabling the feature. The existing behavior for `rag`, `reputation`, and `unifiedComment` is preserved because their override path remains unchanged after the safety special case. The added unit coverage exercises the direct resolver and the async manifest-backed path for the security-relevant override case.

Nits — 3 non-blocking
  • nit: `src/review/feature-activation.ts:4` still describes `.gittensory.yml` as `container-private`, which now conflicts with the new security rationale saying the file is repo-controlled; update that comment so the threat model is internally consistent.
  • `src/review/feature-activation.ts:4`: revise the top-level comment to avoid saying `.gittensory.yml` is container-private, or explicitly distinguish the historical operator-owned model from the current repo-controlled manifest path.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #2269
Related work ⚠️ 2 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (size label size:S; 1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 67 registered-repo PR(s), 57 merged, 589 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 67 PR(s), 589 issue(s).
Gate result ✅ Passing No configured blocker found.
Review context
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Review top overlaps.
  • Add a concise scope and risk note.
  • Triage stale or unlinked PRs.
  • No action.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added gittensor gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. labels Jul 1, 2026
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.71%. Comparing base (17d70b2) to head (951bcfd).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2386   +/-   ##
=======================================
  Coverage   95.71%   95.71%           
=======================================
  Files         222      222           
  Lines       24661    24662    +1     
  Branches     8949     8950    +1     
=======================================
+ Hits        23605    23606    +1     
  Misses        433      433           
  Partials      623      623           
Files with missing lines Coverage Δ
src/review/feature-activation.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

fix(review): features.safety is per-repo disableable via .gittensory.yml even when the operator enabled it globally

1 participant