diff --git a/pkg/workflow/claude_tools.go b/pkg/workflow/claude_tools.go index b3768fe5d0a..46de4db36f6 100644 --- a/pkg/workflow/claude_tools.go +++ b/pkg/workflow/claude_tools.go @@ -389,9 +389,7 @@ func (e *ClaudeEngine) computeAllowedClaudeToolsString(tools map[string]any, saf // Sort the allowed tools alphabetically for consistent output sort.Strings(allowedTools) - if log.Enabled() { - claudeToolsLog.Printf("Generated allowed tools string with %d tools", len(allowedTools)) - } + claudeToolsLog.Printf("Generated allowed tools string with %d tools", len(allowedTools)) return strings.Join(allowedTools, ",") } diff --git a/pkg/workflow/compiler.go b/pkg/workflow/compiler.go index 060f2b92580..5eabf775b5a 100644 --- a/pkg/workflow/compiler.go +++ b/pkg/workflow/compiler.go @@ -517,9 +517,7 @@ func (c *Compiler) CompileWorkflowData(workflowData *WorkflowData, markdownPath // Track compilation time for performance monitoring startTime := time.Now() defer func() { - if log.Enabled() { - log.Printf("Compilation completed in %v", time.Since(startTime)) - } + log.Printf("Compilation completed in %v", time.Since(startTime)) }() // Reset the step order tracker for this compilation diff --git a/pkg/workflow/concurrency_validation.go b/pkg/workflow/concurrency_validation.go index 4fcf80b010d..5de2944581a 100644 --- a/pkg/workflow/concurrency_validation.go +++ b/pkg/workflow/concurrency_validation.go @@ -74,6 +74,7 @@ func validateConcurrencyGroupExpression(group string) error { // validateBalancedBraces checks that all ${{ }} braces are balanced and properly closed func validateBalancedBraces(group string) error { + concurrencyValidationLog.Print("Checking balanced braces in expression") openCount := 0 i := 0 positions := []int{} // Track positions of opening braces for error reporting @@ -111,6 +112,7 @@ func validateBalancedBraces(group string) error { if openCount > 0 { // Find the position of the first unclosed opening brace pos := positions[0] + concurrencyValidationLog.Printf("Found %d unclosed brace(s) starting at position %d", openCount, pos) return NewValidationError( "concurrency", "unclosed expression braces", @@ -119,6 +121,7 @@ func validateBalancedBraces(group string) error { ) } + concurrencyValidationLog.Print("Brace balance check passed") return nil } @@ -128,6 +131,8 @@ func validateExpressionSyntax(group string) error { expressionPattern := regexp.MustCompile(`\$\{\{([^}]*)\}\}`) matches := expressionPattern.FindAllStringSubmatch(group, -1) + concurrencyValidationLog.Printf("Found %d expression(s) to validate", len(matches)) + for _, match := range matches { if len(match) < 2 { continue @@ -189,7 +194,9 @@ func validateExpressionContent(expr string, fullGroup string) error { // Try to parse complex expressions with logical operators if containsLogicalOperators(expr) { + concurrencyValidationLog.Print("Expression contains logical operators, performing deep validation") if _, err := ParseExpression(expr); err != nil { + concurrencyValidationLog.Printf("Expression parsing failed: %v", err) return NewValidationError( "concurrency", "invalid expression syntax", diff --git a/pkg/workflow/expression_builder.go b/pkg/workflow/expression_builder.go index 6df005f3e7c..1780a9791f8 100644 --- a/pkg/workflow/expression_builder.go +++ b/pkg/workflow/expression_builder.go @@ -58,6 +58,7 @@ func BuildOr(left ConditionNode, right ConditionNode) ConditionNode { // BuildAnd creates an AND node combining two conditions func BuildAnd(left ConditionNode, right ConditionNode) ConditionNode { + expressionBuilderLog.Print("Building AND condition node") return &AndNode{Left: left, Right: right} } @@ -81,6 +82,8 @@ func BuildReactionCondition() ConditionNode { } terms = append(terms, pullRequestCondition) + expressionBuilderLog.Printf("Created disjunction with %d event type terms", len(terms)) + // Use DisjunctionNode to avoid deep nesting return &DisjunctionNode{Terms: terms} } @@ -169,6 +172,7 @@ func BuildNotFromFork() *ComparisonNode { } func BuildSafeOutputType(outputType string) ConditionNode { + expressionBuilderLog.Printf("Building safe-output condition for output type: %s", outputType) // Use !cancelled() && needs.agent.result != 'skipped' to properly handle workflow cancellation // !cancelled() allows jobs to run when dependencies fail (for error reporting) // needs.agent.result != 'skipped' prevents running when workflow is cancelled (dependencies get skipped) diff --git a/pkg/workflow/markdown_security_scanner.go b/pkg/workflow/markdown_security_scanner.go index f1d7a495e65..97b9d4b2907 100644 --- a/pkg/workflow/markdown_security_scanner.go +++ b/pkg/workflow/markdown_security_scanner.go @@ -68,6 +68,15 @@ func (f SecurityFinding) String() string { return fmt.Sprintf("[%s] %s", f.Category, f.Description) } +// countCategories counts unique security finding categories +func countCategories(findings []SecurityFinding) int { + categories := make(map[SecurityFindingCategory]bool) + for _, f := range findings { + categories[f.Category] = true + } + return len(categories) +} + // ScanMarkdownSecurity scans markdown content for dangerous or malicious patterns. // It automatically strips YAML frontmatter (delimited by ---) so that only the // markdown body is scanned. Line numbers in returned findings are adjusted to @@ -78,14 +87,21 @@ func ScanMarkdownSecurity(content string) []SecurityFinding { // Strip frontmatter and get the line offset for correct line number reporting markdownBody, lineOffset := stripFrontmatter(content) + markdownSecurityLog.Printf("Stripped frontmatter: %d line(s) removed, scanning %d bytes of markdown", lineOffset, len(markdownBody)) var findings []SecurityFinding + markdownSecurityLog.Print("Running unicode abuse detection") findings = append(findings, scanUnicodeAbuse(markdownBody)...) + markdownSecurityLog.Print("Running hidden content detection") findings = append(findings, scanHiddenContent(markdownBody)...) + markdownSecurityLog.Print("Running obfuscated links detection") findings = append(findings, scanObfuscatedLinks(markdownBody)...) + markdownSecurityLog.Print("Running HTML abuse detection") findings = append(findings, scanHTMLAbuse(markdownBody)...) + markdownSecurityLog.Print("Running embedded files detection") findings = append(findings, scanEmbeddedFiles(markdownBody)...) + markdownSecurityLog.Print("Running social engineering detection") findings = append(findings, scanSocialEngineering(markdownBody)...) // Adjust line numbers to account for stripped frontmatter @@ -98,7 +114,9 @@ func ScanMarkdownSecurity(content string) []SecurityFinding { } if len(findings) > 0 { - markdownSecurityLog.Printf("Found %d security issues in markdown content", len(findings)) + markdownSecurityLog.Printf("Security scan complete: found %d issue(s) across %d categor(ies)", len(findings), countCategories(findings)) + } else { + markdownSecurityLog.Print("Security scan complete: no issues found") } return findings @@ -177,6 +195,8 @@ func scanUnicodeAbuse(content string) []SecurityFinding { var findings []SecurityFinding lines := strings.Split(content, "\n") + markdownSecurityLog.Printf("Scanning %d line(s) for unicode abuse", len(lines)) + for lineNum, line := range lines { lineNo := lineNum + 1 diff --git a/pkg/workflow/redact_secrets.go b/pkg/workflow/redact_secrets.go index e98009735c1..0406ecf616d 100644 --- a/pkg/workflow/redact_secrets.go +++ b/pkg/workflow/redact_secrets.go @@ -22,6 +22,7 @@ func escapeSingleQuote(s string) string { // CollectSecretReferences extracts all secret references from the workflow YAML // This scans for patterns like ${{ secrets.SECRET_NAME }} or secrets.SECRET_NAME func CollectSecretReferences(yamlContent string) []string { + secretMaskingLog.Printf("Scanning workflow YAML (%d bytes) for secret references", len(yamlContent)) secretsMap := make(map[string]bool) // Pattern to match ${{ secrets.SECRET_NAME }} or secrets.SECRET_NAME @@ -44,6 +45,8 @@ func CollectSecretReferences(yamlContent string) []string { // Sort for consistent output SortStrings(secrets) + secretMaskingLog.Printf("Found %d unique secret reference(s) in workflow", len(secrets)) + return secrets } @@ -59,11 +62,13 @@ func (c *Compiler) generateSecretRedactionStep(yaml *strings.Builder, yamlConten // If no secrets found, we still generate the step but it will be a no-op at runtime // This ensures consistent step ordering and validation if len(secretReferences) == 0 { + secretMaskingLog.Print("No secrets found, generating no-op redaction step") // Generate a minimal no-op redaction step for validation purposes yaml.WriteString(" - name: Redact secrets in logs\n") yaml.WriteString(" if: always()\n") yaml.WriteString(" run: echo 'No secrets to redact'\n") } else { + secretMaskingLog.Printf("Generating redaction step for %d secret(s)", len(secretReferences)) yaml.WriteString(" - name: Redact secrets in logs\n") yaml.WriteString(" if: always()\n") fmt.Fprintf(yaml, " uses: %s\n", GetActionPin("actions/github-script")) diff --git a/pkg/workflow/safe_outputs_config.go b/pkg/workflow/safe_outputs_config.go index d6c4b05f4ea..a28950ad368 100644 --- a/pkg/workflow/safe_outputs_config.go +++ b/pkg/workflow/safe_outputs_config.go @@ -18,11 +18,13 @@ func (c *Compiler) extractSafeOutputsConfig(frontmatter map[string]any) *SafeOut if output, exists := frontmatter["safe-outputs"]; exists { if outputMap, ok := output.(map[string]any); ok { + safeOutputsConfigLog.Printf("Processing safe-outputs configuration with %d top-level keys", len(outputMap)) config = &SafeOutputsConfig{} // Handle create-issue issuesConfig := c.parseIssuesConfig(outputMap) if issuesConfig != nil { + safeOutputsConfigLog.Print("Configured create-issue output handler") config.CreateIssues = issuesConfig } @@ -89,6 +91,7 @@ func (c *Compiler) extractSafeOutputsConfig(frontmatter map[string]any) *SafeOut // Handle create-pull-request pullRequestsConfig := c.parsePullRequestsConfig(outputMap) if pullRequestsConfig != nil { + safeOutputsConfigLog.Print("Configured create-pull-request output handler") config.CreatePullRequests = pullRequestsConfig } @@ -126,6 +129,7 @@ func (c *Compiler) extractSafeOutputsConfig(frontmatter map[string]any) *SafeOut } } config.AllowedDomains = domainStrings + safeOutputsConfigLog.Printf("Configured allowed-domains with %d domain(s)", len(domainStrings)) } } @@ -399,11 +403,18 @@ func (c *Compiler) extractSafeOutputsConfig(frontmatter map[string]any) *SafeOut if outputMap, ok := output.(map[string]any); ok { if _, exists := outputMap["threat-detection"]; !exists { // Only apply default if threat-detection key doesn't exist + safeOutputsConfigLog.Print("Applying default threat-detection configuration") config.ThreatDetection = &ThreatDetectionConfig{} } } } } + if config != nil { + safeOutputsConfigLog.Print("Successfully extracted safe-outputs configuration") + } else { + safeOutputsConfigLog.Print("No safe-outputs configuration found in frontmatter") + } + return config }