runPoutineOnDirectory runs the poutine security scanner on a directory containing workflows
(workflowDir string, verbose bool, strict bool)
| 75 | |
| 76 | // runPoutineOnDirectory runs the poutine security scanner on a directory containing workflows |
| 77 | func runPoutineOnDirectory(workflowDir string, verbose bool, strict bool) error { |
| 78 | poutineLog.Printf("Running poutine security scanner on directory: %s", workflowDir) |
| 79 | |
| 80 | // Find git root to get the absolute path for Docker volume mount |
| 81 | gitRoot, err := gitutil.FindGitRoot() |
| 82 | if err != nil { |
| 83 | return fmt.Errorf("failed to find git root: %w", err) |
| 84 | } |
| 85 | |
| 86 | // Validate gitRoot is an absolute path (security: ensure trusted path from git) |
| 87 | if !filepath.IsAbs(gitRoot) { |
| 88 | return fmt.Errorf("git root is not an absolute path: %s", gitRoot) |
| 89 | } |
| 90 | |
| 91 | // Ensure poutine config exists with custom runner configuration |
| 92 | if err := ensurePoutineConfig(gitRoot); err != nil { |
| 93 | return fmt.Errorf("failed to ensure poutine config: %w", err) |
| 94 | } |
| 95 | |
| 96 | // Build the Docker command with JSON output for easier parsing |
| 97 | // docker run --rm -v "$(pwd)":/workdir -w /workdir ghcr.io/boostsecurityio/poutine:latest analyze_local . --format json |
| 98 | // #nosec G204 -- gitRoot comes from git rev-parse (trusted source) and is validated as absolute path |
| 99 | // exec.Command with separate args (not shell execution) prevents command injection |
| 100 | cmd := exec.Command( |
| 101 | "docker", |
| 102 | "run", |
| 103 | "--rm", |
| 104 | "-v", gitRoot+":/workdir", |
| 105 | "-w", "/workdir", |
| 106 | "ghcr.io/boostsecurityio/poutine:latest", |
| 107 | "analyze_local", |
| 108 | ".", |
| 109 | "--format", "json", |
| 110 | "--quiet", // Disable progress output |
| 111 | ) |
| 112 | |
| 113 | // Always show that poutine is running (regular verbosity) |
| 114 | fmt.Fprintf(os.Stderr, "%s\n", console.FormatInfoMessage("Running poutine security scanner")) |
| 115 | |
| 116 | // In verbose mode, also show the command that users can run directly |
| 117 | if verbose { |
| 118 | dockerCmd := fmt.Sprintf("docker run --rm -v \"%s:/workdir\" -w /workdir ghcr.io/boostsecurityio/poutine:latest analyze_local . --format json --quiet", |
| 119 | gitRoot) |
| 120 | fmt.Fprintf(os.Stderr, "%s\n", console.FormatInfoMessage("Run poutine directly: "+dockerCmd)) |
| 121 | } |
| 122 | |
| 123 | // Capture output |
| 124 | var stdout, stderr bytes.Buffer |
| 125 | cmd.Stdout = &stdout |
| 126 | cmd.Stderr = &stderr |
| 127 | |
| 128 | // Run the command |
| 129 | err = cmd.Run() |
| 130 | |
| 131 | // Parse and display output for all files (no filtering) |
| 132 | totalWarnings, parseErr := parseAndDisplayPoutineOutputForDirectory(stdout.String(), verbose, gitRoot) |
| 133 | if parseErr != nil { |
| 134 | poutineLog.Printf("Failed to parse poutine output: %v", parseErr) |
no test coverage detected