pinner 0.0.10

Secure CI/CD workflows by pinning mutable tags to immutable SHA-1 hashes. A high-performance Rust CLI that preserves YAML formatting and comments. Supports GitHub, GitLab, Bitbucket, Forgejo, and Docker image pinning.
Documentation
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Configuration & Options - Pinner</title>
    <link rel="icon" type="image/svg+xml" href="favicon.svg">
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        body {
            background-color: #0d1117;
            color: #c9d1d9;
        }
        .hero-gradient {
            background: radial-gradient(circle at 50% 50%, rgba(56, 189, 248, 0.05) 0%, rgba(13, 17, 23, 0) 50%);
        }
        .card {
            background-color: #161b22;
            border: 1px solid #30363d;
        }
        .terminal {
            background-color: #010409;
            border: 1px solid #30363d;
        }
        pre {
            background-color: #010409 !important;
            border: 1px solid #30363d;
            border-radius: 0.5rem;
            padding: 1rem;
            overflow-x: auto;
        }
        @media (min-width: 768px) {
            pre {
                white-space: pre-wrap !important;
                word-wrap: break-word;
            }
        }
        code {
            color: #38bdf8;
        }
    </style>
</head>
<body class="font-sans antialiased overflow-x-hidden">
    <!-- Navbar -->
    <nav class="flex items-center justify-between px-4 md:px-8 py-6 max-w-7xl mx-auto">
        <div class="flex items-center space-x-2">
            <a href="index.html" class="flex items-center space-x-2 group">
                <i class="fas fa-thumbtack text-sky-400 text-2xl group-hover:rotate-45 transition-transform"></i>
                <span class="text-2xl font-bold tracking-tight text-white">Pinner</span>
            </a>
        </div>
        <div class="flex items-center space-x-4 md:space-x-6 text-sm font-medium">
            <a href="getting-started.html" class="hover:text-sky-400 transition-colors" title="Getting Started"><i class="fas fa-rocket md:mr-2"></i><span class="hidden md:inline">Getting Started</span></a>
            <a href="configuration.html" class="text-sky-400 transition-colors font-bold" title="Configuration"><i class="fas fa-cog md:mr-2"></i><span class="hidden md:inline">Configuration</span></a>
            <a href="https://github.com/ffalcinelli/pinner" class="hover:text-sky-400 transition-colors" title="GitHub"><i class="fab fa-github md:mr-2"></i><span class="hidden md:inline">GitHub</span></a>
            <a href="https://docs.rs/pinner" class="hover:text-sky-400 transition-colors" title="API Docs"><i class="fas fa-book md:mr-2"></i><span class="hidden md:inline">API Docs</span></a>
        </div>
    </nav>

    <!-- Header Section -->
    <section class="relative pt-16 pb-12 hero-gradient border-b border-gray-800/50">
        <div class="max-w-4xl mx-auto px-4">
            <h1 class="text-4xl md:text-5xl font-extrabold text-white mb-6">
                Configuration & <span class="text-sky-400">Options</span>
            </h1>
            <p class="text-xl text-gray-400">
                A comprehensive guide to CLI flags, environment variables, and the <code>.pinner.toml</code> configuration file.
            </p>
        </div>
    </section>

    <!-- Content -->
    <main class="max-w-4xl mx-auto px-4 py-16 relative">
        
        <!-- Global Options -->
        <div class="mb-20">
            <h2 class="text-2xl font-bold text-white mb-8 flex items-center">
                <i class="fas fa-globe text-sky-400 mr-4"></i> Global Options
            </h2>
            <p class="text-gray-400 mb-8">These options can be used with any subcommand to modify Pinner's behavior.</p>
            
            <div class="grid grid-cols-1 gap-6">
                <div class="card p-6 rounded-xl">
                    <div class="flex flex-col md:flex-row md:items-center justify-between mb-4">
                        <code class="text-lg font-bold text-white">-w, --workflows &lt;PATH&gt;</code>
                        <span class="text-xs text-gray-500 uppercase tracking-widest mt-2 md:mt-0">Default: GitHub/GitLab/etc paths</span>
                    </div>
                    <p class="text-gray-400">Specify workflow files or directories to process. Can be used multiple times.</p>
                </div>

                <div class="card p-6 rounded-xl">
                    <div class="flex flex-col md:flex-row md:items-center justify-between mb-4">
                        <code class="text-lg font-bold text-white">-y, --yes</code>
                        <span class="text-xs text-gray-500 uppercase tracking-widest mt-2 md:mt-0">Flag</span>
                    </div>
                    <p class="text-gray-400">Automatically confirm all replacements without prompting. Ideal for non-interactive environments.</p>
                </div>

                <div class="card p-6 rounded-xl">
                    <div class="flex flex-col md:flex-row md:items-center justify-between mb-4">
                        <code class="text-lg font-bold text-white">-d, --dry-run</code>
                        <span class="text-xs text-gray-500 uppercase tracking-widest mt-2 md:mt-0">Flag</span>
                    </div>
                    <p class="text-gray-400">Print what would be changed without actually modifying any files.</p>
                </div>

                <div class="card p-6 rounded-xl">
                    <div class="flex flex-col md:flex-row md:items-center justify-between mb-4">
                        <code class="text-lg font-bold text-white">--format &lt;TYPE&gt;</code>
                        <span class="text-xs text-gray-500 uppercase tracking-widest mt-2 md:mt-0">Values: text, json, markdown</span>
                    </div>
                    <p class="text-gray-400">Output results in the specified format. <code>--json</code> is a deprecated alias for <code>--format json</code>.</p>
                </div>

                <div class="card p-6 rounded-xl">
                    <div class="flex flex-col md:flex-row md:items-center justify-between mb-4">
                        <code class="text-lg font-bold text-white">--concurrency &lt;NUM&gt;</code>
                        <span class="text-xs text-gray-500 uppercase tracking-widest mt-2 md:mt-0">Default: 10</span>
                    </div>
                    <p class="text-gray-400">Number of concurrent API requests to make. Increase for large projects, decrease to avoid rate limits.</p>
                </div>

                <div class="card p-6 rounded-xl">
                    <div class="flex flex-col md:flex-row md:items-center justify-between mb-4">
                        <code class="text-lg font-bold text-white">--ignore &lt;PATTERN&gt;</code>
                        <span class="text-xs text-gray-500 uppercase tracking-widest mt-2 md:mt-0">Multiple</span>
                    </div>
                    <p class="text-gray-400">Actions or images to ignore (e.g., "actions/checkout"). Can be used multiple times.</p>
                </div>

            </div>
        </div>

        <!-- Subcommands -->
        <div class="mb-20">
            <h2 class="text-2xl font-bold text-white mb-8 flex items-center">
                <i class="fas fa-terminal text-sky-400 mr-4"></i> Subcommands
            </h2>
            
            <div class="space-y-8">
                <div class="border-l-2 border-sky-500/30 pl-6">
                    <h3 class="text-xl font-bold text-white mb-2">pin</h3>
                    <p class="text-gray-400">Scans your workflows and replaces all mutable tags with immutable commit SHAs. This is the primary command for securing your supply chain.</p>
                </div>

                <div class="border-l-2 border-emerald-500/30 pl-6">
                    <h3 class="text-xl font-bold text-white mb-2">upgrade</h3>
                    <p class="text-gray-400 mb-4">Updates pinned actions to their latest versions based on the selected <code>--upgrade-strategy</code>.</p>
                    <div class="bg-amber-400/5 p-4 rounded-lg border border-amber-400/10 text-xs text-amber-400/80">
                        <i class="fas fa-exclamation-triangle mr-2"></i> Always review changes after an upgrade to maintain security.
                    </div>
                </div>

                <div class="border-l-2 border-purple-500/30 pl-6">
                    <h3 class="text-xl font-bold text-white mb-2">verify</h3>
                    <p class="text-gray-400">Checks if all actions in your workflows are pinned. Returns a non-zero exit code if unpinned actions are found. Perfect for CI/CD.</p>
                </div>

                <div class="border-l-2 border-blue-500/30 pl-6">
                    <h3 class="text-xl font-bold text-white mb-2">set &lt;ACTION&gt; &lt;HASH&gt;</h3>
                    <p class="text-gray-400">Forcibly sets a specific action to a provided hash across all matching occurrences in your workflows.</p>
                </div>

                <div class="border-l-2 border-orange-500/30 pl-6">
                    <h3 class="text-xl font-bold text-white mb-2">install-hook</h3>
                    <p class="text-gray-400">Installs a git pre-commit hook in your local repository that automatically runs <code>pinner verify</code> before every commit.</p>
                </div>

                <div class="border-l-2 border-teal-500/30 pl-6">
                    <h3 class="text-xl font-bold text-white mb-2">init</h3>
                    <p class="text-gray-400">Automatically initializes a <code>.pinner.toml</code> configuration file for your repository with sensible defaults.</p>
                </div>

                <div class="border-l-2 border-pink-500/30 pl-6">
                    <h3 class="text-xl font-bold text-white mb-2">export-sbom</h3>
                    <p class="text-gray-400">Generates and exports a Software Bill of Materials (SBOM) for all actions and images used in your workflows.</p>
                </div>

                <div class="border-l-2 border-red-500/30 pl-6">
                    <h3 class="text-xl font-bold text-white mb-2">scan</h3>
                    <p class="text-gray-400">Queries the OpenSSF OSV database for both current dependency commit hashes and upgrade candidates, performs Cosign/Sigstore signature verification for OCI images, and prompts to save vetted whitelists and compromised blacklists.</p>
                </div>

                <div class="border-l-2 border-gray-500/30 pl-6">
                    <h3 class="text-xl font-bold text-white mb-2">generate-completion [SHELL]</h3>
                    <p class="text-gray-400">Generates shell completion scripts for Bash, Zsh, Fish, Powershell, or Elvish. If the shell is omitted, Pinner will attempt to detect your current shell automatically.</p>
                </div>
            </div>
        </div>

        <!-- Environment Variables -->
        <div class="mb-20">
            <h2 class="text-2xl font-bold text-white mb-8 flex items-center">
                <i class="fas fa-key text-sky-400 mr-4"></i> Environment Variables
            </h2>
            
            <p class="text-gray-400 mb-8">Pinner automatically reads these variables for authentication and configuration.</p>

            <h3 class="text-lg font-bold text-white mb-4">Authentication & APIs</h3>
            <div class="overflow-x-auto rounded-xl border border-gray-800 card mb-12">
                <table class="min-w-full text-left text-sm">
                    <thead class="bg-gray-800/50 text-white border-b border-gray-800">
                        <tr>
                            <th class="px-6 py-4 font-bold">Variable</th>
                            <th class="px-6 py-4 font-bold">Description</th>
                            <th class="px-6 py-4 font-bold">Default</th>
                        </tr>
                    </thead>
                    <tbody class="divide-y divide-gray-800 text-gray-400">
                        <tr>
                            <td class="px-6 py-4 text-white font-medium"><code>GITHUB_TOKEN</code></td>
                            <td class="px-6 py-4">Auth token for GitHub API</td>
                            <td class="px-6 py-4">-</td>
                        </tr>
                        <tr>
                            <td class="px-6 py-4 text-white font-medium"><code>GITLAB_TOKEN</code></td>
                            <td class="px-6 py-4">Auth token for GitLab API</td>
                            <td class="px-6 py-4">-</td>
                        </tr>
                        <tr>
                            <td class="px-6 py-4 text-white font-medium"><code>BITBUCKET_TOKEN</code></td>
                            <td class="px-6 py-4">Auth token for Bitbucket API</td>
                            <td class="px-6 py-4">-</td>
                        </tr>
                        <tr>
                            <td class="px-6 py-4 text-white font-medium"><code>FORGEJO_TOKEN</code></td>
                            <td class="px-6 py-4">Auth token for Forgejo/Gitea API</td>
                            <td class="px-6 py-4">-</td>
                        </tr>
                        <tr>
                            <td class="px-6 py-4 text-white font-medium"><code>OCI_USERNAME</code></td>
                            <td class="px-6 py-4">Username for OCI Registries (Use <code>AWS</code> for ECR)</td>
                            <td class="px-6 py-4">-</td>
                        </tr>
                        <tr>
                            <td class="px-6 py-4 text-white font-medium"><code>OCI_PASSWORD</code></td>
                            <td class="px-6 py-4">Password for OCI Registries (ECR login token)</td>
                            <td class="px-6 py-4">-</td>
                        </tr>
                        </tbody>
                        </table>
                        </div>

                        <h3 class="text-lg font-bold text-white mb-4">Cloud Provider Notes</h3>
                        <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12">
                        <div class="card p-6 rounded-xl">
                        <h4 class="text-white font-bold mb-2">AWS ECR</h4>
                        <p class="text-sm text-gray-400">To pin private ECR images, pass the token from the AWS CLI:</p>
                        <pre class="mt-4 text-xs"><code>PINNER_OCI_USERNAME=AWS \
                        PINNER_OCI_PASSWORD=$(aws ecr get-login-password) \
                        pinner pin</code></pre>
                        </div>
                        <div class="card p-6 rounded-xl">
                        <h4 class="text-white font-bold mb-2">Azure Marketplace</h4>
                        <p class="text-sm text-gray-400">Azure DevOps tasks are mapped to the <code>microsoft/azure-pipelines-tasks</code> monorepo to resolve SHAs from the latest releases.</p>
                        </div>
                        <div class="card p-6 rounded-xl">
                        <h4 class="text-white font-bold mb-2">CircleCI</h4>
                        <p class="text-sm text-gray-400">Pinner supports pinning Docker images (<code>cimg/*</code>) used in CircleCI workflows. CircleCI Orbs use semantic versions and are not hash-pinned.</p>
                        </div>
                        </div>

                        <h3 class="text-lg font-bold text-white mb-4">Configuration Overrides</h3>
            <p class="text-gray-400 mb-6">Any setting from <code>.pinner.toml</code> can be overridden using an environment variable with the <code>PINNER_</code> prefix.</p>
            <div class="overflow-x-auto rounded-xl border border-gray-800 card">
                <table class="min-w-full text-left text-sm">
                    <thead class="bg-gray-800/50 text-white border-b border-gray-800">
                        <tr>
                            <th class="px-6 py-4 font-bold">Variable</th>
                            <th class="px-6 py-4 font-bold">Description</th>
                        </tr>
                    </thead>
                    <tbody class="divide-y divide-gray-800 text-gray-400">
                        <tr>
                            <td class="px-6 py-4 text-white font-medium"><code>PINNER_CONCURRENCY</code></td>
                            <td class="px-6 py-4">Override the number of concurrent API requests.</td>
                        </tr>
                        <tr>
                            <td class="px-6 py-4 text-white font-medium"><code>PINNER_IGNORE</code></td>
                            <td class="px-6 py-4">Override the list of ignored actions (comma-separated).</td>
                        </tr>
                        <tr>
                            <td class="px-6 py-4 text-white font-medium"><code>PINNER_GITHUB_URL</code></td>
                            <td class="px-6 py-4">Override the GitHub API URL.</td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>

        <!-- Configuration File -->
        <div class="mb-20">
            <h2 class="text-2xl font-bold text-white mb-8 flex items-center">
                <i class="fas fa-file-code text-sky-400 mr-4"></i> Configuration File
            </h2>
            
            <p class="text-gray-400 mb-8">
                Create a <code>.pinner.toml</code> file in your repository root to persist settings and share them with your team.
            </p>

            <div class="card p-8 rounded-xl mb-12">
                <h3 class="text-lg font-bold text-white mb-4">Example .pinner.toml</h3>
                <pre class="text-sm"><code># Actions to exclude from processing
ignore = ["actions/checkout", "my-org/private-repo"]

# Execution settings
concurrency = 5

# API URL overrides (for Enterprise)
github_url = "https://github.mycompany.com/api/v3"
gitlab_url = "https://gitlab.mycompany.com/api/v4"

# Whitelist of vetted dependency hashes/references (supports plain strings or maps)
vetted = [
    # Plain string format (backwards compatible)
    "actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332",

    # Structured format with original tag and timestamp of insertion
    { ref = "actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10", tag = "v6.0.3", timestamp = "2026-06-18T15:28:25Z" }
]

# Blacklist of compromised dependency hashes/references
compromised = [
    "actions/checkout@badhash1234567890badhash1234567890bad",
    { ref = "actions/checkout@evilhash1234567890evilhash1234567890bad", tag = "v3.1.0", timestamp = "2026-06-18T15:28:25Z" }
]

# Disable visual security feedback (default: false)
no_security_feedback = false</code></pre>
            </div>

            <h2 class="text-2xl font-bold text-white mb-8 flex items-center">
                <i class="fas fa-globe text-sky-400 mr-4"></i> Global Configuration & Overrides
            </h2>
            
            <p class="text-gray-400 mb-6 leading-relaxed">
                Pinner automatically loads security configurations from global user locations, allowing you to share whitelists and blacklists across multiple repositories:
            </p>
            <ul class="list-disc pl-6 text-gray-400 mb-8 space-y-2">
                <li><code>~/.cache/pinner/config.toml</code> (Global cache)</li>
                <li><code>~/.config/pinner/config.toml</code> (Standard user configuration)</li>
                <li><code>~/.pinner.toml</code> (User's home directory configuration)</li>
            </ul>
            <p class="text-gray-400 mb-6 leading-relaxed">
                <strong>Precedence (Local Overrides)</strong>:<br>
                The local project-level <code>.pinner.toml</code> file acts as a strict override. If an entry is marked <code>vetted</code> locally, it will override any global <code>compromised</code> entry, and if marked <code>compromised</code> locally, it overrides any global <code>vetted</code> entry. Non-conflicting local and global entries are automatically combined.
            </p>
        </div>

    </main>

    <!-- Footer -->
    <footer class="py-16 text-center text-gray-500">
        <div class="flex items-center justify-center space-x-4 mb-6">
            <a href="https://github.com/ffalcinelli/pinner" class="hover:text-white transition-colors"><i class="fab fa-github text-xl"></i></a>
            <div class="h-4 w-px bg-gray-800"></div>
            <a href="https://docs.rs/pinner" class="hover:text-white transition-colors text-sm font-bold uppercase tracking-tighter">Docs.rs</a>
        </div>
        <p class="text-xs">&copy; 2026 Fabio Falcinelli. Released under the MIT License.</p>
    </footer>
</body>
</html>