Skip to content

Fix #648: recognize root conftest.py and mv/git mv of test files - #689

Closed
Som0111 wants to merge 9 commits into
DobermanCore:mainfrom
Som0111:fix-issue-648
Closed

Fix #648: recognize root conftest.py and mv/git mv of test files#689
Som0111 wants to merge 9 commits into
DobermanCore:mainfrom
Som0111:fix-issue-648

Conversation

@Som0111

@Som0111 Som0111 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Pull Request

Slice

What this PR does

  • Added conftest.py to TEST_FILE_GLOBS in src/doberman/engine/rules/paths.py so a repo-root conftest.py is recognized as a test file (previously only tests/conftest.py matched via tests/**).
  • Added is_test_file() (public helper, mirrors the existing cross-module reuse pattern) so commands.py can reuse the same glob table.
  • Added _shell_mv_source() / _shell_command_text() to recognize a plain mv SRC DST / git mv SRC DST shell payload and extract its source operand.
  • _is_delete_or_rename() now also returns True when the evaluated path is exactly that shell-mv source (backward compatible, new param is optional).
  • ProtectedPathRule.evaluate() extracts the mv source from raw arguments and adds it as an extra path candidate, so it flows through the existing blocked/sensitive/test-file checks.
  • Added _mv_rename_source() in src/doberman/engine/rules/commands.py (same shape recognition, keyed on the real git verb via the existing _git_leading_globals).
  • _segment_verdict() now AUTHs (ReasonCode.test_file_removal) when a segment's mv/git mv source canonicalizes to a test-file path, reusing paths.is_test_file() + doberman.canonical.canonicalize().
  • Updated docs/LIMITATIONS.md to reflect the narrower (plain two-operand-shape-only) fix, replacing the two paragraphs that documented these gaps.

Tests added (run in CI)

  • tests/unit/test_rule_paths.py::test_root_conftest_delete_requires_auth
  • tests/unit/test_rule_commands.py::test_mv_or_git_mv_of_test_file_requires_auth (parametrized: mv and git mv)

Changelog

  • changelog.d/648.fixed.md fragment added: "Root-level conftest.py and mv/git mv of a test file now correctly raise AUTH instead of passing silently."

Public-release safety (doberman-core only)

  • Contains nothing from the "not allowed" list: no enterprise/hosted code, no proprietary detection, no customer data, no secrets, no commercial-license code
  • Core still builds/tests/runs with NO enterprise package installed

Security checklist

  • Fails closed on error / uncertainty
  • No secret, full file, or unredacted prompt logged or committed
  • Any guardrail/learning change is raise-only (no silent loosening) — this only adds/tightens recognition, never removes or softens an existing check
  • Every BLOCK/AUTH carries reason codes + a human explanation (ReasonCode.test_file_removal)
  • doberman-core does not import doberman_enterprise

Edge cases covered / Deviations from plan / Risks introduced

  • Only plain two-operand mv SRC DST / git mv SRC DST shapes are recognized; more complex shell constructs (globs, xargs, multi-file mv, quoting edge cases) are out of scope per the issue's "Out of scope" note and are not covered.
  • No change to already-recognized cases (existing delete/rename detection for filenames matching prior globs is untouched).
  • Diff-content inspection (e.g. skipped tests, deleted coverage lines) remains explicitly out of scope, per issue and docs/LIMITATIONS.md.

@fu351

fu351 commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Thanks @Som0111, the conftest.py glob and the commands.py half of this are both right, but I need to hold the paths.py branch. I ran it on your branch and mv /tmp/downloaded.txt ./notes.txt now comes back BLOCK with "possible traversal or symlink escape", same for mv ../scratch/a.py ./a.py and git mv /home/u/x.md docs/x.md. The mv SOURCE gets appended to paths, and the first branch in _evaluate_one blocks anything whose canonical form escapes the repo root, so moving a file into the repo from outside is now a hard block nobody can override. Dropping the paths.py mv branch fixes it, the commands.py check already catches the test-file move you were after. Push that to this branch and I'll take another look.

@Som0111

Som0111 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @fu351 for catching that — you're right, the paths.py mv-source extraction was wired into _evaluate_one's full path-candidate list, so it was running through every blocking check instead of just the test-file check.

I've reverted that entire branch: removed _shell_mv_source(), _shell_command_text(), and the mv-source extraction in ProtectedPathRule.evaluate(), and reverted _is_delete_or_rename() back to its original 2-arg signature. Kept the conftest.py glob addition and is_test_file() since commands.py depends on the latter.

commands.py's mv/git mv detection is untouched — confirmed it already handles the test-file-move case correctly on its own.

Added a regression test confirming mv /tmp/downloaded.txt ./notes.txt and mv ../scratch/a.py ./a.py are no longer blocked.

All tests + ruff pass. Ready for another look whenever you get a chance.

@fu351

fu351 commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Merged via a landing branch, thanks @Som0111! Dropping the paths.py mv-source wiring was the right call, moving a file into the repo from outside canonicalizes outside the root and was false-positiving as a traversal block, and the commands.py mv/git-mv detection catches a test-file move on its own without it. The regression test you added pinning that mv /tmp/downloaded.txt ./notes.txt passes is what I wanted to see before this went in. Nothing in your code changed on the landing: main now needs branches up to date and refuses merge commits, so I cherry-picked your commits onto main under your name (landing PR #695, now 3d7e5b8) and merged that, which closed #648. If you want the next one, #647 (level-5) is the same idea for the other verb, rm and git rm of a test still pass with no check.

@fu351 fu351 closed this Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants