:root { --bg: #0D0C0B; --surface: #171614; --surface2: #1F1D1B; --border: #2A2723; --text: #EDE9E3; --text-2: #B5AFA8; --muted: #7A736C; --accent: #CBFF2E; --accent-2: #E87C3E; --success: #7DFA9B; --warning: #FFBF47; --danger: #FF6B57; --info: #63C7FF; --nav-h: 60px; --max-w: 1160px; --r-sm: 4px; --r-md: 8px; --r-lg: 12px; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { scroll-behavior: smooth; } body { background: var(--bg); color: var(--text); font-family: 'Instrument Sans', 'PingFang SC', 'Noto Sans SC', 'Microsoft YaHei', -apple-system, sans-serif; font-size: 16px; line-height: 1.65; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } a { color: var(--info); text-decoration: none; } a:hover { text-decoration: underline; } /* ── Nav ─────────────────────────────────────────────────────── */ nav { position: sticky; top: 0; z-index: 100; height: var(--nav-h); background: rgba(13, 12, 11, 0.88); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border-bottom: 1px solid var(--border); display: flex; align-items: center; padding: 0 24px; } .nav-inner { max-width: var(--max-w); width: 100%; margin: 0 auto; display: flex; align-items: center; gap: 32px; } .nav-logo { font-family: 'Fraunces', Georgia, serif; font-weight: 900; font-size: 18px; color: var(--text); letter-spacing: -0.03em; display: flex; align-items: center; gap: 8px; text-decoration: none; } .nav-logo:hover { text-decoration: none; color: var(--text); } .nav-logo .logo-mark { width: 26px; height: 26px; background: var(--accent); border-radius: var(--r-sm); display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 900; color: var(--bg); } .nav-links { display: flex; gap: 24px; font-size: 14px; font-weight: 500; } .nav-links a { color: var(--muted); transition: color 0.15s; text-decoration: none; } .nav-links a:hover { color: var(--text); text-decoration: none; } .nav-right { margin-left: auto; display: flex; align-items: center; gap: 10px; } @media (max-width: 900px) { .nav-links { display: none; } } @media (max-width: 600px) { .nav-right .btn-ghost { display: none; } } .btn-ghost { padding: 6px 14px; border-radius: var(--r-sm); border: 1px solid var(--border); font-size: 13px; font-weight: 500; color: var(--text-2); background: transparent; cursor: pointer; transition: border-color 0.15s, background 0.15s, color 0.15s; display: inline-flex; align-items: center; gap: 6px; text-decoration: none; font-family: inherit; } .btn-ghost:hover { border-color: var(--muted); background: var(--surface); color: var(--text); text-decoration: none; } /* ── Language switcher ──────────────────────────────────────── */ .lang-switch { display: flex; align-items: center; border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; font-size: 12px; font-family: 'IBM Plex Sans Condensed', sans-serif; font-weight: 700; letter-spacing: 0.05em; } .lang-switch a { padding: 5px 11px; color: var(--muted); background: transparent; transition: color 0.15s, background 0.15s; text-decoration: none; border-right: 1px solid var(--border); } .lang-switch a:last-child { border-right: none; } .lang-switch a:hover { color: var(--text); background: var(--surface); text-decoration: none; } .lang-switch a.active { color: var(--text); background: var(--surface2); } /* ── Buttons ─────────────────────────────────────────────────── */ .btn-primary { padding: 11px 22px; border-radius: var(--r-sm); background: var(--accent); color: var(--bg); font-weight: 700; font-size: 14px; font-family: 'IBM Plex Sans Condensed', sans-serif; letter-spacing: 0.04em; text-transform: uppercase; transition: opacity 0.15s, transform 0.1s; display: inline-flex; align-items: center; gap: 6px; text-decoration: none; border: none; cursor: pointer; } .btn-primary:hover { opacity: 0.9; transform: translateY(-1px); text-decoration: none; color: var(--bg); } .btn-secondary { padding: 11px 22px; border-radius: var(--r-sm); border: 1px solid var(--border); color: var(--text-2); font-size: 14px; font-weight: 500; transition: border-color 0.15s, background 0.15s, color 0.15s; display: inline-flex; align-items: center; gap: 6px; text-decoration: none; } .btn-secondary:hover { border-color: var(--muted); background: var(--surface); color: var(--text); text-decoration: none; } /* ── Hero ────────────────────────────────────────────────────── */ .hero-wrapper { position: relative; overflow: hidden; border-bottom: 1px solid var(--border); } .hero-wrapper::before { content: ''; position: absolute; top: 0; bottom: 0; left: 52%; width: 1px; background: linear-gradient(to bottom, transparent 0%, #2A2723 15%, #2A2723 85%, transparent 100%); transform: rotate(1.5deg); pointer-events: none; } .hero { max-width: var(--max-w); margin: 0 auto; padding: 96px 24px 88px; display: grid; grid-template-columns: 55% 1fr; gap: 64px; align-items: center; } @media (max-width: 800px) { .hero { grid-template-columns: 1fr; gap: 48px; padding: 64px 20px 56px; } .hero-wrapper::before { display: none; } } .hero-badge { display: inline-flex; align-items: center; gap: 8px; padding: 5px 12px 5px 8px; border-radius: 999px; border: 1px solid rgba(203, 255, 46, 0.25); background: rgba(203, 255, 46, 0.06); font-family: 'IBM Plex Sans Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent); margin-bottom: 24px; } .hero-badge .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: pulse 2.5s infinite; flex-shrink: 0; } @keyframes pulse { 0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(203,255,46,0.4); } 50% { opacity: 0.7; box-shadow: 0 0 0 4px rgba(203,255,46,0); } } .hero h1 { font-family: 'Fraunces', 'PingFang SC', 'Noto Sans SC', 'Microsoft YaHei', Georgia, serif; font-size: clamp(44px, 6vw, 80px); font-weight: 900; line-height: 1.1; letter-spacing: -0.03em; margin-bottom: 24px; color: var(--text); } .hero h1 .accent { color: var(--accent); } .hero-sub { font-size: 17px; color: var(--text-2); max-width: 500px; margin-bottom: 36px; line-height: 1.7; } .hero-ctas { display: flex; gap: 12px; flex-wrap: wrap; } /* ── Terminal ────────────────────────────────────────────────── */ .terminal { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; font-family: 'JetBrains Mono', 'Berkeley Mono', 'SFMono-Regular', Consolas, monospace; font-size: 12.5px; box-shadow: 0 24px 64px rgba(0,0,0,0.5), 0 0 0 1px rgba(203,255,46,0.04); } .terminal-bar { display: flex; align-items: center; gap: 7px; padding: 10px 14px; border-bottom: 1px solid var(--border); background: var(--surface2); } .terminal-dot { width: 10px; height: 10px; border-radius: 50%; } .terminal-dot.red { background: #ff5f56; } .terminal-dot.amber { background: #ffbd2e; } .terminal-dot.green { background: #27c93f; } .terminal-bar .term-title { margin-left: auto; margin-right: auto; color: var(--muted); font-size: 11px; font-family: 'IBM Plex Sans Condensed', sans-serif; letter-spacing: 0.06em; } .terminal-body { padding: 18px 20px; line-height: 2.0; } .t-prompt { color: var(--accent); } .t-cmd { color: var(--text); } .t-out { color: var(--muted); } .t-ok { color: var(--success); } .t-warn { color: var(--warning); } .t-dim { color: #3A3630; } .t-path { color: var(--info); } /* ── Stats bar ───────────────────────────────────────────────── */ .stats-bar { background: var(--surface); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); } .stats-inner { max-width: var(--max-w); margin: 0 auto; padding: 28px 24px; display: flex; gap: 0; align-items: stretch; } .stat { flex: 1; padding: 0 32px; border-right: 1px solid var(--border); } .stat:first-child { padding-left: 0; } .stat:last-child { border-right: none; } .stat-n { font-family: 'Fraunces', Georgia, serif; font-size: 40px; font-weight: 900; color: var(--text); letter-spacing: -0.03em; line-height: 1; margin-bottom: 6px; font-variant-numeric: tabular-nums; } .stat-n .unit { font-family: 'IBM Plex Sans Condensed', sans-serif; font-size: 13px; font-weight: 700; color: var(--muted); letter-spacing: 0.06em; text-transform: uppercase; margin-left: 4px; } .stat-label { font-family: 'IBM Plex Sans Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); } @media (max-width: 700px) { .stats-inner { flex-wrap: wrap; gap: 24px; } .stat { flex: 1 1 40%; border-right: none; padding: 0; } } /* ── Section shared ──────────────────────────────────────────── */ section { padding: 88px 24px; } .section-inner { max-width: var(--max-w); margin: 0 auto; } .section-label { font-family: 'IBM Plex Sans Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); margin-bottom: 16px; display: flex; align-items: center; gap: 12px; } .section-label::before { content: ''; display: block; width: 16px; height: 1px; background: var(--accent); flex-shrink: 0; } .section-title { font-family: 'Fraunces', 'PingFang SC', 'Noto Sans SC', Georgia, serif; font-size: clamp(28px, 4vw, 48px); font-weight: 900; letter-spacing: -0.03em; line-height: 1.15; margin-bottom: 16px; color: var(--text); } .section-sub { color: var(--text-2); max-width: 580px; font-size: 16px; line-height: 1.7; } /* ── Tools / Features ────────────────────────────────────────── */ .tools-section { background: var(--surface); border-top: 1px solid var(--border); } .tools-grid { margin-top: 52px; display: grid; grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; } .tool-card { background: var(--surface); padding: 32px; transition: background 0.15s; } .tool-card:hover { background: var(--surface2); } .tool-icon { font-size: 24px; margin-bottom: 14px; } .tool-name { font-weight: 700; font-size: 15px; margin-bottom: 6px; color: var(--text); letter-spacing: -0.01em; } .tool-powered { font-family: 'IBM Plex Sans Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; color: var(--muted); margin-bottom: 12px; text-transform: uppercase; } .tool-powered a { color: var(--info); } .tool-desc { font-size: 13.5px; color: var(--text-2); line-height: 1.65; } /* ── How it works ────────────────────────────────────────────── */ .steps { margin-top: 56px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; position: relative; } @media (max-width: 700px) { .steps { grid-template-columns: 1fr; gap: 32px; } } .steps::before { content: ''; position: absolute; top: 20px; left: calc(16.67% + 20px); right: calc(16.67% + 20px); height: 1px; background: var(--border); } @media (max-width: 700px) { .steps::before { display: none; } } .step-num { width: 40px; height: 40px; border-radius: 50%; background: var(--surface); border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; font-family: 'Fraunces', Georgia, serif; font-weight: 900; font-size: 16px; color: var(--accent); margin-bottom: 20px; position: relative; z-index: 1; flex-shrink: 0; } .step-title { font-weight: 700; font-size: 15px; margin-bottom: 10px; color: var(--text); } .step-desc { font-size: 13.5px; color: var(--text-2); line-height: 1.65; } .step-code { margin-top: 16px; padding: 10px 16px; background: var(--bg); border: 1px solid var(--border); border-radius: var(--r-sm); font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--accent); letter-spacing: 0.01em; } /* ── Template matrix ─────────────────────────────────────────── */ .matrix-section { background: var(--surface); border-top: 1px solid var(--border); } .matrix { margin-top: 52px; overflow-x: auto; } .matrix table { width: 100%; border-collapse: separate; border-spacing: 0; border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; font-size: 13.5px; font-variant-numeric: tabular-nums; } .matrix th { background: var(--surface2); padding: 13px 20px; text-align: left; font-family: 'IBM Plex Sans Condensed', sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); border-bottom: 1px solid var(--border); } .matrix td { padding: 16px 20px; border-bottom: 1px solid var(--border); vertical-align: middle; color: var(--text-2); } .matrix tr:last-child td { border-bottom: none; } .matrix tr:hover td { background: rgba(255,255,255,0.015); } .check { color: var(--success); font-size: 15px; } .stack-name { font-weight: 700; font-size: 14px; color: var(--text); } .stack-tools { font-family: 'IBM Plex Sans Condensed', sans-serif; font-size: 11px; letter-spacing: 0.04em; color: var(--muted); margin-top: 3px; } .platform-badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px; border-radius: var(--r-sm); border: 1px solid var(--border); font-family: 'IBM Plex Sans Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; color: var(--muted); text-transform: uppercase; } /* ── Plugin teaser ───────────────────────────────────────────── */ .plugin-section { border-top: 1px solid var(--border); } .plugin-inner { max-width: var(--max-w); margin: 0 auto; padding: 88px 24px; display: grid; grid-template-columns: 1fr 1fr; gap: 72px; align-items: start; } @media (max-width: 700px) { .plugin-inner { grid-template-columns: 1fr; gap: 48px; } } .plugin-code { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; font-family: 'JetBrains Mono', monospace; font-size: 12px; } .plugin-code-header { padding: 9px 16px; background: var(--surface2); border-bottom: 1px solid var(--border); font-family: 'IBM Plex Sans Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; color: var(--muted); text-transform: uppercase; } .plugin-code pre { padding: 20px; line-height: 1.9; overflow-x: auto; color: var(--text-2); } .kw { color: var(--accent-2); } .str { color: var(--success); } .cm { color: var(--muted); } .key { color: var(--info); } .val { color: var(--warning); } /* ── CTA strip ───────────────────────────────────────────────── */ .cta-strip { background: var(--surface); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); padding: 88px 24px; } .cta-inner { max-width: var(--max-w); margin: 0 auto; display: grid; grid-template-columns: 1fr auto; gap: 48px; align-items: center; } @media (max-width: 700px) { .cta-inner { grid-template-columns: 1fr; gap: 32px; } } .cta-strip h2 { font-family: 'Fraunces', 'PingFang SC', 'Noto Sans SC', Georgia, serif; font-size: clamp(28px, 4vw, 44px); font-weight: 900; letter-spacing: -0.03em; line-height: 1.15; margin-bottom: 12px; color: var(--text); } .cta-strip p { color: var(--text-2); font-size: 16px; line-height: 1.65; } .cta-btns { display: flex; gap: 12px; flex-direction: column; align-items: flex-start; } /* ── Footer ──────────────────────────────────────────────────── */ footer { border-top: 1px solid var(--border); padding: 40px 24px; } .footer-inner { max-width: var(--max-w); margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px; } .footer-logo { font-family: 'Fraunces', Georgia, serif; font-weight: 900; font-size: 16px; letter-spacing: -0.03em; color: var(--text); display: flex; align-items: center; gap: 8px; } .footer-logo .logo-mark { width: 22px; height: 22px; background: var(--accent); border-radius: var(--r-sm); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 900; color: var(--bg); } .footer-links { display: flex; gap: 20px; font-size: 13px; } .footer-links a { color: var(--muted); transition: color 0.15s; } .footer-links a:hover { color: var(--text); text-decoration: none; } .footer-right { font-family: 'IBM Plex Sans Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; color: var(--muted); text-transform: uppercase; }
开源 · Apache 2.0

一个工程师的标准,覆盖每个团队的流水线

让你的 DevSecOps 规范自动执行于所有仓库——秘密扫描、SAST、SCA、容器安全、策略即代码门禁,支持 Python、Node.js 和 Go,兼容 GitHub Actions 与 GitLab CI。

bash — ~/project
$ pip install castops
正在安装 castops… 
 
$ cast init
🔍 检测到:Python · GitHub Actions
 
写入 .github/workflows/devsecops.yml
 
✓ 秘密检测     Gitleaks
✓ SAST 静态分析  Semgrep
✓ SCA pip-audit
✓ 容器安全     Trivy
✓ 代码质量     Ruff
✓ 安全门禁     conftest + OPA
 
🛡  流水线已就绪。推送代码即可激活。
6
安全检测层
3
支持语言
2
CI 平台
1条命令
完成安装
0
外部账号依赖

完整的安全技术栈。开箱即用。

每套 CAST 模板并行运行 5 个安全 Job,最后由策略即代码门禁决定 PR 能否合并。

🔑
秘密检测
Gitleaks 驱动
扫描完整 git 历史中泄露的凭据、API 密钥和令牌——不仅限于最新提交。
🔍
SAST 静态分析
Semgrep 驱动
使用 1,000+ 开源规则检测源码中的安全漏洞和反模式,并将 SARIF 上传至 GitHub Security。
📦
SCA 依赖审计
pip-audit · npm audit · govulncheck
检测依赖项中的已知 CVE,自动选择适合当前技术栈的工具。
🐳
容器安全
Trivy 驱动
扫描 Docker 镜像的操作系统和依赖库 CVE。若无 Dockerfile 则自动跳过。
✏️
代码质量
Ruff · ESLint · staticcheck
强制执行代码风格与质量标准。默认为信息模式,不阻断合并。
🚦
安全门禁
conftest + OPA Rego 驱动
根据版本化策略评估所有 SARIF 发现。发现严重漏洞时阻断合并。策略文件存放于你的仓库。

从零到生产级流水线,五分钟以内。

1
安装 CLI
一条 pip install 命令。支持 Python 3.9+。无需任何外部账号或 API 令牌。
pip install castops
2
执行 cast init
CAST 自动检测你的技术栈和 CI 平台,将工作流文件写入正确位置。
cast init
3
推送代码
每次推送和 Pull Request 都将触发完整安全流水线。发现高危漏洞时阻断不安全的合并。
git push

每种技术栈。两个平台。

所有模板均包含完整的 6 层安全技术栈,并在每次 CAST 发版时进行测试。

技术栈 SCA 工具 质量工具 GitHub Actions GitLab CI
🐍 Python
pyproject.toml · requirements.txt · setup.py
pip-audit Ruff
⬡ Node.js
package.json
npm audit ESLint
🐹 Go
go.mod
govulncheck staticcheck

接入你自己的工具。

CAST 的门禁使用 SARIF——通用安全发现格式。任何输出 SARIF 的工具都能自动接入流水线。无需 fork,无需重写。

用 Snyk 替换 pip-audit。在 Semgrep 旁边加上 Bandit。接入你们内部的合规扫描器。门禁会统一评估所有结果。

插件指南 →
custom-snyk.yml — 与 CAST 工作流并列放置
jobs:
  snyk:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: snyk test --sarif-file-output=snyk.sarif
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
      - uses: actions/upload-artifact@v4
        with:
          # CAST 门禁自动收集所有 cast-sarif-* artifact
          name: cast-sarif-snyk
          path: snyk.sarif

从今天开始,交付安全的代码。

一条命令。无需外部账号。零配置。