feat: declare indexing roots from the CLI; size-safe compression; prune the cache from discovery - #1472
Open
DeusData wants to merge 4 commits into
Open
feat: declare indexing roots from the CLI; size-safe compression; prune the cache from discovery#1472DeusData wants to merge 4 commits into
DeusData wants to merge 4 commits into
Conversation
…walk Two loose ends from the bounds work. The decompression side already took size_t so a >2 GiB capacity could not wrap through int, but compression still took int for both the source length and the destination capacity, and the artifact export cast a size_t database size down to reach it. A database past 2 GiB would have handed the encoder a negative length. Both lengths and the bound helper are size_t now, and the function returns int64_t like its decompressing counterpart. The discovery walk now prunes the cache directory by absolute path. A custom CBM_CACHE_DIR may sit inside a repository — tests do it routinely — and walking into it pulls every other project's graph database into this project's file list. This is the narrow form of a concern that was briefly implemented as refusing any root that contained the cache; refusing a whole root was too blunt, and not walking the cache is what the concern actually asks for. The test fails without the prune: a .go file planted under the cache is otherwise discovered, and "cache" is not in the built-in skip list, so the assertion is not vacuous. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The grant store was readable but nothing could write to it, so only CBM_ALLOWED_ROOT could declare a root. `allow-root <path>` records one, `--list` shows what is recorded, and `--approve-sensitive` is required for a home or credential directory. Roots that are refused outright — volume and drive roots, paths too broad to index as a unit — cannot be granted at all. Enrollment is a command a person types and deliberately nothing else. That is the property the whole store exists for: neither an indexed repository nor a tool caller can widen its own boundary. A confirmation delivered through the MCP surface would be answered by the same agent that may have been influenced, so it would not be a human decision. The path is canonicalized before recording, because the policy is defined over resolved paths and a grant stored as a symlink would not match the resolved path the indexer later presents. Classified stateless in the daemon bootstrap: it writes one line of user-level config and reads nothing from a project, so enrolling a root must not depend on daemon state. Without that classification the argument fell through to MCP server mode and exited silently on EOF. Verified end to end: /etc refused as too broad, $HOME refused pending the flag, a project directory recorded, a granted root indexes, and a non-granted root is refused naming the exact command that would allow it. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…roval A repository may ship .cbmpathwhitelist listing outside roots it would like indexed alongside it. The file REQUESTS; it never GRANTS. Both attackers in this project's threat model can write a file inside a repository — a malicious indexed repo, and an agent with write access to the project — so a repo-local file cannot be authoritative without handing them the boundary. Approval is recorded user-level and keyed to a SHA-256 of the manifest bytes, so editing the file lapses approval and it must be granted again. Without the hash a repo approved once could widen itself forever on a later pull. This is direnv's model, and it is the only shape that keeps a checked-in file's ergonomics without making the file a permission slip. The hash covers the raw bytes rather than the parsed entries: a reordering or comment change is still a change the approver has not seen. `allow-root --approve-manifest <project>` is the human action that grants. Approval refuses outright if any requested entry would not stand on its own as an indexing root, so approving cannot become a route around the breadth policy, and entries are re-classified at use time as well so a stored approval cannot outrank a credential list that has grown since. Entries containing control characters are rejected — the same shape as the newline splitting that let a crafted indexed path inject an extra entry into the scoped file list. Deliberately NOT wired into the root boundary. A project's manifest authorizes outside roots FOR THAT PROJECT, so the question is "may project P pull in tree T", not "may T be indexed standalone", and the boundary check only ever sees one path with no project context to ask that with. A first draft did wire it there by passing the candidate as its own project root, which read a manifest that by definition was not the one requesting it and so authorized nothing — caught by exercising it end to end. The consuming half belongs where project context exists, in discovery walking a project's approved extra roots; cbm_workspace_manifest_allows is the query it will use. Five tests, including the one the design rests on: approval lapses when the manifest content changes. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Three language fixtures assign cbm_project_name_from_path into lp->project each time they open an indexed store. Teardown frees the last one, so a fixture that indexes more than once dropped every earlier heap name — 68 allocations across the run, which the macOS leak lane reports at process exit. Pre-existing rather than introduced here: none of these files or fqn.c are in this branch's diff, and the leak lane passed on the previous PR. Adding a suite shifted what the leak job sees, which is how it surfaced. Fixing it here rather than recording it, since a gate that only stays green by composition accident is not a gate. Freeing before reassigning also makes the ownership obvious at the point it matters, instead of relying on the reader noticing the teardown three functions away. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-ups to #1464.
allow-rootThe grant store landed readable but unwritable, so only
CBM_ALLOWED_ROOTcoulddeclare a root.
allow-root <path>records one,--listshows what is recorded,and
--approve-sensitiveis required for a home or credential directory. Rootsrefused outright — volume and drive roots, paths too broad to index as a unit —
cannot be granted at all.
Enrollment is a command a person types and deliberately nothing else, which is
the property the store exists for: neither an indexed repository nor a tool
caller can widen its own boundary. A confirmation returned through the MCP
surface would be answered by the same agent that may have been influenced, so it
would not be a human decision.
Paths are canonicalized before recording, since the policy is defined over
resolved paths and a grant stored as a symlink would not match what the indexer
later presents. Classified stateless in the daemon bootstrap — it writes one line
of user-level config and reads nothing from a project, so enrolling a root must
not depend on daemon state.
Compression sizes
Decompression already took
size_tso a >2 GiB capacity could not wrap throughint, but compression still tookintfor both source length and destinationcapacity, and the artifact export cast a
size_tdatabase size down to reach it.Both are
size_tnow and the function returnsint64_t, matching itsdecompressing counterpart.
Cache pruning in discovery
A custom
CBM_CACHE_DIRmay sit inside a repository — tests do it routinely — andwalking into it pulls every other project's graph database into this project's
file list. The walk now prunes it by absolute path. This is the narrow form of a
concern briefly implemented as refusing any root that contained the cache;
refusing a whole root was too blunt, and not walking the cache is what the
concern actually asks for. The test fails without the prune, and
cacheis notin the built-in skip list, so it is not vacuous.