fix(agent): fail closed on invalid AGENT frontmatter

This commit is contained in:
afjcjsbx
2026-03-29 23:41:32 +02:00
parent 847218ef29
commit 409251e69d
3 changed files with 68 additions and 6 deletions
+16 -6
View File
@@ -45,6 +45,7 @@ type AgentPromptDefinition struct {
Body string `json:"body"`
RawFrontmatter string `json:"raw_frontmatter,omitempty"`
Frontmatter AgentFrontmatter `json:"frontmatter"`
FrontmatterErr string `json:"frontmatter_error,omitempty"`
}
// SoulDefinition represents the resolved SOUL.md file linked to the agent.
@@ -146,19 +147,21 @@ func loadUserDefinition(workspace string) *UserDefinition {
func parseAgentPromptDefinition(path, content string) AgentPromptDefinition {
frontmatter, body := splitAgentFrontmatter(content)
parsedFrontmatter, err := parseAgentFrontmatter(path, frontmatter)
return AgentPromptDefinition{
Path: path,
Raw: content,
Body: body,
RawFrontmatter: frontmatter,
Frontmatter: parseAgentFrontmatter(path, frontmatter),
Frontmatter: parsedFrontmatter,
FrontmatterErr: errorString(err),
}
}
func parseAgentFrontmatter(path, frontmatter string) AgentFrontmatter {
func parseAgentFrontmatter(path, frontmatter string) (AgentFrontmatter, error) {
frontmatter = strings.TrimSpace(frontmatter)
if frontmatter == "" {
return AgentFrontmatter{}
return AgentFrontmatter{}, nil
}
rawFields := make(map[string]any)
@@ -167,7 +170,7 @@ func parseAgentFrontmatter(path, frontmatter string) AgentFrontmatter {
"path": path,
"error": err.Error(),
})
return AgentFrontmatter{}
return AgentFrontmatter{}, err
}
var typed struct {
@@ -184,7 +187,7 @@ func parseAgentFrontmatter(path, frontmatter string) AgentFrontmatter {
"path": path,
"error": err.Error(),
})
return AgentFrontmatter{}
return AgentFrontmatter{}, err
}
return AgentFrontmatter{
@@ -196,7 +199,7 @@ func parseAgentFrontmatter(path, frontmatter string) AgentFrontmatter {
Skills: append([]string(nil), typed.Skills...),
MCPServers: append([]string(nil), typed.MCPServers...),
Fields: rawFields,
}
}, nil
}
func splitAgentFrontmatter(content string) (frontmatter, body string) {
@@ -253,3 +256,10 @@ func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func errorString(err error) string {
if err == nil {
return ""
}
return err.Error()
}
+36
View File
@@ -330,3 +330,39 @@ Use frontmatter identity.
t.Fatal("expected slack MCP server to be blocked by frontmatter allowlist")
}
}
func TestNewAgentInstance_InvalidFrontmatterFailsClosedForToolsAndMCPServers(t *testing.T) {
workspace := setupWorkspace(t, map[string]string{
"AGENT.md": `---
tools: [read_file
mcpServers: [github]
---
# Agent
`,
})
defer cleanupWorkspace(t, workspace)
cfg := &config.Config{
Agents: config.AgentsConfig{
Defaults: config.AgentDefaults{
Workspace: workspace,
ModelName: "default-model",
},
},
Tools: config.ToolsConfig{
ReadFile: config.ReadFileToolConfig{Enabled: true},
},
}
agent := NewAgentInstance(&config.AgentConfig{
ID: "research",
Workspace: workspace,
}, &cfg.Agents.Defaults, cfg, &mockProvider{})
if _, ok := agent.Tools.Get("read_file"); ok {
t.Fatal("expected malformed frontmatter to fail closed and block read_file")
}
if agent.AllowsMCPServer("github") {
t.Fatal("expected malformed frontmatter to fail closed for MCP servers")
}
}
+16
View File
@@ -6,6 +6,9 @@ import (
)
func resolveAgentToolAllowlist(definition AgentContextDefinition) []string {
if frontmatterParseFailed(definition) {
return []string{}
}
if definition.Agent == nil || definition.Agent.Frontmatter.Tools == nil {
return nil
}
@@ -28,6 +31,9 @@ func resolveAgentToolAllowlist(definition AgentContextDefinition) []string {
}
func resolveAgentMCPServerAllowlist(definition AgentContextDefinition) map[string]struct{} {
if frontmatterParseFailed(definition) {
return map[string]struct{}{}
}
if definition.Agent == nil || definition.Agent.Frontmatter.MCPServers == nil {
return nil
}
@@ -43,3 +49,13 @@ func resolveAgentMCPServerAllowlist(definition AgentContextDefinition) map[strin
return allowlist
}
func frontmatterParseFailed(definition AgentContextDefinition) bool {
if definition.Agent == nil {
return false
}
if strings.TrimSpace(definition.Agent.RawFrontmatter) == "" {
return false
}
return strings.TrimSpace(definition.Agent.FrontmatterErr) != ""
}