Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 49 additions & 15 deletions pkg/parser/frontmatter_content.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@ type FrontmatterResult struct {
// ExtractFrontmatterFromContent parses YAML frontmatter from markdown content string
func ExtractFrontmatterFromContent(content string) (*FrontmatterResult, error) {
log.Printf("Extracting frontmatter from content: size=%d bytes", len(content))
lines := strings.Split(content, "\n")
// Fast-path: inspect only the first line to determine whether frontmatter exists.
firstNewline := strings.IndexByte(content, '\n')
firstLine := content
if firstNewline >= 0 {
firstLine = content[:firstNewline]
}

// Check if file starts with frontmatter delimiter
if len(lines) == 0 || strings.TrimSpace(lines[0]) != "---" {
// Check if file starts with frontmatter delimiter.
if strings.TrimSpace(firstLine) != "---" {
log.Print("No frontmatter delimiter found, returning content as markdown")
// No frontmatter, return entire content as markdown
return &FrontmatterResult{
Expand All @@ -38,22 +43,52 @@ func ExtractFrontmatterFromContent(content string) (*FrontmatterResult, error) {
}, nil
}

// Find end of frontmatter
endIndex := -1
for i := 1; i < len(lines); i++ {
if strings.TrimSpace(lines[i]) == "---" {
endIndex = i
// Find end of frontmatter by scanning line-by-line without splitting the entire document.
searchStart := len(content)
if firstNewline >= 0 {
searchStart = firstNewline + 1
}
frontmatterEndStart := -1
markdownStart := len(content)
for cursor := searchStart; cursor <= len(content); {
lineStart := cursor
lineEnd := len(content)
nextCursor := len(content) + 1

if cursor < len(content) {
if relNewline := strings.IndexByte(content[cursor:], '\n'); relNewline >= 0 {
lineEnd = cursor + relNewline
nextCursor = lineEnd + 1
}
}

if strings.TrimSpace(content[lineStart:lineEnd]) == "---" {
frontmatterEndStart = lineStart
markdownStart = nextCursor
break
}

if nextCursor > len(content) {
break
}
cursor = nextCursor
}

if endIndex == -1 {
if frontmatterEndStart == -1 {
return nil, errors.New("frontmatter not properly closed")
}

// Extract frontmatter YAML
frontmatterLines := lines[1:endIndex]
frontmatterYAML := strings.Join(frontmatterLines, "\n")
frontmatterYAML := content[searchStart:frontmatterEndStart]
frontmatterLines := []string{}
if frontmatterYAML != "" {
frontmatterLines = strings.Split(frontmatterYAML, "\n")
// Preserve previous behavior from lines[1:endIndex]: a trailing newline before
// the closing delimiter does not create an additional empty frontmatter line.
if strings.HasSuffix(frontmatterYAML, "\n") {
frontmatterLines = frontmatterLines[:len(frontmatterLines)-1]
}
}

// Sanitize no-break whitespace characters (U+00A0) which break the YAML parser
frontmatterYAML = strings.ReplaceAll(frontmatterYAML, "\u00A0", " ")
Expand All @@ -73,11 +108,10 @@ func ExtractFrontmatterFromContent(content string) (*FrontmatterResult, error) {
}

// Extract markdown content (everything after the closing ---)
var markdownLines []string
if endIndex+1 < len(lines) {
markdownLines = lines[endIndex+1:]
markdown := ""
if markdownStart <= len(content) {
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

markdownStart <= len(content) will slice content[len(content):] when the closing --- is the last line (or the file ends immediately after it). In Go, even an empty substring can keep the entire original content backing array alive, so this can retain large file contents unnecessarily (previous strings.Join(nil, "\n") returned a true empty string). Consider changing the condition to markdownStart < len(content) (leave markdown as "" when empty) and/or ensuring the returned Markdown doesn’t retain the full input when there’s no body content.

Suggested change
if markdownStart <= len(content) {
if markdownStart < len(content) {

Copilot uses AI. Check for mistakes.
markdown = content[markdownStart:]
}
markdown := strings.Join(markdownLines, "\n")

log.Printf("Successfully extracted frontmatter: fields=%d, markdown_size=%d bytes", len(frontmatter), len(markdown))
return &FrontmatterResult{
Expand Down
14 changes: 12 additions & 2 deletions pkg/workflow/compiler_orchestrator_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ func (c *Compiler) setupEngineAndImports(result *parser.FrontmatterResult, clean
if idx := strings.Index(importFilePath, "#"); idx >= 0 {
importFilePath = importFilePath[:idx]
}
// Only scan markdown files — .yml imports are YAML config, not markdown content
if !strings.HasSuffix(importFilePath, ".md") {
// Only scan non-builtin markdown imports.
// Builtin imports are trusted project assets and are validated in-source.
if !shouldScanImportedMarkdown(importFilePath) {
continue
}
// Resolve the import path to a full filesystem path
Expand Down Expand Up @@ -358,6 +359,15 @@ func (c *Compiler) setupEngineAndImports(result *parser.FrontmatterResult, clean
}, nil
}

// shouldScanImportedMarkdown reports whether an import path should be processed by
// markdown security scanning.
func shouldScanImportedMarkdown(importFilePath string) bool {
if !strings.HasSuffix(importFilePath, ".md") {
return false
}
return !strings.HasPrefix(importFilePath, parser.BuiltinPathPrefix)
}

// isStringFormEngine reports whether the "engine" field in the given frontmatter is a
// plain string (e.g. "engine: copilot"), as opposed to an object with an "id" or
// "runtime" sub-key.
Expand Down
31 changes: 31 additions & 0 deletions pkg/workflow/compiler_orchestrator_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,37 @@ strict: false
}
}

func TestShouldScanImportedMarkdown(t *testing.T) {
tests := []struct {
name string
importFilePath string
want bool
}{
{
name: "scans regular markdown imports",
importFilePath: "shared/workflow.md",
want: true,
},
{
name: "skips builtin markdown imports",
importFilePath: parser.BuiltinPathPrefix + "engines/claude.md",
want: false,
},
{
name: "skips non-markdown imports",
importFilePath: "shared/workflow.lock.yml",
want: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := shouldScanImportedMarkdown(tt.importFilePath)
assert.Equal(t, tt.want, got, "shouldScanImportedMarkdown(%q)", tt.importFilePath)
})
}
}

// TestSetupEngineAndImports_NetworkMerging tests network permissions merging from imports
func TestSetupEngineAndImports_NetworkMerging(t *testing.T) {
tmpDir := testutil.TempDir(t, "engine-network")
Expand Down
Loading