Skip to content

feat(workspace): add PATH_ALLOW_BROAD override for too-shallow roots - #2017

Open
kobaz wants to merge 1 commit into
DeusData:mainfrom
kobaz:feat/path-allow-broad-too-shallow-override
Open

feat(workspace): add PATH_ALLOW_BROAD override for too-shallow roots#2017
kobaz wants to merge 1 commit into
DeusData:mainfrom
kobaz:feat/path-allow-broad-too-shallow-override

Conversation

@kobaz

@kobaz kobaz commented Sep 2, 2026

Copy link
Copy Markdown

CBM_WS_DENY_TOO_SHALLOW ("path is too broad to index as one root") has no override today: cbm_workspace_verdict_is_overridable() only lifts CBM_WS_DENY_SENSITIVE, and allow-root --list already documents shallow/absolute refusals as "always-refused" rather than configurable. That default is right -- a bare top-level tree like "/etc" or "/home" is almost always a mistake -- but it leaves no way to say "I mean this one specific broad root, on purpose." That's a real case: a person who deliberately wants one combined project spanning several sibling repos under a shared parent has no path forward today short of physically restructuring their checkout.

PATH_ALLOW_BROAD names one exact canonical path that lifts a CBM_WS_DENY_TOO_SHALLOW verdict, and only that verdict:

  • exact match only, not a prefix -- naming one broad root must not quietly approve every root below it too, the same "/Users"-style breadth this depth rule exists to catch in the first place
  • process environment, not a recorded grant: it never appears in cbm_workspace_grant_list, and cbm_workspace_verdict_is_overridable() is untouched, so CBM_WS_DENY_ABSOLUTE and CBM_WS_DENY_SENSITIVE stay exactly as unliftable as before
  • read once per call site with getenv(), mirroring how CBM_ALLOWED_ROOT is already threaded into cbm_workspace_root_allowed() from each of its four callers, rather than read inside cbm_workspace_classify_root() itself -- that function is documented as a pure function of its arguments precisely so it stays host-independent and directly testable

Also updates the refusal message to name the fix, the same way the sensitive-root refusal already names allow-root --approve-sensitive.

Adds ws_allow_broad_root_lifts_too_shallow_for_an_exact_match_only alongside the existing too-shallow coverage, checking the exact-match requirement, the untouched sensitive/absolute paths, and that nothing is written to the grant store.

What does this PR do?

Checklist

  • Every commit is signed off (git commit -s) — required, CI rejects
    unsigned commits (DCO, see CONTRIBUTING.md)
  • Tests pass locally (make -f Makefile.cbm test)
  • Lint passes (make -f Makefile.cbm lint-ci)
  • New behavior is covered by a test (reproduce-first for bug fixes)

CBM_WS_DENY_TOO_SHALLOW ("path is too broad to index as one root") has
no override today: cbm_workspace_verdict_is_overridable() only lifts
CBM_WS_DENY_SENSITIVE, and `allow-root --list` already documents
shallow/absolute refusals as "always-refused" rather than configurable.
That default is right -- a bare top-level tree like "/etc" or "/home"
is almost always a mistake -- but it leaves no way to say "I mean this
one specific broad root, on purpose." That's a real case: a person who
deliberately wants one combined project spanning several sibling repos
under a shared parent has no path forward today short of physically
restructuring their checkout.

PATH_ALLOW_BROAD names one exact canonical path that lifts a
CBM_WS_DENY_TOO_SHALLOW verdict, and only that verdict:

- exact match only, not a prefix -- naming one broad root must not
  quietly approve every root below it too, the same "/Users"-style
  breadth this depth rule exists to catch in the first place
- process environment, not a recorded grant: it never appears in
  cbm_workspace_grant_list, and cbm_workspace_verdict_is_overridable()
  is untouched, so CBM_WS_DENY_ABSOLUTE and CBM_WS_DENY_SENSITIVE stay
  exactly as unliftable as before
- read once per call site with getenv(), mirroring how CBM_ALLOWED_ROOT
  is already threaded into cbm_workspace_root_allowed() from each of
  its four callers, rather than read inside
  cbm_workspace_classify_root() itself -- that function is documented
  as a pure function of its arguments precisely so it stays
  host-independent and directly testable

Also updates the refusal message to name the fix, the same way the
sensitive-root refusal already names `allow-root --approve-sensitive`.

Adds ws_allow_broad_root_lifts_too_shallow_for_an_exact_match_only
alongside the existing too-shallow coverage, checking the exact-match
requirement, the untouched sensitive/absolute paths, and that nothing
is written to the grant store.

Signed-off-by: Mark Murawski <github@kobaz.net>
@kobaz
kobaz requested a review from DeusData as a code owner September 2, 2026 17:46
@DeusData

DeusData commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Actionable note ahead of the review verdict, so you are not blocked on me for it.

lint / lint is red and it is yours — unusually, this is not the false positive several contributors have hit this week. CI's own pinned formatter reports violations on the exact lines you added:

src/daemon/application.c:441:63: error: code should be clang-formatted
                                   getenv("PATH_ALLOW_BROAD"), boundary_error,

src/daemon/application.c:438-441. Running the formatter over that file should clear it.

(For context, since it has caught others: our gate requires the Homebrew LLVM clang-format; a distro or standalone clang-format-20 reports spurious whole-file drift on some large files. That is not what this is — these are your own new lines.)

On the change itself, I have verified the three boundary claims in your description and they hold:

  • the lift is guarded on verdict == CBM_WS_DENY_TOO_SHALLOW alone, so CBM_WS_DENY_SENSITIVE and CBM_WS_DENY_ABSOLUTE are untouched;
  • ws_paths_equal(canonical_path, allow_broad_root) is exact equality rather than a prefix test, so naming one broad root does not quietly approve everything beneath it;
  • the decision lives in cbm_workspace_root_allowed and not in cbm_workspace_classify_root, which stays a pure function of its arguments, and cbm_workspace_verdict_is_overridable really is untouched — the two occurrences in the diff are a context line and a comment saying it is deliberately excluded.

That is a carefully drawn boundary and it is the reason this is reviewable at all.

What I cannot decide on my own is whether we want a new environment variable that lifts a workspace security refusal. A new env var is a one-way door, and this one weakens a guard by design, however narrowly. That is a maintainer call and I have put it in front of ours; I will come back with an answer rather than leaving it open-ended.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

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