parseAndDisplayPoutineOutputForDirectory parses poutine JSON output and displays all findings Returns the total number of warnings found across all files
(stdout string, verbose bool, gitRoot string)
| 383 | // parseAndDisplayPoutineOutputForDirectory parses poutine JSON output and displays all findings |
| 384 | // Returns the total number of warnings found across all files |
| 385 | func parseAndDisplayPoutineOutputForDirectory(stdout string, verbose bool, gitRoot string) (int, error) { |
| 386 | // Parse JSON output from stdout |
| 387 | var output poutineOutput |
| 388 | if stdout == "" { |
| 389 | return 0, nil // No output means no findings |
| 390 | } |
| 391 | |
| 392 | trimmed := strings.TrimSpace(stdout) |
| 393 | if !strings.HasPrefix(trimmed, "{") { |
| 394 | // Non-JSON output, likely an error |
| 395 | if len(trimmed) > 0 { |
| 396 | return 0, fmt.Errorf("unexpected poutine output format: %s", trimmed) |
| 397 | } |
| 398 | return 0, nil |
| 399 | } |
| 400 | |
| 401 | if err := json.Unmarshal([]byte(stdout), &output); err != nil { |
| 402 | return 0, fmt.Errorf("failed to parse poutine JSON output: %w", err) |
| 403 | } |
| 404 | |
| 405 | // Display all findings (no filtering by file) |
| 406 | totalWarnings := len(output.Findings) |
| 407 | |
| 408 | // Skip if no warnings |
| 409 | if totalWarnings == 0 { |
| 410 | return 0, nil |
| 411 | } |
| 412 | |
| 413 | // Group findings by file for better readability |
| 414 | findingsByFile := make(map[string][]poutineFinding) |
| 415 | for _, finding := range output.Findings { |
| 416 | findingsByFile[finding.Meta.Path] = append(findingsByFile[finding.Meta.Path], finding) |
| 417 | } |
| 418 | |
| 419 | // Display findings for each file |
| 420 | for filePath, findings := range findingsByFile { |
| 421 | // Validate and sanitize file path to prevent path traversal |
| 422 | cleanPath := filepath.Clean(filePath) |
| 423 | |
| 424 | // Convert to absolute path if relative |
| 425 | absPath := cleanPath |
| 426 | if !filepath.IsAbs(cleanPath) { |
| 427 | absPath = filepath.Join(gitRoot, cleanPath) |
| 428 | } |
| 429 | |
| 430 | // Ensure the file is within gitRoot to prevent path traversal |
| 431 | absGitRoot, err := filepath.Abs(gitRoot) |
| 432 | if err != nil { |
| 433 | poutineLog.Printf("Failed to get absolute path for git root: %v", err) |
| 434 | continue |
| 435 | } |
| 436 | |
| 437 | absPath, err = filepath.Abs(absPath) |
| 438 | if err != nil { |
| 439 | poutineLog.Printf("Failed to get absolute path for %s: %v", filePath, err) |
| 440 | continue |
| 441 | } |
| 442 |
no test coverage detected