foxguard 0.3.2

Security scanner as fast as a linter. 118 built-in rules, 10 languages, sub-second scans.
Documentation
---
import { frameworkGroups, features, compatFeatures } from '../../data/features';

const langColors: Record<string, string> = {
  'Express / Node': '#F7DF1E',
  'Flask / Django': '#3776AB',
  'Gin / net/http': '#00ADD8',
  'Rails / Ruby': '#CC342D',
  'Spring / Java': '#ED8B00',
  'PHP / Laravel': '#777BB4',
  'Rust': '#DEA584',
};
---

<section class="py-20" id="coverage">
  <div class="section-divider mb-20"></div>
  <div class="max-w-5xl mx-auto px-6">

    <!-- Framework cards -->
    <div class="text-center mb-12">
      <h2 class="font-heading text-2xl sm:text-3xl text-noir-50 mb-3">What it catches</h2>
      <p class="text-noir-500 max-w-xl mx-auto text-sm leading-relaxed">
        Framework-aware rules across 10 languages. Express sessions, Django CSRF, Rails mass assignment, Spring XXE, iOS keychain, .NET LDAP, and more.
      </p>
    </div>

    <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 mb-16">
      {frameworkGroups.map((group) => (
        <div class="bg-noir-900 border border-noir-800 rounded-xl p-4 transition-colors hover:border-noir-700">
          <div class="flex items-center gap-2 mb-3">
            <span class="w-2.5 h-2.5 rounded-full shrink-0" style={`background: ${langColors[group.title] || '#78716C'}`}></span>
            <h3 class="text-noir-100 font-semibold text-sm">{group.title}</h3>
            <span class="font-mono text-[9px] text-fox ml-auto">{group.ruleCount}</span>
          </div>
          <p class="text-[11px] text-noir-500 leading-relaxed mb-3">{group.desc}</p>
          <div class="flex flex-wrap gap-1">
            {group.badges.map((badge) => (
              <span class="rounded bg-noir-950 px-1.5 py-0.5 font-mono text-[9px] text-noir-600">{badge}</span>
            ))}
          </div>
        </div>
      ))}
    </div>

    <!-- Features grid -->
    <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mb-16">
      {features.map((f) => (
        <div class="bg-noir-900 border border-noir-800 rounded-xl p-5 transition-colors hover:border-noir-700">
          <div class="font-mono text-[10px] font-bold text-fox mb-2.5 tracking-wider uppercase">{f.icon}</div>
          <h3 class="text-noir-100 font-semibold text-sm mb-1.5">{f.title}</h3>
          <p class="text-xs text-noir-500 leading-relaxed">{f.desc}</p>
        </div>
      ))}
    </div>

    <!-- Semgrep compat -->
    <div class="bg-noir-900 border border-noir-800 rounded-xl p-6">
      <div class="flex flex-col sm:flex-row sm:items-start gap-6">
        <div class="sm:flex-1">
          <div class="font-mono text-[10px] uppercase tracking-wider text-noir-600 mb-2">Semgrep compatibility</div>
          <h3 class="text-noir-100 font-semibold text-sm mb-2">Bring your existing rules</h3>
          <p class="text-xs text-noir-500 leading-relaxed">
            Load Semgrep-compatible YAML on top of built-ins with <code class="text-noir-400">--rules</code>. Supports the structural matching subset most teams actually use.
          </p>
        </div>
        <div class="flex flex-wrap gap-1.5 sm:max-w-[280px]">
          {compatFeatures.map((f) => (
            <span class:list={[
              'rounded-md px-2 py-0.5 font-mono text-[10px]',
              f.supported ? 'bg-fox/10 text-fox' : 'bg-noir-950 text-noir-600'
            ]}>
              {f.label}
            </span>
          ))}
        </div>
      </div>
    </div>
  </div>
</section>