---
import Base from '../layouts/Base.astro';
import SiteNav from '../components/ui/SiteNav.astro';
import AnnouncementBar from '../components/ui/AnnouncementBar.astro';
import Footer from '../components/sections/Footer.astro';
const APP_INSTALL_URL = 'https://github.com/apps/foxguard-app/installations/new';
const SELF_HOST_IMAGE = 'ghcr.io/0sec-labs/foxguard-github-app';
const APP_REGISTERED = true;
---
<Base
title="foxguard for GitHub: one-click PR scans"
description="Install foxguard on any repo. Every PR scanned for vulns, secrets, and quantum-weak crypto in seconds. Free for open source. Self-hostable."
>
<div class="sticky top-0 z-40 bg-noir-950 border-b border-noir-800">
{!APP_REGISTERED && (
<AnnouncementBar
href="https://github.com/0sec-labs/foxguard/issues/246"
label="GitHub App coming soon."
detail="The hosted app is in active development."
cta="Track #246 ->"
/>
)}
<div class="max-w-5xl mx-auto px-6">
<SiteNav />
</div>
</div>
<section class="pt-12 pb-24">
<div class="max-w-5xl mx-auto px-6">
<div class="max-w-3xl mx-auto text-center mb-16">
<h1 class="font-heading text-noir-50 text-3xl sm:text-5xl lg:text-6xl leading-[1.1] tracking-tight mb-6">
Install foxguard<br/>on any GitHub repo
</h1>
<p class="text-noir-400 text-sm sm:text-lg leading-relaxed mb-8 max-w-xl mx-auto">
foxguard scans every pull request in seconds and posts any vulnerabilities, secrets, or quantum-weak crypto as comments. Free for open source.
</p>
<div class="flex flex-col sm:flex-row items-center justify-center gap-3">
<a
href={APP_INSTALL_URL}
class={`flex items-center gap-2 rounded-lg ${APP_REGISTERED ? 'bg-fox hover:bg-fox-light' : 'bg-noir-800 cursor-not-allowed opacity-60'} px-6 py-3 text-sm font-medium transition-colors no-underline`}
style={APP_REGISTERED ? 'color:#000;' : 'color:#a8a29e;'}
aria-disabled={!APP_REGISTERED}
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/></svg>
{APP_REGISTERED ? 'Install on GitHub' : 'Install, coming soon'}
</a>
<a
href="#self-host"
class="text-sm text-noir-500 hover:text-noir-100 transition-colors no-underline"
>
Self-host instead →
</a>
</div>
</div>
<!-- What it does -->
<div class="grid md:grid-cols-3 gap-6 mb-24">
<div class="rounded-xl border border-noir-800 p-6">
<div class="text-fox-light text-2xl mb-3 font-mono">01</div>
<h3 class="font-heading text-noir-50 text-lg mb-2">Click install</h3>
<p class="text-noir-500 text-sm leading-relaxed">Pick your repos. There's no card and no signup. Private repos work too, since the App scans inside your tenancy.</p>
</div>
<div class="rounded-xl border border-noir-800 p-6">
<div class="text-fox-light text-2xl mb-3 font-mono">02</div>
<h3 class="font-heading text-noir-50 text-lg mb-2">Open a PR</h3>
<p class="text-noir-500 text-sm leading-relaxed">foxguard scans the head ref, most repos in under a second. Findings post as one comment, grouped by severity.</p>
</div>
<div class="rounded-xl border border-noir-800 p-6">
<div class="text-fox-light text-2xl mb-3 font-mono">03</div>
<h3 class="font-heading text-noir-50 text-lg mb-2">Fix and merge</h3>
<p class="text-noir-500 text-sm leading-relaxed">Each finding shows file, line, rule, and a fix. Push a change and the scan re-runs. A clean scan posts a clean comment.</p>
</div>
</div>
<!-- What it catches -->
<div class="mb-24">
<h2 class="font-heading text-noir-50 text-2xl sm:text-3xl mb-6 text-center">What it flags on PRs</h2>
<p class="text-noir-500 text-sm text-center mb-8 max-w-2xl mx-auto">It runs the same engine as the local <code class="text-fox-light bg-noir-900 px-1.5 py-0.5 rounded">foxguard</code> CLI, with 200+ rules, 12 source languages, and cross-file taint tracking.</p>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
{[
['Command injection', 'shelling out with concatenated user input'],
['SQL injection', 'unparameterized queries via f-strings or concat'],
['Path traversal', '../ payloads landing on filesystem reads'],
['SSRF', 'outbound fetches that hit private/loopback ranges'],
['Hardcoded secrets', 'API tokens, AWS keys, Stripe keys, private keys'],
['Weak crypto', 'MD5, SHA-1, DES, ECB, math/rand for tokens'],
['Deserialization', 'pickle.load, yaml.load, unserialize on untrusted input'],
['PQ-vulnerable crypto', 'RSA / ECDSA / ECDH usage with CNSA 2.0 deadlines'],
].map(([title, desc]) => (
<div class="rounded-lg border border-noir-800 p-4">
<div class="font-heading text-noir-100 text-sm mb-1">{title}</div>
<div class="text-noir-500 text-xs leading-relaxed">{desc}</div>
</div>
))}
</div>
</div>
<!-- Trust / safety -->
<div class="rounded-xl border border-noir-800 bg-noir-900/30 p-8 mb-24">
<h2 class="font-heading text-noir-50 text-2xl mb-6">How your code is handled</h2>
<ul class="space-y-3 text-sm text-noir-400 leading-relaxed">
<li class="flex gap-3"><span class="text-fox-light flex-shrink-0">→</span><span><strong class="text-noir-200">Webhooks are verified.</strong> Every GitHub delivery is HMAC-SHA256 checked before any code is fetched. Forged events get a 401.</span></li>
<li class="flex gap-3"><span class="text-fox-light flex-shrink-0">→</span><span><strong class="text-noir-200">Code runs in a sandbox.</strong> The PR head ref is shallow-cloned to a temp dir, scanned, then deleted. Hard caps: 60s timeout, 1 GB repo.</span></li>
<li class="flex gap-3"><span class="text-fox-light flex-shrink-0">→</span><span><strong class="text-noir-200">Nothing leaves your tenancy.</strong> It's the same Rust binary as <code class="text-fox-light bg-noir-950 px-1 rounded">npx foxguard</code>. There are no model calls and no uploads. Findings stay in the comment.</span></li>
<li class="flex gap-3"><span class="text-fox-light flex-shrink-0">→</span><span><strong class="text-noir-200">Dedicated infra.</strong> Single-tenant namespace on a Hetzner Xeon box in HEL1 (EU). Operated by 0sec Labs.</span></li>
<li class="flex gap-3"><span class="text-fox-light flex-shrink-0">→</span><span><strong class="text-noir-200">Open source.</strong> Receiver, scanner, and Dockerfile all live in <a href="https://github.com/0sec-labs/foxguard" class="text-fox-light hover:underline">0sec-labs/foxguard</a>. Audit every line.</span></li>
</ul>
</div>
<!-- Self-host -->
<div id="self-host" class="mb-24 scroll-mt-20">
<h2 class="font-heading text-noir-50 text-2xl sm:text-3xl mb-4">Self-host the receiver</h2>
<p class="text-noir-500 text-sm leading-relaxed mb-6 max-w-2xl">
Run your own instance for air-gapped CI or regulated workloads. The receiver ships as a Docker image, and the Dockerfile is in the repo. Register your own GitHub App, then point its webhook at your instance.
</p>
<div class="rounded-xl border border-noir-800 overflow-hidden">
<div class="bg-noir-900/50 border-b border-noir-800 px-5 py-2 text-xs text-noir-500 font-mono">terminal</div>
<pre class="px-5 py-4 text-sm font-mono text-noir-200 overflow-x-auto"><code>{`docker run --rm -p 8080:8080 \\
-e FOXGUARD_WEBHOOK_SECRET=$(openssl rand -hex 32) \\
${SELF_HOST_IMAGE}:latest`}</code></pre>
</div>
<p class="text-noir-600 text-xs mt-3">
See <a href="https://github.com/0sec-labs/foxguard/blob/main/Dockerfile.github-app" class="text-noir-400 hover:text-noir-100">Dockerfile.github-app</a> for the build details and <a href="https://github.com/0sec-labs/foxguard/issues/246" class="text-noir-400 hover:text-noir-100">issue #246</a> for the broader Phase 1/2 plan.
</p>
</div>
<!-- CTA -->
<div class="text-center">
<h2 class="font-heading text-noir-50 text-2xl sm:text-3xl mb-4">Try it locally first</h2>
<p class="text-noir-500 text-sm mb-6">It needs no App, install, or signup. See what it flags in your codebase.</p>
<div class="inline-flex rounded-xl border border-noir-800 overflow-hidden">
<div class="px-5 py-3 font-mono text-sm text-fox-light">npx foxguard .</div>
</div>
<div class="mt-6">
<a href="/" class="text-sm text-noir-500 hover:text-noir-100 transition-colors no-underline">← Back to foxguard.dev</a>
</div>
</div>
</div>
</section>
<Footer />
</Base>