Skip to content

fix(coder): address #827 + #828 auto-review findings (security + runtime)#832

Merged
kovtcharov merged 1 commit into
coderfrom
fix/coder-security-and-runtime-bugs
Apr 20, 2026
Merged

fix(coder): address #827 + #828 auto-review findings (security + runtime)#832
kovtcharov merged 1 commit into
coderfrom
fix/coder-security-and-runtime-bugs

Conversation

@kovtcharov
Copy link
Copy Markdown
Collaborator

Summary

Six fixes flagged by the auto-review bot: one Critical (security), five Important (two on #827, two on #828, one on both). All 395 tests pass on coder with the fixes.

Changes

Critical (security):

  • oss_reuse.py import_with_attribution — path traversal on LLM-controlled dest_path. Now resolves + relative_to(root)-checks; raises AttributionError on escape.

Important:

  • oss_reuse.py _validate_license_filter — unknown SPDX ids silently dropped; now raises per CLAUDE.md fail-loudly.
  • tools/github.py gh_pr_merge — hardcoded --admin; now gated behind admin_override=False default.
  • repo_binding.py webhook round-trip — only did positive check; added wrong-signature + wrong-payload discrimination.
  • tools/debug.py add_instrumented_trace — emitted logger.debug(...) requiring pre-bound logger; now inlines __import__('logging') lookup.
  • tools/debug.py diff_behaviorgit switch - after two detached switches returns to wrong ref; now captures + explicitly restores original HEAD.

Test plan

  • pytest tests/coder/ tests/eval/ — 395 pass
  • 7 new regression tests in test_fixes_827_828.py cover each fix
  • test_add_instrumented_trace_* now asserts the mutated module actually imports (previously asserted only the string was written)

Six fixes flagged by the auto-review bot on PRs #827 and #828:

Critical:
- Path traversal in import_with_attribution (oss_reuse.py). dest_path is
  @tool-exposed (LLM-controlled) and was joined into repo_root without
  containment check — absolute paths and ../ traversal escaped. Now
  resolve + relative_to-check + AttributionError on violation.

Important:
- License filter silent-drop in _validate_license_filter (oss_reuse.py).
  Unknown SPDX ids were silently dropped; a typo ("MIT-License") produced
  an empty search with no feedback. Now raises ValueError per CLAUDE.md
  fail-loudly.
- gh_pr_merge hardcoded --admin (tools/github.py). Bypasses branch
  protection unconditionally. Gate behind admin_override=False default;
  --admin only when explicitly requested.
- Webhook signature round-trip was effectively a no-op (repo_binding.py).
  Positive check alone would pass a verifier that always returns True.
  Added a wrong-signature rejection test and a wrong-payload rejection
  test — verifier must discriminate on both.
- add_instrumented_trace produced broken targets (tools/debug.py). Probe
  wrote `logger.debug(...)` into files that may not bind a module-level
  logger; NameError at import. Now inlines __import__('logging') lookup.
- diff_behavior left HEAD detached at the wrong ref (tools/debug.py).
  ``git switch -`` after two detached switches returns to the FIRST
  detached state, not the caller's original branch. Now captures
  original_head via symbolic-ref + rev-parse at entry and restores
  explicitly in the finally block.

Tests:
- 7 new regression tests in tests/coder/test_fixes_827_828.py.
- Updated test_debug_tools.py::test_add_instrumented_trace_* to assert the
  mutated module actually imports (previously only asserted the string
  was written — would have passed even on a broken probe).
- Updated test_debug_tools.py::test_diff_behavior_* to expect the new
  symbolic-ref call at sequence start.

All 395 tests pass on coder HEAD with the fixes.
@kovtcharov kovtcharov merged commit 8a2cdf1 into coder Apr 20, 2026
7 checks passed
@kovtcharov kovtcharov deleted the fix/coder-security-and-runtime-bugs branch April 20, 2026 15:20
@github-actions github-actions Bot added the tests Test changes label Apr 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Summary

Clean, well-scoped follow-up that addresses every auto-review finding from #827 and #828 with matching regression tests. The path-traversal fix in import_with_attribution is the standout — correctly uses resolve() + relative_to() which also blocks symlink escapes, not just ... Each fix is paired with a test that would have caught the original bug, and the add_instrumented_trace test now verifies the mutated module actually imports (the previous assertion only checked string contents). One tiny nit below, otherwise nothing blocking — and since the PR is already merged, these are FYI for future passes.

Issues Found

🟢 Minor — Unused Path import (tests/coder/test_fixes_827_828.py:16)

Path is imported but never referenced — the path-traversal tests use the tmp_path pytest fixture directly (already a Path). isort/flake8 will flag this.

from __future__ import annotations

import pytest

from gaia.coder import oss_reuse

🟢 Minor — Behaviour change to _validate_license_filter return semantics (src/gaia/coder/oss_reuse.py:442)

Before the fix, the function returned requested & PERMISSIVE_LICENSES (intersection — silently widened to allowlist on empty). After, it returns requested directly. For the happy path these are identical once unknown ids raise, but any caller that previously relied on "pass anything, I'll get back a sanitised permissive subset" now gets ValueError. The new behaviour is correct per CLAUDE.md's fail-loudly rule; flagging only so future callers know the contract shifted from "sanitise" to "validate". Test coverage (test_license_filter_still_accepts_known_permissive_subset) confirms the happy path. No change requested.

🟢 Minor — diff_behavior original-HEAD capture happens before git stash (src/gaia/coder/tools/debug.py:471-477)

Ordering is fine today (stash doesn't change HEAD), but if someone later changes git stash to git stash --keep-index or introduces an auto-commit path, HEAD could move before capture. A short comment explaining the ordering invariant would protect the next editor. Not blocking.

Strengths

  • Fail-loudly discipline applied consistently — every fix replaces silent degradation (silent license drop, hardcoded --admin, swallowed path escapes, positive-only HMAC check) with a raise or an explicit-opt-in flag, matching the CLAUDE.md rule verbatim.
  • Webhook discrimination tests are thoughtful (repo_binding.py:347-364) — both wrong-sig and wrong-payload negative tests catch the "verifier always returns True" failure mode that the positive-only round-trip missed.
  • Probe self-containment__import__('logging').getLogger(__name__) sidesteps the whole "does the target module bind logger?" question and makes the probe safe to inject into any Python file. Test upgrade to actually exec_module the mutated file (test_debug_tools.py:173-181) is exactly the right assertion level.
  • HEAD restoration via symbolic-ref short-circuit correctly distinguishes branch vs. detached and restores with the matching git switch variant — handles the two-detached-switches case that broke git switch -.

Verdict

Approve with suggestions — the unused-import cleanup is the only mechanical change worth making; everything else is either informational or defensive. Nice surgical PR.

kovtcharov added a commit that referenced this pull request Apr 20, 2026
…view pass) (#834)

## Summary

Final cleanup pass to complete the `coder` branch for EM testing. Five
Important + three Minor findings across the Phase 5/6/11 auto-reviews.
All 395 tests pass.

## Changes

- `test_self_fix/test_cli.py` — `pytest.raises` so a silent-pass
regression in argparse can't pass the test. [#825, #829]
- `test_integration_e2e.py` — real `PATH` prepend via
`monkeypatch.setenv` instead of a no-op assignment that leaked env.
[#829]
- `test_fixes_827_828.py` — drop unused `Path` import. [#832]
- `loop_driver.py` — narrow broad `except Exception` around
`review_gate` and `notify_em` to `(RuntimeError, CalledProcessError,
OSError)`. Programming errors now surface per CLAUDE.md fail-loudly.
[#825]
- `loop_driver.py` + `verifier.py` — `_append_notes` / `_append_note`
raise `ValueError` on corrupted or wrong-type `notes_json` instead of
silently replacing with `[]`. [#825]

## Test plan

- [x] `pytest tests/coder/ tests/eval/` — 395/395 pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant