claude-code: read front matter as YAML and refuse a file reference outside the item
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

A byte-order mark, a flow mapping or a merged key hid a key the line scan
looked for, and @path read any file into every session the item reached
(hq issue 340 review).
This commit is contained in:
jochen
2026-10-09 02:52:57 +02:00
parent 736743fedc
commit c613efe189
5 changed files with 126 additions and 20 deletions
+66 -19
View File
@@ -41,6 +41,8 @@ import (
"strings"
"sync"
"time"
"gopkg.in/yaml.v3"
)
// Plugin is the plugin's name, and its marketplace's: what its items are called in a session, `nox-mesh:<name>`.
@@ -112,8 +114,14 @@ var runsFrontMatter = map[string]string{
// !`command` inline, or a fenced block opened with ```!.
var shellLine = regexp.MustCompile("!`[^`\n]+`|(?m)^[ \t]*```!")
// fileRef is a file the agent may read into the prompt when it loads a skill or a command: `@` and a path, at the
// start of a line or after a space, a bracket or a quote. Any such word counts, a path without a dot or a slash
// (`@Makefile`) as much as one with: refused unless it is one of the item's own files, rather than guessed at.
var fileRef = regexp.MustCompile(`(?:^|[\s(\[{"'<])@([^\s` + "`" + `"')\]}>]+)`)
// runsProblem says why an item's text would run something in a session by itself, or "": the shell lines the
// agent runs when it loads a skill or a command, and the front matter keys that start, run or pre-approve.
// agent runs when it loads a skill or a command, the front matter keys that start, run or pre-approve, front
// matter the module cannot read for certain, and a file outside the item that the agent would read in.
func runsProblem(it Item) string {
if it.Kind != KindSkill && it.Kind != KindAgent && it.Kind != KindCommand {
return ""
@@ -127,13 +135,18 @@ func runsProblem(it Item) string {
if !strings.HasSuffix(rel, ".md") {
continue // a script beside a skill runs only when the agent runs it, through the session's own rules
}
content := it.Files[rel]
content := strings.TrimPrefix(it.Files[rel], "\ufeff")
if shellLine.MatchString(content) {
return fmt.Sprintf("%s holds a shell line (!`…` or ```!), which the agent runs when it loads the %s, "+
"before any permission is asked; nothing registered through this module runs by itself in a "+
"session (novox/hq issue 340)", rel, it.Kind)
}
for _, key := range frontMatterKeys(content) {
keys, problem := frontMatterKeys(content)
if problem != "" {
return fmt.Sprintf("%s: %s, so what it asks of a session cannot be judged; it is refused rather than "+
"guessed at (novox/hq issue 340)", rel, problem)
}
for _, key := range keys {
norm := strings.ToLower(strings.NewReplacer("-", "", "_", "").Replace(key))
if why, ok := runsFrontMatter[norm]; ok {
return fmt.Sprintf("%s says %q in its front matter: %s. Nothing registered through this module "+
@@ -141,30 +154,64 @@ func runsProblem(it Item) string {
"terminal (novox/hq issue 340)", rel, key, why)
}
}
if it.Kind == KindSkill || it.Kind == KindCommand {
for _, m := range fileRef.FindAllStringSubmatch(content, -1) {
ref := strings.TrimRight(m[1], ".,;:!?")
if ref == "" {
continue
}
if _, own := it.Files[ref]; !own || pathProblem(ref) != "" {
return fmt.Sprintf("%s refers to @%s, a file outside the %s that the agent reads into its "+
"prompt when it loads it; a registered item refers only to its own files (novox/hq issue 340)",
rel, ref, it.Kind)
}
}
}
}
return ""
}
// frontMatterKeys are the top-level keys of a markdown file's front matter: the lines between a first line
// `---` and the next, that start a key at the left margin.
func frontMatterKeys(content string) []string {
lines := strings.Split(strings.ReplaceAll(content, "\r\n", "\n"), "\n")
if len(lines) == 0 || strings.TrimSpace(lines[0]) != "---" {
return nil
// frontMatterKeys are the top-level keys of a markdown file's front matter, read as YAML: the block between a
// first line `---` (after a byte-order mark) and the next. A block that does not close, does not parse, or is not
// a mapping is a problem: the agent's own reader may take from it what this one cannot see.
func frontMatterKeys(content string) ([]string, string) {
content = strings.ReplaceAll(strings.TrimPrefix(content, "\ufeff"), "\r\n", "\n")
lines := strings.Split(content, "\n")
first := 0
for first < len(lines) && strings.TrimSpace(lines[first]) == "" {
first++
}
var keys []string
for _, line := range lines[1:] {
if strings.TrimSpace(line) == "---" {
if first == len(lines) || strings.TrimSpace(lines[first]) != "---" {
return nil, ""
}
end := -1
for i := first + 1; i < len(lines); i++ {
if t := strings.TrimSpace(lines[i]); t == "---" || t == "..." {
end = i
break
}
if line == "" || line[0] == ' ' || line[0] == '\t' || line[0] == '#' || line[0] == '-' {
continue
}
if i := strings.Index(line, ":"); i > 0 {
keys = append(keys, strings.Trim(strings.TrimSpace(line[:i]), `"'`))
}
}
return keys
if end < 0 {
return nil, "its front matter does not close"
}
block := strings.Join(lines[first+1:end], "\n")
if strings.TrimSpace(block) == "" {
return nil, ""
}
var node yaml.Node
if err := yaml.Unmarshal([]byte(block), &node); err != nil {
return nil, "its front matter is not YAML (" + err.Error() + ")"
}
var top map[string]any
if err := node.Decode(&top); err != nil {
return nil, "its front matter is not a mapping of keys (" + err.Error() + ")"
}
keys := make([]string, 0, len(top))
for k := range top {
keys = append(keys, k)
}
sort.Strings(keys)
return keys, ""
}
// Item is one registration, as the state keeps it.