GrooveSeek

Semantic search over a Markdown knowledge base, served over MCP.

View the Project on GitHub alphabet-h/grooveseek

16. Keep the plugin directory outside the knowledge base

Context and problem

ADR-0013 defines a grammar plugin as native code where loading it is already executing it, and draws the consequence that the directory plugins are read from is a privileged setting. The implementation honours that consequence — but only for the question of who may write the value. A config file found next to the knowledge base has its grammar_dir replaced with a safe default (R4), and v1.3.0 extended the same rule one step upstream to [parsers].enabled (R5).

What was never checked is what the value may point at. grammar_dir could name a directory inside the knowledge base, with no check and no warning.

Meanwhile the knowledge-base directory has been declared, consistently since ADR-0003, to be not a security boundary. The crate::links module documentation says it, and so does the README. It is a statement that whoever can write to the knowledge base decides what gets indexed, and that this is by design.

Where the two meet there was no check. With the plugin directory inside the knowledge base, anyone who can write to the knowledge base can run arbitrary code in the groove process by dropping a library there. It is the one place where “index this folder” turns into “run this code”.

Decision drivers

Options considered

  1. Warn and load anyway.
  2. Substitute the safe default and warn — the shape R4 uses for untrusted configs.
  3. Refuse and stop the run.

Decision

Take option 3. When the resolved grammar_dir is inside kb_path — including when it is kb_path itself — the run stops, whatever the trust of the config.

Option 1 is not taken because the warning would be a claim and nothing more. What is needed here is stopping before the load, not explaining after it. The problem ADR-0003 names — a guard claiming more than it does — reappears here with its direction reversed.

Option 2 is not taken because the justification for substituting does not extend to a trusted setting. R4 may discard an untrusted config’s value precisely because whoever wrote it holds no authority. Silently redirecting a value the operator wrote themselves leaves that operator with no way to learn why the plugin they installed is never read.

The judgement is made after grammar_dir is resolved. Environment variable, config file and OS default all carry the same danger, so the inputs are not judged separately — separating them would give one physical situation several meanings.

The refusal names the input that has to change. Telling someone who set the environment variable to edit the config file is advice that does not work when followed, since the variable wins either way.

Consequences

References